- Joined
- Jul 10, 2007
- Messages
- 6,306
Hi all. Before submitting this system, I'd like to polish it off, get some feedback on what could be improved, should be added, removed, and etc : ).
This code should give you an idea as to what it does
I create a little diamon with attack.left, attack.right, attack.back, attack.front. I make it so that when I chat, my little unit attacks. maxTargets of -1 makes infinite targets and myattack is the attack code that runs.
Here are some of the main methods so a better understanding can be achieved
So this is the code (some extra stuff in it so that people can see what's going on if they decide to run the above)
You also need
http://www.hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
http://www.hiveworkshop.com/forums/jass-functions-413/snippet-pointintriangle-180407/
to run it : )
So yea.. I'm hoping this will change the way ORPGs, RPGs, arenas, and etc are created ; P. Manual attacking with targeting or super custom attacks on auto attack ; D.
The next script to do this sort of thing, if anyone is interested, is CustomMotionAttack, which adds motion to the above (like swing a sword along an arc). This will make attacks much more realistic and will damage units more the more exposed they are to an attack ; ).
After that I'll work on CustomDefend and CustomMotionDefend.
CustomAttack can be used for spells, projectiles, melee attacks, or anything else you want.
This code should give you an idea as to what it does
JASS:
library tester initializer init uses CustomAttack
globals
CustomAttack attack
endglobals
private function myattack takes nothing returns boolean
if (CustomAttack.triggerAttack.isHit(false)) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetUnitName(CustomAttack.triggerAttack.attacker) + " is attacking " + GetUnitName(CustomAttack.triggerAttack.attackedUnit))
endif
return false
endfunction
private function init takes nothing returns nothing
local unit u = CreateUnit(Player(0), 'hpea', 2000, 1900, 90)
local unit u2 = CreateUnit(Player(0), 'hpea', 2000, 2000, 0)
local unit u3 = CreateUnit(Player(0), 'hpea', 2000, 2200, 0)
set attack = CustomAttack.create()
set attack.attacker = u
set attack.maxTargets = -1
set attack.code = Condition(function myattack)
set attack.left = 150
set attack.right = 150
set attack.back = 60
set attack.front = 300
set attack.trigger = CreateTrigger()
call TriggerRegisterPlayerEvent(attack.trigger, Player(0), EVENT_PLAYER_CHAT)
endfunction
endlibrary
I create a little diamon with attack.left, attack.right, attack.back, attack.front. I make it so that when I chat, my little unit attacks. maxTargets of -1 makes infinite targets and myattack is the attack code that runs.
Here are some of the main methods so a better understanding can be achieved
public method attackPosition takes real x, real y, real facing returns nothing
public method isInAttackRange takes real x, real y, real facing, unit u returns boolean
public method isHit takes boolean targetSelf returns boolean
So this is the code (some extra stuff in it so that people can see what's going on if they decide to run the above)
JASS:
library CustomAttack uses UnitIndexer, IsPointInTriangle
struct CustomAttack extends array
public unit attacker
private static integer instanceCount = 0
private static integer array recycle
private static integer recycleCount = 0
private static hashtable attackTable
private static code attackTargetsClear
private static code attackTargetsAdd
private static code attackTargetsRemove
private static boolexpr attackEvent
private static boolexpr attackTarget
private trigger attackTrigger
private boolexpr attackFilter
private boolexpr attackCode
private boolexpr attackTargetCode
private trigger attackCodeTrigger
private group targetGroup
public integer maxTargets
private integer target_Count
private static group array enumGroup
private static thistype array triggeringAttack
private static integer attackCount = 0
//attack max ranges
public real right
public real left
public real front
public real back
//attack min ranges
public real minRight
public real minLeft
public real minFront
public real minBack
//attack rectangle
private real ax
private real ay
private real bx
private real by
private real cx
private real cy
private real dx
private real dy
//non attack rectangle
private real nax
private real nay
private real nbx
private real nby
private real ncx
private real ncy
private real ndx
private real ndy
private static real worldMaxX
private static real worldMaxY
private static real worldMinX
private static real worldMinY
public method operator targets takes nothing returns group
return targetGroup
endmethod
public method isTarget takes unit u returns boolean
return IsUnitInGroup(u, targetGroup)
endmethod
public method addTarget takes unit u returns boolean
if ((maxTargets < 0 or target_Count < maxTargets) and not IsUnitInGroup(u, targetGroup)) then
call GroupAddUnit(targetGroup, u)
set target_Count = target_Count + 1
return true
endif
return false
endmethod
public method removeTarget takes unit u returns boolean
if (IsUnitInGroup(u, targetGroup)) then
call GroupRemoveUnit(targetGroup, u)
set target_Count = target_Count - 1
return true
endif
return false
endmethod
public method operator targetCount takes nothing returns integer
return target_Count
endmethod
public method operator trigger takes nothing returns trigger
return attackCodeTrigger
endmethod
public method operator trigger= takes trigger t returns nothing
if (attackCodeTrigger != null) then
call TriggerClearConditions(attackCodeTrigger)
call RemoveSavedInteger(attackTable, GetHandleId(attackCodeTrigger), 0)
call DestroyTrigger(attackCodeTrigger)
endif
set attackCodeTrigger = t
if (t != null) then
call SaveInteger(attackTable, GetHandleId(t), 0, this)
call TriggerAddCondition(t, attackEvent)
endif
endmethod
public method operator filter takes nothing returns boolexpr
return attackFilter
endmethod
public method operator filter= takes boolexpr c returns nothing
set attackFilter = c
endmethod
public method operator code takes nothing returns boolexpr
return attackCode
endmethod
public method operator code= takes boolexpr c returns nothing
set attackCode = c
if (attackTargetCode != null) then
call DestroyBoolExpr(attackTargetCode)
endif
if (c != null) then
set attackTargetCode = And(attackTarget, c)
else
set attackTargetCode = null
endif
endmethod
public static method operator attackedUnit takes nothing returns unit
return GetFilterUnit()
endmethod
public static method operator triggerAttack takes nothing returns thistype
return triggeringAttack[attackCount]
endmethod
public method operator backX takes nothing returns real
return ax
endmethod
public method operator backY takes nothing returns real
return ay
endmethod
public method operator frontX takes nothing returns real
return bx
endmethod
public method operator frontY takes nothing returns real
return by
endmethod
public method operator leftX takes nothing returns real
return cx
endmethod
public method operator leftY takes nothing returns real
return cy
endmethod
public method operator rightX takes nothing returns real
return dx
endmethod
public method operator rightY takes nothing returns real
return dy
endmethod
public method operator nBackX takes nothing returns real
return nax
endmethod
public method operator nBackY takes nothing returns real
return nay
endmethod
public method operator nFrontX takes nothing returns real
return nbx
endmethod
public method operator nFrontY takes nothing returns real
return nby
endmethod
public method operator nLeftX takes nothing returns real
return ncx
endmethod
public method operator nLeftY takes nothing returns real
return ncy
endmethod
public method operator nRightX takes nothing returns real
return ndx
endmethod
public method operator nRightY takes nothing returns real
return ndy
endmethod
//width of attack
public method operator width= takes real val returns nothing
set right = val/2
set left = val/2
endmethod
public method operator width takes nothing returns real
return left+right
endmethod
//minimum attack radius
public method operator minRadius= takes real val returns nothing
set minRight = val
set minLeft = val
set minFront = val
set minBack = val
endmethod
public method operator minRadius takes nothing returns real
return (minRight+minLeft+minFront+minBack)/4
endmethod
public method isHit takes boolean targetSelf returns boolean
local real tx
local real ty
if (targetSelf or GetFilterUnit() != attacker) then
set tx = GetUnitX(GetFilterUnit())
set ty = GetUnitY(GetFilterUnit())
return not (IsPointInTriangle(nax, nay, ncx, ncy, ndx, ndy, tx, ty) or IsPointInTriangle(nbx, nby, ncx, ncy, ndx, ndy, tx, ty)) and (IsPointInTriangle(ax, ay, cx, cy, dx, dy, tx, ty) or IsPointInTriangle(bx, by, cx, cy, dx, dy, tx, ty))
endif
return false
endmethod
public method isInAttackRange takes real x, real y, real facing, unit u returns boolean
local real yProj
local real xProj
local real tx
local real ty
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set facing = facing*bj_DEGTORAD
set xProj = Cos(facing)
set yProj = Sin(facing)
set rs[0] = x - back*xProj
set rs[1] = x + front*xProj
set rs[4] = y - back*yProj
set rs[5] = y + front*yProj
set rs[10] = x - minBack*xProj
set rs[11] = x + minFront*xProj
set rs[14] = y - minBack*yProj
set rs[15] = y + minFront*yProj
set facing = facing + bj_PI/2
set xProj = Cos(facing)
set yProj = Sin(facing)
set rs[2] = x + left*xProj
set rs[3] = x - right*xProj
set rs[6] = y + left*yProj
set rs[7] = y - right*yProj
set rs[12] = x + minLeft*xProj
set rs[13] = x - minRight*xProj
set rs[16] = y + minLeft*yProj
set rs[17] = y - minRight*yProj
return not (IsPointInTriangle(rs[10], rs[14], rs[12], rs[16], rs[13], rs[17], tx, ty) or IsPointInTriangle(rs[11], rs[15], rs[12], rs[16], rs[13], rs[17], tx, ty)) and (IsPointInTriangle(rs[0], rs[4], rs[2], rs[6], rs[3], rs[7], tx, ty) or IsPointInTriangle(rs[1], rs[5], rs[2], rs[6], rs[3], rs[7], tx, ty))
endmethod
private static real array rs
public method attackPosition takes real x, real y, real facing returns nothing
local rect attackRegion
local real yProj
local real xProj
local real minX = worldMaxX
local real minY = worldMaxY
local real maxX = worldMinX
local real maxY = worldMinY
local integer i
if (attackTargetCode != null) then
set facing = facing*bj_DEGTORAD
set xProj = Cos(facing)
set yProj = Sin(facing)
set rs[0] = x - back*xProj
set rs[1] = x + front*xProj
set rs[4] = y - back*yProj
set rs[5] = y + front*yProj
set rs[10] = x - minBack*xProj
set rs[11] = x + minFront*xProj
set rs[14] = y - minBack*yProj
set rs[15] = y + minFront*yProj
set facing = facing + bj_PI/2
set xProj = Cos(facing)
set yProj = Sin(facing)
set rs[2] = x + left*xProj
set rs[3] = x - right*xProj
set rs[6] = y + left*yProj
set rs[7] = y - right*yProj
set rs[12] = x + minLeft*xProj
set rs[13] = x - minRight*xProj
set rs[16] = y + minLeft*yProj
set rs[17] = y - minRight*yProj
set ax = rs[0]
set bx = rs[1]
set cx = rs[2]
set dx = rs[3]
set ay = rs[4]
set by = rs[5]
set cy = rs[6]
set dy = rs[7]
set nax = rs[10]
set nbx = rs[11]
set ncx = rs[12]
set ndx = rs[13]
set nay = rs[14]
set nby = rs[15]
set ncy = rs[16]
set ndy = rs[17]
set i = 7
loop
if (rs[i] < minX) then
set minX = rs[i]
elseif (rs[i] > maxX) then
set maxX = rs[i]
endif
if (rs[i] < minY) then
set minY = rs[i]
elseif (rs[i] > maxY) then
set maxY = rs[i]
endif
set i = i - 1
exitwhen i < 0
endloop
call SetUnitScale(CreateUnit(Player(0), 'hfoo', ax, ay, 0), 1, 1, 1)
call SetUnitScale(CreateUnit(Player(0), 'hfoo', cx, cy, 0), 1, 1, 1)
call SetUnitScale(CreateUnit(Player(0), 'hfoo', dx, dy, 0), 1, 1, 1)
call SetUnitScale(CreateUnit(Player(1), 'hfoo', bx, by, 0), 1, 1, 1)
call SetUnitScale(CreateUnit(Player(1), 'hfoo', cx, cy, 0), 1, 1, 1)
call SetUnitScale(CreateUnit(Player(1), 'hfoo', dx, dy, 0), 1, 1, 1)
set attackRegion = Rect(minX, minY, maxX, maxY)
set attackCount = attackCount + 1
set triggeringAttack[attackCount] = this
if (enumGroup[attackCount] == null) then
set enumGroup[attackCount] = CreateGroup()
endif
call GroupEnumUnitsInRect(enumGroup[attackCount], attackRegion, attackFilter)
if (target_Count != 0) then
call ForGroup(targetGroup, attackTargetsClear)
endif
if (maxTargets < 0 or target_Count < maxTargets) then
call ForGroup(enumGroup[attackCount], attackTargetsAdd)
elseif (target_Count > maxTargets) then
call ForGroup(targetGroup, attackTargetsRemove)
endif
call GroupEnumUnitsInRect(enumGroup[attackCount], attackRegion, attackTargetCode)
call GroupClear(enumGroup[attackCount])
set attackCount = attackCount - 1
call RemoveRect(attackRegion)
set attackRegion = null
endif
endmethod
private static method attackEventFunc takes nothing returns boolean
local thistype this = LoadInteger(attackTable, GetHandleId(GetTriggeringTrigger()), 0)
if (IsUnitIndexed(attacker)) then
call attackPosition(GetUnitX(attacker), GetUnitY(attacker), GetUnitFacing(attacker))
endif
return false
endmethod
private static method attackTargetsClearFunc takes nothing returns nothing
if (IsUnitIndexed(GetEnumUnit())) then
if (not IsUnitInGroup(GetEnumUnit(), enumGroup[attackCount])) then
call GroupRemoveUnit(triggeringAttack[attackCount].targetGroup, GetEnumUnit())
set triggeringAttack[attackCount].target_Count = triggeringAttack[attackCount].target_Count - 1
endif
else
call GroupRemoveUnit(triggeringAttack[attackCount].targetGroup, GetEnumUnit())
set triggeringAttack[attackCount].target_Count = triggeringAttack[attackCount].target_Count - 1
endif
endmethod
private static method attackTargetsAddFunc takes nothing returns nothing
if (not IsUnitInGroup(GetEnumUnit(), triggeringAttack[attackCount].targetGroup) and (triggeringAttack[attackCount].maxTargets < 0 or triggeringAttack[attackCount].target_Count < triggeringAttack[attackCount].maxTargets)) then
call GroupAddUnit(triggeringAttack[attackCount].targetGroup, GetEnumUnit())
set triggeringAttack[attackCount].target_Count = triggeringAttack[attackCount].target_Count + 1
endif
endmethod
private static method attackTargetFunc takes nothing returns boolean
return IsUnitInGroup(GetFilterUnit(), triggeringAttack[attackCount].targetGroup)
endmethod
private static method attackTargetsRemoveFunc takes nothing returns nothing
if (triggeringAttack[attackCount].maxTargets >= 0 and triggeringAttack[attackCount].target_Count > triggeringAttack[attackCount].maxTargets) then
call GroupRemoveUnit(triggeringAttack[attackCount].targetGroup, GetEnumUnit())
set triggeringAttack[attackCount].target_Count = triggeringAttack[attackCount].target_Count - 1
endif
endmethod
public static method create takes nothing returns thistype
local thistype this
if (recycleCount != 0) then
set recycleCount = recycleCount - 1
set this = recycle[recycleCount]
else
set instanceCount = instanceCount + 1
set this = instanceCount
endif
if (targetGroup == null) then
set targetGroup = CreateGroup()
endif
return this
endmethod
public method destroy takes nothing returns nothing
if (attackTrigger != null) then
call TriggerClearConditions(attackCodeTrigger)
call RemoveSavedInteger(attackTable, GetHandleId(attackCodeTrigger), 0)
call DestroyTrigger(attackCodeTrigger)
set attackTrigger = null
endif
if (attackTargetCode != null) then
set attackCode = null
call DestroyBoolExpr(attackTargetCode)
set attackTargetCode = null
endif
call GroupClear(targetGroup)
set left = 0
set right = 0
set front = 0
set back = 0
set minLeft = 0
set minRight = 0
set minFront = 0
set minBack = 0
set target_Count = 0
set attackFilter = null
set recycle[recycleCount] = this
set recycleCount = recycleCount + 1
endmethod
private static method onInit takes nothing returns nothing
local rect r = GetWorldBounds()
set worldMaxX = GetRectMaxX(r)
set worldMaxY = GetRectMaxY(r)
set worldMinX = GetRectMinX(r)
set worldMinY = GetRectMinY(r)
call RemoveRect(r)
set r = null
set attackTargetsClear = function thistype.attackTargetsClearFunc
set attackTargetsAdd = function thistype.attackTargetsAddFunc
set attackTargetsRemove = function thistype.attackTargetsRemoveFunc
set attackEvent = Condition(function thistype.attackEventFunc)
set attackTarget = Condition(function thistype.attackTargetFunc)
set attackTable = InitHashtable()
endmethod
endstruct
endlibrary
You also need
http://www.hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
http://www.hiveworkshop.com/forums/jass-functions-413/snippet-pointintriangle-180407/
to run it : )
So yea.. I'm hoping this will change the way ORPGs, RPGs, arenas, and etc are created ; P. Manual attacking with targeting or super custom attacks on auto attack ; D.
The next script to do this sort of thing, if anyone is interested, is CustomMotionAttack, which adds motion to the above (like swing a sword along an arc). This will make attacks much more realistic and will damage units more the more exposed they are to an attack ; ).
After that I'll work on CustomDefend and CustomMotionDefend.
CustomAttack can be used for spells, projectiles, melee attacks, or anything else you want.
Last edited: