• 🏆 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!

Handling Handles

Status
Not open for further replies.
Level 8
Joined
Nov 9, 2008
Messages
502
Hello Hive I'm back! Been busy with college work no map making until my recent project - Crazy Catas!

Problem is, I need a way move projectiles effectively.

I thought of having an overall engine trigger to handle projectile moving and planned to use the unit's custom value to store the vertical velocity but I remembered custom value is only for integers. It's possible I could do this if I used a very large integer and then divide it to minimise the loss of accuracy provided by decimals but I would prefer not to.

Before I left I was just discovering H2R and such functions and never really understood them. I know they are used to attach values to objects like timers. This would be perfect but I know that Blizzard changed those functions when they updated a while ago.

So could anyone explain how to use H2R to so that I may store the vertical velocity of a unit and pass it to the next iteration?

it would be something like this:

function CreateProj()
set z.velocity
create timer(function MoveProj)
endfunction

funtion MoveProj()
destroy timer
get z.velocity
if z.value <= 0 then
call ProjDeath(unit)
else
set new z.velocity
create time(function MoveProj())
endif
endfunction

K gotta go to work hopefully there will be a coherant answer when i get back :)

Thanks,
Sabre2th
 
Well, H2I/I2X functions are no longer supported aside from backwards support using a new method of Typecasting. (Well, H2I [GetHandleId()] array attachments are still used a lot, but the old handle var methods are deprecated for using Gamecache)

There are a couple of options for data attachments (or at least efficient ones):
  • Gamecache - Slowest, but still work. However, this shouldn't really be used for attachment aside from map-to-map attachment. They are pretty slow, require typecasting to work [since the API doesn't support other handle types], and they leak strings when used.
  • Hashtables - They are fast, but not the fastest. However, you can assign structs as integers into a hashtable under the ID of the timer. You also need a unique timer or some recycling system to use them for MUI. Example:
    JASS:
    //Basically allows you to transfer the members and use them across functions
    //It is slower than other methods, but it works and is faster than gamecache.
    //This is just an example, it is untested. You might need to save it as integer(this) instead
    struct Data
        real x
        real y
        static hashtable HT = InitHashtable()
        
        static method onExpire takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = LoadInteger(.HT,GetHandleId(t),0) //retrieves the struct data
            set .x = 17
            set .y = 13 
            call PauseTimer(t) 
            call DestroyTimer(t)
            call this.destroy()
            set t = null
        endmethod
        
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            local timer t = CreateTimer()
            set .x = 22
            set .y = 55
            call SaveInteger(.HT,GetHandleId(t),0,this) //saves the struct as an integer
            call TimerStart(t,15,false,function thistype.onExpire)
            set t = null 
            return this
        endmethod
    endstruct
  • TimerUtils - These basically allow you to recycle timers easily and are nice for usage. Essentially, there is a version for hashtables and a version for using the index as an array. Basically, arrays allow for an index of 0-8192 or something around that value before it crashes. Each handle created (aside from "special" ones like texttags, ubersplats, etc.) have a unique ID assigned that we can retrieve with GetHandleId(). Normally-allocated handles will start off with a value of 1048576 (or 0x100000 in hex) and then each id will progressively be assigned to the next index. (previousHandleID+1) When a handle is destroyed (assuming all pointers to it are nulled), it will basically recycle that index. TimerUtils uses this to assign arrays, by retrieving the handle ID of the timer and subtracting 0x100000, so it will essentially get some value from 0-8192. The only problem is that with this, you can sometimes get a higher value than you'd expect, so you might need to increase the "0x100000" (known as OFFSET in TimerUtils) to allow the timers to have a usable index. [else it will crash] This essentially limits the amount of timers made, but it uses a recycling method so that it is less likely to crash into this limit. Usually tweaking the offset will work even in big maps. With a handle-heavy map though, it can still screw you over even if you tweak the offsets. But, without further ado, I'll show you a basic usage of TimerUtils:
    JASS:
    //This is basically a struct using TimerUtils for attachment
    //It is merely an example, so some things might not compile/work
    //But this is generally how it is done.
    struct Data
        real x
        real y
        
        static method Lala takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t) //retrieves the struct data from the timer
            set .x = 5
            set .y = 10
            call ReleaseTimer(t)
            //releases the timer to be recycled
        endmethod
         
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            local timer t = NewTimer() //generates a new timer or reuses one
            set .x = 15
            set .y = 62
            call SetTimerData(t,this) //"attaches" a struct to a timer
            call TimerStart(t,15,false,function Lala)
            //I don't need to null "t" since TimerUtils recycles the timer.
            return this
        endmethod
    endstruct
    TimerUtils is a nice all-around-timer-system. Which basically means, it is faster than hashtables and works decently well with any period.
  • TriggerExecCount - This basically works by executing a trigger X times based on the struct integer (structs are really just unique integers). Then you just retrieve it via GetTriggerExecCount(), but it is slower compared to other systems apparently, so I won't really go into the usage of it.
  • KeyTimers 2 - This uses a complex array kind of system. It uses both timers and triggers for executing things. It is essentially Timer Ticker, but with a non-fixed period. This is another all-around-timer-system, with a pretty easy interface. It isn't as widely supported, but definitely works and there are a lot of arguments made about this timer system being superior to others. Here is an example of usage:
    JASS:
    //This is an example of KT2 usage.
    //It is pretty simple and modular.
    struct Data
        real x
        real y
        integer count
        
        static method Lala takes nothing returns boolean
            local thistype this = KT_GetData()
            //Retrieves the data, simple as that
            set .count = .count+1
            set .x = .x+2
            set .y = .y+2
            //do whatever you want with your members
            if .count > 5 then
                call this.destroy()
                //return true results in the timer to stop
                return true
            endif
            //return false results in the timer to continue
            return false
        endmethod
         
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set .count = 0
            set .x = 15
            set .y = 62
            call KT_Add( function thistype.Lala, this, 3 )
            //You add a function to the periodic to be executed every 3 seconds
            //Takes the function, then the struct to attach, and then the period
            return this
        endmethod
    endstruct
    KT2 is overall a nice system, but TU red can still win for high periods. Generally, there is never a noticeable difference, so it is mostly up to whichever interface you like better. KT2 is pretty ideal for low periods though.
  • Timer32 - This is the fastest, and lightest system. It is just a timer exec loop, but the difference to this is that it uses one timer and trigger for the entire system. The other difference is that it isn't a multi-instance timer system in the fact that it runs per instance, it runs once for ALL instances in general. It has a fixed looping, and executes all the periodic functions added to the queue. The only difference is that it has a fixed period of 0.03125. So it is very ideal for most spells, but not ideal for things like timed handles, creep respawning, spawning systems etc.

    Essentially, Timer32 adds all of the periodic functions to be executed as a condition. For each loop of the core timer, it will execute a trigger that contains all the conditions added. The other con besides the fixed period is that the periodic function must be named "periodic". It is an implemented module so it has to have someway to execute the function, so if they are all named alike it is fine to use. However, since it is a module it is inline friendly. Anyway, here is a simple usage of T32:
    JASS:
    //This is an example of T32.
    //It is extremely simple to use (at least in my opinion)
    //It is also very fast and light.
    struct Data
        real x
        real y
        integer count
        
        private method periodic takes nothing returns nothing
            set .x = .x + 7  //no retrieving, just use the struct members directly!!
            set .y = .y + 17 //note that it is a "method", not a static method
            set .count = .count + 1 //T32 executes this with the proper instance
            if .count > 6 then
                call this.destroy()
                call this.stopPeriodic() //this ends the timer
            endif
        endmethod
        implement T32x //It is a module so it needs the functions to be implemented into the struct
        
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set .x = 15
            set .y = 24 
            set .count = 0
            call this.startPeriodic() //this begins the periodic looping
            return this
        endmethod
    endstruct
    T32 essentially just uses a linked list and loops through the members of the list. startPeriodic acts as an "insert" to insert the instance into the looping queue and stopPeriodic acts as a "remove" to remove the instance from the queue. Now, this is generally slower for longer periods, so for those longer periods you should use either KT2 or TimerUtils. It is also synchronous, so you can also use KT2 or TimerUtils if you want. T32 is just very nice for spells. (32 executions per second) It is nice for looping and will definitely get the job done. T32 is also very inline friendly (since it is a module) so it is very easy to get the most efficiency out of it as you can. (not the biggest difference, only if you are a freak for efficiency and willing to inline start/stopPeriodic for whatever reason) However, inlining it can be useful when you want to allow for different struct method naming, or when you want to save yourself from making a separate struct for a timer execution.
  • Struct/stack looping - Some people prefer a timer per struct. I'm not going to go too in depth on this because I haven't used this method much myself. It basically involves a struct array, an integer for counting, integer for indexing, and the timer itself. Or sometimes I believe one of those types are excluded and it uses some other method.

Sorry if there are any mistakes, I didn't reread it much. But that should generally give you information about the latest methods for struct attachments.
 
Last edited:
Level 8
Joined
Nov 9, 2008
Messages
502
Probably the shortest and logest possible answers right there. Cookies all round.

That is something I forgot about - hashtables. Seeing as they are so rediculously easy to use and my map really only needs to handle about 20 projectiles maximum per iteration, I'm going to try this method first.

Really good information you both gave.
 
Level 8
Joined
Nov 9, 2008
Messages
502
Ok I've got a few problems trying to implement a hashtable.

JASS:
function Trig_CreateProj_Actions takes nothing returns nothing
   
    local location castTargetLoc = GetSpellTargetLoc()
    local integer cataIndex = GetPlayerId(GetOwningPlayer(GetTriggerUnit()))
    local real castSourceX = GetUnitX(GetTriggerUnit())
    local real castTargetX = GetLocationX(castTargetLoc)
    local real castSourceY = GetUnitY(GetTriggerUnit())
    local real castTargetY = GetLocationY(castTargetLoc)
    local real castAngle = bj_RADTODEG * Atan2(castTargetY - castSourceY, castTargetX - castSourceX)
    local integer playerId = GetPlayerId(GetOwningPlayer(GetTriggerUnit()))
    local region battlezone = CreateRegion()
    local unit projectile
    
    call RegionAddRect(battlezone, gg_rct_battlezone)
    if IsLocationInRegion(battlezone, castTargetLoc) then
        set battlezone = null
        
    //modify aiming time
        call DisplayTextToPlayer(Player(playerId), 0, 0, "Aiming . . .")
    
    //move units angle
        call SetUnitTimeScale(GetTriggerUnit(), (udg_playerAimSpeed[playerId]*0.2))
        call SetUnitFacing(udg_catasAll[cataIndex], castAngle)
    
    //0.75 threshhold
        call TriggerSleepAction(2.35-(udg_playerAimSpeed[playerId]*0.35))
        call DisplayTextToPlayer(Player(playerId), 0, 0, "Fire!!")
    
    //animate cata fire
        call SetUnitAnimationByIndex(udg_catasAll[cataIndex], 2)
        call TriggerSleepAction(0.5)
    
    //boulder
        if GetSpellAbilityId() == 'A000' then
        if playerId >= 5 then
            set projectile = CreateUnit(Player(playerId), 'h001', (castSourceX-75), castSourceY, 180)
        else
            set projectile = CreateUnit(Player(playerId), 'h001', (castSourceX-64), castSourceY, 0)
        endif
        
    //give to hashtable
        //save projectile unit
            call SaveUnitHandleBJ( projectile, GetHandleIdBJ(projectile), 0, udg_projHash )
        //save targetX
            call SaveRealBJ( castTargetX, GetHandleIdBJ(projectile), 1, udg_projHash)
        //save targetY
            call SaveRealBJ( castTargetY, GetHandleIdBJ(projectile), 2, udg_projHash)
        //save yVelocity
            // !!!<make airtime calculation>!!! call SaveRealBJ(Value, GetHandleIdBJ(projectile), 4, udg_projHash )
        
    //another ammo, insert elseif
        
        //set projectile = null
        endif
    else
        call IssueImmediateOrder(GetTriggerUnit(), "stop")
        call DisplayTextToPlayer(Player(playerId), 0, 0, "Out of bounds")
    endif
    
    call DisplayTextToPlayer(Player(0), 0, 0, R2S(LoadRealBJ(GetHandleIdBJ(projectile), 1, udg_projHash)))
    call DisplayTextToPlayer(Player(0), 0, 0, R2S(LoadRealBJ(GetHandleIdBJ(projectile), 2, udg_projHash)))
    
    call RemoveRegion(battlezone)
    call RemoveLocation(castTargetLoc)
endfunction

//===========================================================================
function InitTrig_CreateProj takes nothing returns nothing
    local integer i = 0
    set gg_trg_CreateProj = CreateTrigger(  )
    loop
        exitwhen i == 10
        call TriggerRegisterPlayerUnitEvent(gg_trg_CreateProj, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set i = i+1
    endloop
    call TriggerAddAction( gg_trg_CreateProj, function Trig_CreateProj_Actions )
endfunction

The DisplayTextToPlayer(Load hash entry) is for debugging but keeps returning 0.000 and I don't see why it is. Can someone see the problem.

And another thing, I have used this method for looping through a group and performing actions many times before but now it doesn't work for some odd reason...

JASS:
function Trig_MoveProj_Actions takes nothing returns nothing
    
    local unit projectile
    local real castTargetX
    local real castTargetY
    local real castSourceX
    local real castSourceY
    local real projSpeed
    local group projAll
    
    set projAll = CreateGroup()
    call GroupEnumUnitsOfType(projAll, "hgry", null)
    loop
        call DisplayTextToPlayer(Player(0), 0, 0, "test1")
        set projectile = FirstOfGroup(projAll)
        exitwhen projectile == null
        call DisplayTextToPlayer(Player(0), 0, 0, "test2") 
        e.t.c........
    endloop

test1 calls but test2 does not. Why?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Did you forget to use
JASS:
GroupRemoveUnit(projAll,projectile)

Within the loop?

Also, your first trigger needs a lot of work. I've patched up some things but it is still a mess.

JASS:
function Trig_CreateProj_Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local real x1 = GetUnitX(u)
    local real y1 = GetUnitY(u)
    local real x2 = GetSpellTargetX()
    local real y2 = GetSpellTargetX()
    local player p = GetTriggerPlayer()
    local integer playerId = GetPlayerId(p)
    local unit projectile
    
    if RectContainsCoords(gg_rct_battlezone,x2,y2) then
        
    //modify aiming time
        call DisplayTextToPlayer(p, 0, 0, "Aiming . . .")
    
    //move units angle
        call SetUnitTimeScale(u, (udg_playerAimSpeed[playerId]*0.2))
        call SetUnitFacing(udg_catasAll[playerId], bj_RADTODEG * Atan2(y2 - y1, x2 - x1))
    
    //0.75 threshhold
        call TriggerSleepAction(2.35-(udg_playerAimSpeed[playerId]*0.35))
        call DisplayTextToPlayer(p, 0, 0, "Fire!!")
    
    //animate cata fire
        call SetUnitAnimationByIndex(udg_catasAll[playerId], 2)
        call TriggerSleepAction(0.5)
    
    //boulder
        if GetSpellAbilityId() == 'A000' then //what the heck? this should be a condition.  The prior settings happens EVERY time someone casts ANY spell.
            if playerId >= 5 then
                set projectile = CreateUnit(p, 'h001', (x1-75), y1, 180)
            else
                set projectile = CreateUnit(p, 'h001', (x1-64), y1, 0)
            endif
        
    //give to hashtable
        //save projectile unit
            call SaveUnitHandle( udg_projHash, GetHandleId(projectile), 0, projectile)
        //save targetX
            call SaveReal( udg_projHash, GetHandleId(projectile), 1, x2)
        //save targetY
            call SaveReal( udg_projHash, GetHandleId(projectile), 2, y2)
        //save yVelocity
            // !!!<make airtime calculation>!!! call SaveRealBJ(Value, GetHandleIdBJ(projectile), 4, udg_projHash )
        
    //another ammo, insert elseif
        endif
    else
        call IssueImmediateOrder(u, "stop")
        call DisplayTextToPlayer(p, 0, 0, "Out of bounds")
    endif
    
    call DisplayTextToPlayer(Player(0), 0, 0, R2S(LoadReal(udg_projHash,GetHandleId(projectile), 1)))
    call DisplayTextToPlayer(Player(0), 0, 0, R2S(LoadReal(udg_projHash,GetHandleId(projectile), 2)))
    
    set projectile=null
endfunction
 
//===========================================================================
function InitTrig_CreateProj takes nothing returns nothing
    local integer i = 0
    local trigger t = CreateTrigger()
    call TriggerAddAction( t, function Trig_CreateProj_Actions )
    
    loop
        exitwhen i == 10 //so players 10-15 are getting ignored?  10 and 11 are human player slots fyi.
        call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set i = i+1
    endloop
    
endfunction
 
Level 8
Joined
Nov 9, 2008
Messages
502
Lol ok yeh I didn't notice that bad placing of the spell type condition. Will fix thanks.

Yes it's for players 1-10 (0-9).

Other than that, moving the spell condition won't change the fac tthat the hashtable calls aren't working.

EDIT: I know it needs work, it's only half done. I'm not going to work more on it till the fundamental problem of the hashtable is fixed. Without that I will have to use another method and rework the whole design.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
For the test:

JASS:
function Trig_MoveProj_Actions takes nothing returns nothing
    
    local unit projectile
    local real castTargetX
    local real castTargetY
    local real castSourceX
    local real castSourceY
    local real projSpeed
    local group projAll
    
    set projAll = CreateGroup()
    call GroupEnumUnitsOfType(projAll,GetObjectName('h001'), null)
    loop
        call DisplayTextToPlayer(Player(0), 0, 0, "test1")
        set projectile = FirstOfGroup(projAll)
        exitwhen projectile == null
        call GroupRemoveUnit(projAll,projectile)
        call DisplayTextToPlayer(Player(0), 0, 0, "test2") 
        e.t.c........
    endloop
endfunction
 
Level 8
Joined
Nov 9, 2008
Messages
502
That doesn't work either.

Why is it that when I attempt something half decent, Jass just won't cooperate?
 
Level 8
Joined
Nov 9, 2008
Messages
502
No I didn't because GetObjectName(integer) takes units integer ('h001') and returns string ("hgry").

You copied his code exactly?

It seems the code is just completely disregarding the unit I create for some reason.
I counted the units in the group and displayed as text and it is a resounding 0 every iteration.
 
It does a FirstOfGroup loop, which is why the count returns 0 after the loop. You would have to do this:
JASS:
function Trig_MoveProj_Actions takes nothing returns nothing
    
    local unit projectile
    local real castTargetX
    local real castTargetY
    local real castSourceX
    local real castSourceY
    local real projSpeed
    local group projAll
    local integer count = 0
    
    set projAll = CreateGroup()
    call GroupEnumUnitsOfType(projAll,GetObjectName('h001'), null)
    loop
        call DisplayTextToPlayer(Player(0), 0, 0, "test1")
        set projectile = FirstOfGroup(projAll)
        exitwhen projectile == null
        set count = count+1
        call GroupRemoveUnit(projAll,projectile)
        call DisplayTextToPlayer(Player(0), 0, 0, "test2") 
        e.t.c........
    endloop
endfunction
 
Level 8
Joined
Nov 9, 2008
Messages
502
EDIT!!!!!!!!!!!!!!!!
No one told me units with locust cannot be grabbed into a group ^^
I removed locust and bingo! it works.
So how to make a single unit unselectable without giving it locust?

Still, the hash calls are fail pls help :cry:
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Berbanog's custom projectiles library is at near maximum-efficiency and you can't get script that efficient without an extremely large understanding of jass. I recommend you learn how to use his system for this, because if you're stuck on a problem this simple already, you have a long and painful road ahead and I recommend you invest your time into something that people haven't done before you (I don't know, be creative).
 
Level 8
Joined
Nov 9, 2008
Messages
502
Lol, you have no idea what I'm making and how I'm using this projectile system so how can you make such a judgement? Just because I'm writing a projectile system doesn't mean that I'm making an unoriginal map. The physics of projectile systems are always going to be similar in their execution because there's only so many ways you can do it.

And also, I don't particularly want to spend any more time than I have to learning a system that has stupid restrictions like not being able to touch units with locust even using code. To be fair, this was an error of misunderstanding w3 restrictions, not failing to understand the logic of code.

But thanks for the advice, it looks like I might have to use Berb's lib anyway.
Oh no wait, that doesn't solve my hashtable problem.

It's the timerUtils I need.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Hashtable --

1) did you initialize it?
2) did you use the ones I gave you or your crappy GUI-converted ones?
3) did you do both of those things in the same trial?

Congrats on getting the group enumeration to work. You edited your post after I made mine.

learning a system that has stupid restrictions like not being able to touch units with locust even using code
Have you even tested his code? His projectiles can collide with each other, last I checked. You just want to disagree with me, and to fan the flame at this point I've also been looking for ways to disagree with you, becuase you've been such a dick.
 
Level 8
Joined
Nov 9, 2008
Messages
502
Honestly I did not know that about Locust. Thanks for the link to the GroupEnumLocusts() but just before I read it I found a nice thread about giving locust units chaos which will make them plyable by code yet unselectable.
chaos ftw

I have initialised the table on mapInit and yes, I'm using the crappy GUI converted functions because I havent updated JassNewGenPack with the new functions so I'm kind of in the dark.

You say you gave me some hashtable fuctions but where? All I see is in PurgeandFire111's post on the first page. Sorry if I've missed it.

Sorry again but I'm not going to even test his code. I'm sure it's the perfect projectile system but due to that I don't need such features like missiles colliding with each other it would only bloat my map's size.

Sure, I do like disagreeing with you because I am a dick and you are a little pompous.

But your still here helping which is a testamony to your good will.
Thankyou.


EDIT: AH! I DID miss your hash calls and guess what! It works perfectly! Hmm, probably I should apologise if I've offended you. You're the best dude. No hard feelings? Feel free to say 'I told you so'.
And I'd be honoured if you would review my map when it's done.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
toofless said:
Honestly I did not know that about Locust. Thanks for the link to the GroupEnumLocusts() but just before I read it I found a nice thread about giving locust units chaos which will make them plyable by code yet unselectable.

First of all, if you're trying to enumerate locusts from an enormous array of player-owned dummies it's going to bog down your projectiles a lot. Also, if you're adding Chaos to the locust units then while they may not be selectable, they'll be able to be attacked. I'm not sure if this allows them to be enumerated or not. Also, units that have Chaos applied will still be able to be selected by dragging the cursor.

toofless said:
Sorry again but I'm not going to even test his code. I'm sure it's the perfect projectile system but due to that I don't need such features like missiles colliding with each other it would only bloat my map's size.

Actually I don't include missile collision at all. There is nothing in the system (I made sure of this) that bloats its performance, and everything that you consider a "useless feature" can easily be disabled (though projectile-collision isn't even included since it's a slow operation in general).

toofless said:
Sure, I do like disagreeing with you because I am a dick and you are a little pompous.

Except you're the one who doesn't know what he's talking about, and PurgeandFire111 likely won't be so helpful next time if you're so arrogant and objective towards ideas that you do not like. If you're going to waste your time coding things and going through problems that have already been solved by other systems, then don't expect anybody to spare a second of their time helping you.
 
Level 8
Joined
Nov 9, 2008
Messages
502
Whatever man.

I made my peace.

FYI, Locusts with Chaos cannot be drag-selected and can be enumerated. That was the whole point.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
You're right, the following code will make a unit enumerable while still maintaining certain "Locust" traits.

JASS:
call UnitAddAbility(<unit>, 'Aloc')
call UnitAddAbility(<unit>, 'Srtt')

But be careful, if you hide/show the unit (using ShowUnit(<unit>, false/true)) it will make the unit selectable by drag, and reveal the health/mana bar above it's head. I believe the unit will still stay invulnerable (and un-attackable), that is what I meant.

When using Chaos and Locust together, be careful about what you do. I was unaware that the unit would be enumerable with Chaos added, though I'm not sure what the consequences of other actions on-top of this may cause. In any circumstance, it is a dangerous water and you should be careful.

Clearly my understanding of the situation was lacking, apologies.
 
But be careful, if you hide/show the unit (using ShowUnit(<unit>, false/true)) it will make the unit selectable by drag, and reveal the health/mana bar above it's head. I believe the unit will still stay invulnerable (and un-attackable), that is what I meant.

Just to add a little foot-note, you can remove the ability before hiding and add it once again after showing the unit for the health/mana bar/drag select to stay disabled after revealing the unit. ;) [Or at least for locust, I never tried it with Chaos]

JASS:
call UnitRemoveAbility(unit,'Aloc')
call ShowUnit(unit,false)
...
call ShowUnit(unit,true)
call UnitAddAbility(unit,'Aloc')

Just in case anyone encounters this problem.
 
Level 8
Joined
Nov 9, 2008
Messages
502
Ok thanks for the info.

Thanks again everyone for your help. I'm working through the projectile maths at the moment.
 
Status
Not open for further replies.
Top