• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] Please help me, otimize my spell

Status
Not open for further replies.

nGy

nGy

Level 11
Joined
Apr 15, 2011
Messages
123
Hello,

I've finished my first (v)Jass spell and as I'm not that experienced, I'd be glad, if someone could help me, otzimize the code. Thanks (in advance, hopefully) :)

Creates a missile, that is attracted by enemy units around it

JASS:
scope SpellHeatSeekingMissile initializer init

globals
    private constant real InitialOffset = 100 //the missile's distance from the caster, when summoned
    private constant real InitialFlyHeight = 80 //the missile's flying height, when summoned
    private constant real InitialSpeed = 30 //the missile's speed, when summoned; in units per 0.03 seconds
    private constant real DetectionRadius = 1000 //the radius in which the missile gets attracted by units
    private constant real ExpirationRadius = 50 //the radius in which the missile detects uni
    private constant real ExplosionRadius = 150 //the radius in which the damage is dealt, when the missile explodes
    private constant real Attraction = 200 //this value determines how much the missile is attracted by other units
    private constant real Damage = 250 //damage dealt by the missile
    private constant real Size = ExplosionRadius / 150 // the visual explosion size
    private constant integer OffsetX = 0
    private constant integer OffsetY = 1
    private constant integer Effect = 2
    private constant hashtable hash = InitHashtable()
    //--------------------------------------------------\\
    private real minX = 0
    private real maxX = 0
    private real minY = 0
    private real maxY = 0
    private group Missiles = CreateGroup()
    private unit tempUnit
endglobals

private function EnumFilter takes nothing returns boolean
    local unit u = GetFilterUnit()
    //--------------------------------------------------\\
    return GetUnitTypeId(u) != 'n001' and GetUnitState(u, UNIT_STATE_LIFE) > 0 and IsUnitEnemy(u, GetOwningPlayer(tempUnit))
endfunction

private function Cond takes nothing returns boolean
    return GetSpellAbilityId() == 'A000'
endfunction

private function Death takes unit u1, real x, real y returns nothing
    local effect e = LoadEffectHandle(hash, GetHandleId(u1), Effect)
    local unit u2
    local group g = CreateGroup()
    //--------------------------------------------------\\
    call KillUnit(u1)
    call DestroyEffect(e)
    call SetUnitScale(u1, Size, Size, Size)
    call GroupRemoveUnit(Missiles, u1)
    call GroupEnumUnitsInRange(g, x, y, ExplosionRadius, function EnumFilter)
    loop
        set u2 = FirstOfGroup(g)
        exitwhen u2 == null
        call GroupRemoveUnit(g, u2)
        call UnitDamageTarget(u1, u2, Damage, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_FIRE, null) 
    endloop
    call DestroyGroup(g)
    set e = null
    set g = null
    set u1 = null
    set u2 = null
endfunction

private function Movement takes nothing returns nothing
    local unit u1
    local unit u2
    local group g1 = CreateGroup()
    local group g2
    local real x1
    local real x2
    local real x3
    local real y1
    local real y2
    local real y3
    local real ox
    local real oy
    local real difx
    local real dify
    local integer id
    local boolean explode
    //--------------------------------------------------\\
    call GroupAddGroup(Missiles, g1)
    loop
        set u1 = FirstOfGroup(g1)
        exitwhen u1 == null
        call GroupRemoveUnit(g1, u1)
        set id = GetHandleId(u1)
        set x1 = GetUnitX(u1)
        set y1 = GetUnitY(u1)
        set ox = LoadReal(hash, id, OffsetX)
        set oy = LoadReal(hash, id, OffsetY)
        set tempUnit = u1
        set g2 = CreateGroup()
        call GroupEnumUnitsInRange(g2, x1, y1, DetectionRadius, function EnumFilter)
        loop
            set u2 = FirstOfGroup(g2)
            exitwhen u2 == null
            call GroupRemoveUnit(g2, u2)
            set x2 = GetUnitX(u2)
            set y2 = GetUnitY(u2)
            set difx = x2 - x1
            set dify = y2 - y1
            if (difx > 5 or difx < -5) and (dify > 5 or dify < -5) then
                set ox = ox + (((x2 - x1) * Attraction) / (Pow(x2 - x1, 2) + Pow(y2 - y1, 2)))
                set oy = oy + (((y2 - y1) * Attraction) / (Pow(x2 - x1, 2) + Pow(y2 - y1, 2)))
            endif
            set u2 = null
        endloop
        call DestroyGroup(g2)
        set g2 = null
        set x3 = x1 + ox
        set y3 = y1 + oy
        set explode = false
        if minX <= x3 and x3 <= maxX and minY <= y3 and y3 <= maxY then
            set g2 = CreateGroup()
            call GroupEnumUnitsInRange(g2, x1, y1, ExpirationRadius, function EnumFilter)
                loop
                    set u2 = FirstOfGroup(g2)
                    exitwhen u2 == null   
                    set explode = true
                    exitwhen explode == true
                endloop
            //I might have to do a rectangle detection here as well. When the missile gets too fast it just skips units :S
            call DestroyGroup(g2)
            set g2 = null
            if explode == false then
                call SetUnitFacing(u1, Atan2(oy, ox) * bj_RADTODEG)
                call SetUnitX(u1, x3)
                call SetUnitY(u1, y3)
                call SaveReal(hash, id, OffsetX, ox)
                call SaveReal(hash, id, OffsetY, oy)
            else
                call Death(u1, x1, y1)
            endif
        else
            call Death(u1, x1, y1)
        endif
        set u1 = null
    endloop
    call DestroyGroup(g1)
    set g1 = null
endfunction

private function Cast takes nothing returns nothing
    local unit u1 = GetTriggerUnit()
    local real r = Atan2(GetSpellTargetY() - GetUnitY(u1), GetSpellTargetX() - GetUnitX(u1))
    local unit u2 = CreateUnit(GetTriggerPlayer(), 'n001', GetUnitX(u1) + (InitialOffset * Cos(r)), GetUnitY(u1) + (InitialOffset * Sin(r)), r * bj_RADTODEG)
    local integer id = GetHandleId(u2)
    //--------------------------------------------------\\
    call SetUnitFlyHeight(u2, InitialFlyHeight, 0)
    call SetUnitScale(u2, 2, 2, 2)
    call GroupAddUnit(Missiles, u2)
    call SaveReal(hash, id, OffsetX, InitialSpeed * Cos(r))
    call SaveReal(hash, id, OffsetY, InitialSpeed * Sin(r))
    call SaveEffectHandle(hash, id, Effect, AddSpecialEffectTarget("Rocket.mdx", u2, "head"))
    set u1 = null
    set u2 = null
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local timer tmr = CreateTimer()
    //--------------------------------------------------\\
    set minX = GetRectMinX(bj_mapInitialPlayableArea)   //I had some problems here.
    set maxX = GetRectMaxX(bj_mapInitialPlayableArea)   //It seemed to me like
    set minY = GetRectMinY(bj_mapInitialPlayableArea)   //bj_mapInitialPlayableArea
    set maxY = GetRectMaxY(bj_mapInitialPlayableArea)   //is null at map initialization :S
    //--------------------------------------------------\\
    call TimerStart(tmr, 0.03, true, function Movement)
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, function Cond)
    call TriggerAddAction(t, function Cast)
    set t = null
    set tmr = null
endfunction

endscope
 
Last edited:
Level 5
Joined
Sep 28, 2010
Messages
75
GetUnitState(u, UNIT_STATE_LIFE) > 0
You can change it to GetWidgetLife(u) > 0.405
It will be more accurate that way.

local group g = CreateGroup()
Instead of declaring local groups over and over again you could use a global group that can be reused.
JASS:
globals
    private group temp_group = CreateGroup()
endglobals

private function ExampleUsage takes nothing returns nothing
    //Add any units in range of Location(0, 0) to group
    call GroupEnumUnitsInRange(temp_group, 0, 0, 1000, null)
    //(Your actions here)
    //============
    //Clear the group after use
    call GroupClear(temp_group)
endfunction

This way you won't have to create and destroy groups, along with nulling them.

Aside from that I didn't spot any other inefficient code. I am a little discomforted about this line, though. call GroupAddGroup(Missiles, g1)

EDIT:
JASS:
private function EnumFilter takes nothing returns boolean
    local unit u = GetFilterUnit()
    //--------------------------------------------------\\
    return GetUnitTypeId(u) != 'n001' and GetUnitState(u, UNIT_STATE_LIFE) > 0 and IsUnitEnemy(u, GetOwningPlayer(tempUnit))
endfunction

You forgot to null "u"
 
Last edited:
  • Like
Reactions: nGy
Level 6
Joined
May 13, 2009
Messages
260
EDIT:
JASS:
private function EnumFilter takes nothing returns boolean
    local unit u = GetFilterUnit()
    //--------------------------------------------------\\
    return GetUnitTypeId(u) != 'n001' and GetUnitState(u, UNIT_STATE_LIFE) > 0 and IsUnitEnemy(u, GetOwningPlayer(tempUnit))
endfunction
You forgot to null "u"

I noticed that as well, but is it more efficient to make a local boolean value and return it after the null set of u? Just wondering :)

JASS:
private function EnumFilter takes nothing returns boolean
    local unit u = GetFilterUnit()
    local boolean returnMe = GetUnitTypeId(u) != 'n001' and GetUnitState(u, UNIT_STATE_LIFE) > 0 and IsUnitEnemy(u, GetOwningPlayer(tempUnit))
    //--------------------------------------------------\\

   set u = null
   return returnMe
endfunction
 
  • Like
Reactions: nGy
Level 17
Joined
Apr 27, 2008
Messages
2,455
You don't need to clear the group here.
The GroupEnum... functions already clear the group and then eventually add units inside it.

Also, personnaly i use IsUnitType(<unit>,UNIT_TYPE_DEAD), because you can have a dead unit with more than 0.405 hp, with passive abilities or if you set the life of the unit by trigger actions.
 
  • Like
Reactions: nGy

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
set difx = x2 - x1
set dify = y2 - y1
if (difx > 5 or difx < -5) and (dify > 5 or dify < -5) then
set ox = ox + (((x2 - x1) * Attraction) / (Pow(x2 - x1, 2) + Pow(y2 - y1, 2)))
set oy = oy + (((y2 - y1) * Attraction) / (Pow(x2 - x1, 2) + Pow(y2 - y1, 2)))
endif

Probably some optimization possible here by caching intermittent results for reuse.
 
  • Like
Reactions: nGy

nGy

nGy

Level 11
Joined
Apr 15, 2011
Messages
123
phew, my english isn't that good but if you meant i could do

JASS:
set difx = x2 - x1
set dify = y2 - y1
//if-line deleted as it was not necessary
set ox = ox + (((difx) * Attraction) / (Pow(difx, 2) + Pow(dify, 2)))
set oy = oy + (((dify) * Attraction) / (Pow(difx, 2) + Pow(dify, 2)))

instead, then yeah. i guess, that was my original intention with setting those two dif values, i just forgot to use them.

Edit: No, you most likely didn't mean that. I don't really see, which results I can store and reuse though.
 
Last edited:
This way you won't have to create and destroy groups, along with nulling them.
bj_lastCreatedGroup is best as your code 'still create' the group via globals...

//============
to optimize the spell, my suggestions are;
- never create a local group, when you destroy it immediately, use the above mentioned
- use GetWidgetLife or UnitAlive instead of GetUnitState
- replace your local u with a global one to avoic leak, or better just use;
return GetUnitTypeId(GetFilterUnit()) != 'n001' and GetWidgetLife(GetFilterUnit()) > 0.405 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(tempUnit))
- merge the cast and cond into TriggerAddCondition
- the timer should be paused when no instance is running otherwise, Movement will run forever,
use a ForGroup to run the movement instead
 
  • Like
Reactions: nGy

nGy

nGy

Level 11
Joined
Apr 15, 2011
Messages
123
thanks for your suggestions, mckill. however i didn't get, how i could use bj_lastCreatedGroup, why I should use ForGroup and why I should merge cast and cond into TriggerAddCondition instead of TriggerAddAction (and why I should merge them anyway. Cuz if I do, it will always declare the bunch of locals and that's kinda unnecessary, imo). Would appreciate some explanation.

(here's the updated code:)
JASS:
scope SpellHeatSeekingMissile initializer init

globals
    private constant real InitialOffset = 100                   //the missile's distance from the caster, when summoned
    private constant real InitialFlyHeight = 80                 //the missile's flying height, when summoned
    private constant real InitialSpeed = 30                     //the missile's speed, when summoned; in units per 0.03 seconds
    private constant real Size = 2                              //the missile's size
    private constant real DetectionRadius = 1000                //the radius in which the missile gets attracted by units
    private constant real ExpirationRadius = 50                 //the radius in which the missile detects uni
    private constant real ExplosionRadius = 300                 //the radius in which the damage is dealt, when the missile explodes
    private constant real Attraction = 200                      //the degree of attraction by surrounding enemy units
    private constant real Damage = 250                          //the damage dealt by the missile
    private constant real ExplosionSize = ExplosionRadius / 150 //the visual explosion size
    private constant real LifeSpan = 3                          //the maximum time the missile will fly until it explodes (in seconds).
    private constant integer DummyName = 'n001'                 //the missile's unit type id
    private constant integer AbilityName = 'A000'               //the spell's ability id
    private constant string MissileModel = "Rocket.mdx"         //the missile model path
    private constant attacktype AttType = ATTACK_TYPE_MAGIC     //the explosion's attacktype
    private constant damagetype DmgType = DAMAGE_TYPE_FIRE      //the explosion's damagetype
    //===================end of setup===================\\
    private constant integer OffsetX = 0
    private constant integer OffsetY = 1
    private constant integer RestSpan = 2
    private constant integer Effect = 3
    private constant hashtable hash = InitHashtable()
    //--------------------------------------------------\\
    private real minX
    private real maxX
    private real minY
    private real maxY
    private group Missiles = CreateGroup()
    private group tempGrp1 = CreateGroup()
    private group tempGrp2 = CreateGroup()
    private unit tempUnit
    private timer tmr = CreateTimer()
endglobals

private function EnumFilter takes nothing returns boolean
    local unit u = GetFilterUnit()
    local boolean b = GetUnitTypeId(u) != DummyName and not IsUnitType(u, UNIT_TYPE_DEAD) and IsUnitEnemy(u, GetOwningPlayer(tempUnit))
    set u = null
    return b
endfunction

private function Cond takes nothing returns boolean
    return GetSpellAbilityId() == AbilityName
endfunction

private function Death takes unit u1, real x, real y returns nothing
    local integer id = GetHandleId(u1)
    local effect e = LoadEffectHandle(hash, id, Effect)
    local unit u2
    //--------------------------------------------------\\
    call KillUnit(u1)
    call DestroyEffect(e)
    call SetUnitScale(u1, ExplosionSize, ExplosionSize, ExplosionSize)
    call GroupRemoveUnit(Missiles, u1)
    if FirstOfGroup(Missiles) == null then
        call PauseTimer(tmr)
    endif
    call GroupEnumUnitsInRange(tempGrp2, x, y, ExplosionRadius, function EnumFilter)
    loop
        set u2 = FirstOfGroup(tempGrp2)
        exitwhen u2 == null
        call GroupRemoveUnit(tempGrp2, u2)
        call UnitDamageTarget(u1, u2, Damage, true, false, AttType, DmgType, null)
    endloop
    call RemoveSavedReal(hash, id, OffsetX)
    call RemoveSavedReal(hash, id, OffsetY)
    call RemoveSavedReal(hash, id, RestSpan)
    call RemoveSavedHandle(hash, id, Effect)
    set e = null
    set u1 = null
    set u2 = null
endfunction

private function Movement takes nothing returns nothing
    local unit u1
    local unit u2
    local real x1
    local real x2
    local real x3
    local real y1
    local real y2
    local real y3
    local real ox
    local real oy
    local real difx
    local real dify
    local real span
    local integer id
    //--------------------------------------------------\\
    call GroupClear(tempGrp1)
    call GroupAddGroup(Missiles, tempGrp1)
    loop
        set u1 = FirstOfGroup(tempGrp1)
        exitwhen u1 == null
        call GroupRemoveUnit(tempGrp1, u1)
        set id = GetHandleId(u1)
        set x1 = GetUnitX(u1)
        set y1 = GetUnitY(u1)
        set ox = LoadReal(hash, id, OffsetX)
        set oy = LoadReal(hash, id, OffsetY)
        set tempUnit = u1
        call GroupEnumUnitsInRange(tempGrp2, x1, y1, DetectionRadius, function EnumFilter)
        loop
            set u2 = FirstOfGroup(tempGrp2)
            exitwhen u2 == null
            call GroupRemoveUnit(tempGrp2, u2)
            set x2 = GetUnitX(u2)
            set y2 = GetUnitY(u2)
            set difx = x2 - x1
            set dify = y2 - y1
            set ox = ox + (((difx) * Attraction) / (Pow(difx, 2) + Pow(dify, 2)))
            set oy = oy + (((dify) * Attraction) / (Pow(difx, 2) + Pow(dify, 2)))
            set u2 = null
        endloop
        set x3 = x1 + ox
        set y3 = y1 + oy
        call GroupEnumUnitsInRange(tempGrp2, x1, y1, ExpirationRadius, function EnumFilter)
        if minX <= x3 and x3 <= maxX and minY <= y3 and y3 <= maxY and FirstOfGroup(tempGrp2) == null then
            call SetUnitFacing(u1, Atan2(oy, ox) * bj_RADTODEG)
            call SetUnitX(u1, x3)
            call SetUnitY(u1, y3)
            call SaveReal(hash, id, OffsetX, ox)
            call SaveReal(hash, id, OffsetY, oy)
            set span = LoadReal(hash, id, RestSpan) - 0.03
            if span <= 0 then
                call Death(u1, x1, y1)
            else
                call SaveReal(hash, id, RestSpan, span)
            endif
        else
            call Death(u1, x1, y1)
        endif
        set u1 = null
    endloop
endfunction

private function Cast takes nothing returns nothing
    local unit u1 = GetTriggerUnit()
    local real r = Atan2(GetSpellTargetY() - GetUnitY(u1), GetSpellTargetX() - GetUnitX(u1))
    local unit u2 = CreateUnit(GetTriggerPlayer(), DummyName, GetUnitX(u1) + (InitialOffset * Cos(r)), GetUnitY(u1) + (InitialOffset * Sin(r)), r * bj_RADTODEG)
    local integer id = GetHandleId(u2)
    //--------------------------------------------------\\
    if FirstOfGroup(Missiles) == null then
        call TimerStart(tmr, 0.03, true, function Movement)
    endif
    call SetUnitFlyHeight(u2, InitialFlyHeight, 0)
    call SetUnitScale(u2, Size, Size, Size)
    call GroupAddUnit(Missiles, u2)
    call SaveReal(hash, id, OffsetX, InitialSpeed * Cos(r))
    call SaveReal(hash, id, OffsetY, InitialSpeed * Sin(r))
    call SaveReal(hash, id, RestSpan, LifeSpan)
    call SaveEffectHandle(hash, id, Effect, AddSpecialEffectTarget(MissileModel, u2, "origin"))
    set u1 = null
    set u2 = null
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    //--------------------------------------------------\\
    set minX = GetRectMinX(bj_mapInitialPlayableArea)
    set maxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set minY = GetRectMinY(bj_mapInitialPlayableArea)
    set maxY = GetRectMaxY(bj_mapInitialPlayableArea)
    //--------------------------------------------------\\
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, function Cond)
    call TriggerAddAction(t, function Cast)
    set t = null
endfunction

endscope
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
JASS:
private function EnumFilter takes nothing returns boolean
    local unit u = GetFilterUnit()
    local boolean b = GetUnitTypeId(u) != DummyName and not IsUnitType(u, UNIT_TYPE_DEAD) and IsUnitEnemy(u, GetOwningPlayer(tempUnit))
    set u = null
    return b
endfunction

....

call GroupEnumUnitsInRange(tempGrp2, x, y, ExplosionRadius, function EnumFilter)
    loop
        set u2 = FirstOfGroup(tempGrp2)
        exitwhen u2 == null
        call GroupRemoveUnit(tempGrp2, u2)
        call UnitDamageTarget(u1, u2, Damage, true, false, AttType, DmgType, null)
    endloop

-->

JASS:
private function EnumFilter takes unit u returns boolean
    return GetUnitTypeId(u)!=DummyName and not IsUnitType(u,UNIT_TYPE_DEAD) and IsUnitEnemy(u,GetOwningPlayer(tempUnit))
endfunction

....

call GroupEnumUnitsInRange(bj_lastCreatedGroup,x,y,ExplosionRadius,null)
    loop
        set u2=FirstOfGroup(bj_lastCreatedGroup)
        exitwhen u2==null
        call GroupRemoveUnit(bj_lastCreatedGroup,u2)
        if EnumFilter(u2) then
            call UnitDamageTarget(u1,u2,Damage,true,false,AttType,DmgType,null)
        endif
    endloop
 
  • Like
Reactions: nGy

nGy

nGy

Level 11
Joined
Apr 15, 2011
Messages
123
Thanks, Luorax, I'll use bj_lastCreatedGroup.
But as I use the EnumFilter in the following case as well

JASS:
        call GroupEnumUnitsInRange(tempGrp2, x1, y1, ExpirationRadius, function EnumFilter)
        if minX <= x3 and x3 <= maxX and minY <= y3 and y3 <= maxY and FirstOfGroup(tempGrp2) == null then
            call SetUnitFacing(u1, Atan2(oy, ox) * bj_RADTODEG)
            call SetUnitX(u1, x3)
            call SetUnitY(u1, y3)
            call SaveReal(hash, id, OffsetX, ox)
            call SaveReal(hash, id, OffsetY, oy)
            set span = LoadReal(hash, id, RestSpan) - 0.03
            if span <= 0 then
                call Death(u1, x1, y1)
            else
                call SaveReal(hash, id, RestSpan, span)
            endif
        else
            call Death(u1, x1, y1)
        endif

(where I'd hate to put an ugly loop and additional ifs or something), I'll just leave the EnumFilter as it is - I also don't really think, changing that would make any noticeable difference, performance-wise. Thanks again for your suggestion, though and tell me, if I'm wrong.
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
I also don't really think, changing that would make any noticeable difference, performance-wise.

Oh, it would. Every time a unit is filtered a new thread is opened, and the samte thing happens when you enum the group. But on the other hand, if you pass "null" as filter and loop through it manually you'll stay in the current thread without opening new ones, which is a quite expensive procedure.

If you want to stick to the filter in that function, but are willing to make my changes in the other one, you can simply do this:

JASS:
private function GroupFilter takes unit u returns boolean
    return GetUnitTypeId(u)!=DummyName and not IsUnitType(u,UNIT_TYPE_DEAD) and IsUnitEnemy(u,GetOwningPlayer(tempUnit))
endfunction
private function EnumFilter takes nothing returns boolean
    return GroupFilter(GetFilterUnit())
endfunction
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
I've already explained that - it's way faster to just loop through them and to open 1 or 2 new threads for each unit (which may be quite high as it enums corpses, buildings, dummies, etc). You said you wanted your code to be optimized, didn't you?
 

nGy

nGy

Level 11
Joined
Apr 15, 2011
Messages
123
I think, we're talking at cross-purposes. Anyway, I'll take another look at it.

JASS:
call GroupEnumUnitsInRange(tempGrp2, x1, y1, ExpirationRadius, function EnumFilter)
if minX <= x3 and x3 <= maxX and minY <= y3 and y3 <= maxY and FirstOfGroup(tempGrp2) == null then
    call SetUnitFacing(u1, Atan2(oy, ox) * bj_RADTODEG)
    call SetUnitX(u1, x3)
    call SetUnitY(u1, y3)
    call SaveReal(hash, id, OffsetX, ox)
    call SaveReal(hash, id, OffsetY, oy)
    set span = LoadReal(hash, id, RestSpan) - 0.03
    if span <= 0 then
        call Death(u1, x1, y1)
    else
        call SaveReal(hash, id, RestSpan, span)
    endif
else
    call Death(u1, x1, y1)
endif
updated to:
JASS:
        set b = false
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, x1, y1, ExpirationRadius, null)
        loop
            set u2 = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen u2 == null
            set b = GroupFilter(u2)
            exitwhen b == true
            call GroupRemoveUnit(bj_lastCreatedGroup, u2)
        endloop
        set u2 = null        
        if minX <= x3 and x3 <= maxX and minY <= y3 and y3 <= maxY and b == false then
            call SetUnitFacing(u1, Atan2(oy, ox) * bj_RADTODEG)
            call SetUnitX(u1, x3)
            call SetUnitY(u1, y3)
            call SaveReal(hash, id, OffsetX, ox)
            call SaveReal(hash, id, OffsetY, oy)
            set span = LoadReal(hash, id, RestSpan) - 0.03
            if span <= 0 then
                call Death(u1, x1, y1)
            else
                call SaveReal(hash, id, RestSpan, span)
            endif
        else
            call Death(u1, x1, y1)
        endif
 
Status
Not open for further replies.
Top