• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

How to Pick Units in JASS

Status
Not open for further replies.
Is there a better way of picking (Enumerating?) units in range in JASS/vJASS other than this atrocity of a GUI-to-JASS conversion?

JASS:
function Trig_Untitled_Trigger_002_Func001002003001 takes nothing returns boolean
    return ( IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true )
endfunction

function Trig_Untitled_Trigger_002_Func001002003002001 takes nothing returns boolean
    return ( IsUnitAliveBJ(GetFilterUnit()) == true )
endfunction

function Trig_Untitled_Trigger_002_Func001002003002002 takes nothing returns boolean
    return ( IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(udg_DamageEventSource)) == true )
endfunction

function Trig_Untitled_Trigger_002_Func001002003002 takes nothing returns boolean
    return GetBooleanAnd( Trig_Untitled_Trigger_002_Func001002003002001(), Trig_Untitled_Trigger_002_Func001002003002002() )
endfunction

function Trig_Untitled_Trigger_002_Func001002003 takes nothing returns boolean
    return GetBooleanAnd( Trig_Untitled_Trigger_002_Func001002003001(), Trig_Untitled_Trigger_002_Func001002003002() )
endfunction

function Trig_Untitled_Trigger_002_Func002A takes nothing returns nothing
    call UnitDamageTargetBJ( GetTriggerUnit(), GetTriggerUnit(), 500, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
endfunction

function Trig_Untitled_Trigger_002_Actions takes nothing returns nothing
    set udg_TempGroup = GetUnitsInRangeOfLocMatching(512, udg_TempPoint, Condition(function Trig_Untitled_Trigger_002_Func001002003))
    call ForGroupBJ( udg_TempGroup, function Trig_Untitled_Trigger_002_Func002A )
    call DestroyGroup(mygroup)
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_002 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_002 = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Untitled_Trigger_002, function Trig_Untitled_Trigger_002_Actions )
endfunction
I saw something somewhere about enumerating units in range, but do I need to clean it afterwards like you would a unit group?
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
I've not tested it for syntax errors, but should be roughly like this:
JASS:
native UnitAlive takes unit u returns boolean
library Enum
    globals
        private player p
    endglobals
    function func takes nothing returns boolean
        local unit u = GetFilterUnit()
        local boolean b = true
        if IsUnitAlly(u,p) then
            set b = false
        elseif not(UnitAlive(u) then
            set b = false
        endif
        return b
    endfunction
    function Enumerate takes nothing returns nothing
        local group g = CreateGroup()
        local real range = 300
        local unit caster = GetTriggerUnit()//or whatever
        local unit u
        set p = GetOwningPlayer(caster)
        call GroupEnumUnitsInRange(g,x,y,Filter(func))
        
        //Faster than ForGroup
        loop
            set u = FirstOfGroup(g)
            exitwhen u == null
            //Do stuff with unit u
            call GroupRemoveUnit(g,u)
        endloop
        call DestroyGroup(g)
        set g = null
        set caster = null
    endfunction
endlibrary
 
I plan to use this in conjunction with BPower's Missile, which in turn is used to replace ranged attacks altogether (for an altered melee). So the most performance I can squeeze out of, well, anything, the better.

Wietlol said:
Filter should be null at all times.
According to Cokemonkey11's tutorial, call GroupEnumUnitsInRange(grp,0.,0.,256.,null) leaks, so I'd like to avoid that since I intend for this enumeration to happen ALL THE TIME.

So, should I use what Xonok wrote is the null leak really not that important?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
You can simply declare an onCollide method in a struct having the MissileStruct module implemented, it will do what you try to achieve.

Be aware that onCollide runs every 0.03125. So if you only want to check for
close targets when the missile finishes it's course you should do the
group enumeration in onFinish.
onCollide is useful, if you also wish to check collision during a Missile fly time.
 
I'm not sure how an onCollide would work in this situation. Wouldn't an onCollide affect only 1 unit? I'm trying to enumerate upon onFinish, but for some reason, calling the group damage function is overriding the return true. If I comment the call out, the missiles destroy as normal. Otherwise, they slide off on their merry way into map bounds lol

EDIT:

Nevermind, figured it out.
I had
private group grp
instead of
private group grp = CreateGroup()

>_>

EDIT2: Btw, do I need to destroy the group when the loop ends?


JASS:
    private function GroupDamage takes unit s, real d, real x, real y, player o returns nothing
        local unit FoG=null
        call GroupEnumUnitsInRange(grp,x,y,100.,null)
        loop
            set FoG=FirstOfGroup(grp)
            exitwhen FoG==null
            if IsUnitType(FoG, UNIT_TYPE_GROUND) and UnitAlive(FoG) and IsUnitEnemy(FoG, o) then
                set udg_DamageEventType = 3
                call UnitDamageTarget( s, FoG, d, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null )
            endif
            call GroupRemoveUnit(grp,FoG)
        endloop
    endfunction
The missile's not being destroyed when the above function is called so I had to comment it out.
JASS:
    private static method onFinish takes Missile this returns boolean
    
        //call GroupDamage(this.source,this.damage,this.x,this.y,this.owner)
        
        if GetUnitTypeId(this.source) == 'hmpr' then
            set bj_lastCreatedEffect = AddSpecialEffect(splashfx, this.x, this.y)
            call DestroyEffect (bj_lastCreatedEffect)
        endif
            
        return true
    endmethod

PS: the same thing happens if the loop is inside the onFinish method and not a separate function.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
onCollide can also deal with all units in appropriate range of the Missile,
however as I mentioned, if you wish to deal damage only on onFinsh
then do the enumeration by yourself.

No you don't have to destroy the group.

You solved the issue that the thread doesn't run till the end?
Like you said the group handle grp has to exist, that should have been the problem.
 
If you just want to apply an action to units in range, you might as well do it inside the filter. This is, naturally, even faster than the FoG method:

JASS:
globals
    private group ENUM_GROUP = CreateGroup()
endglobals

private function MyFilter takes nothing returns boolean
    local unit u = GetFilterUnit()

    if IsUnitType(u, UNIT_TYPE_GROUND) and UnitAlive(u) and IsUnitEnemy(u, o) then
        //Do stuff with the unit here...
    endif

    set u = null
    return false
endfunction

private function DoStuff takes real x, real y, real radius returns nothing
    call GroupEnumUnitsInRange(ENUM_GROUP, x, y, radius, Filter(function MyFilter))
endfunction

Stuff like damage source and amount can be saved in global variables.
 
Status
Not open for further replies.
Top