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

[System] Is Unit Moving

Level 17
Joined
Apr 27, 2008
Messages
2,455
It's quite common to pause an unit before moving it with a trigger, and in this case the unit receive a point order "851973".

So you could include this order in this part of code :

// If the unit was ordered to stop or to hold position, disable the boolean
// that says it is moving naturally.
//
private function onOrder takes nothing returns boolean
if (GetIssuedOrderId() == 0xD0004 or GetIssuedOrderId() == 0xD0019) then
call enable(false)
endif
return false
endfunction

PS :
I don't remember if an order event fire when the unit is being unpaused and redo his last order, you should test it.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok... this library needs an update


It fails on recursive events.

JASS:
                if u.moving then
                    set u.moving=false
                    set get=u
                    call thistype.STOP.fire()
                endif

As you clearly see there, you aren't storing the previous value anywhere >.>



Also, you are using GetUnitX and GetUnitY. You should be using GetWidgetX and GetWidgetY.


Furthermore, when a unit dies, you should remove it from the list. Add it back to the list on revival or raise dead. For this, u'd need UnitEvent.

There is no point in tracking a dead unit >: P.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Some maps might need you to track dead units, so you should use a static if.

ah, true that =)


ah well, I made a custom version for me which also includes a list of all native moving units and some fixes...

for example, the native movement ordered thing is not unset when a unit stops moving =).

What this means is that if a unit starts moving again and wasn't ordered, it'll still pick up as a native movement.


yea, I needed this, so I made my own private version with all of the fixes that the current one needs ; D.


edit
Do a units that can naturally move but aren't naturally moving list


In my special version, I just added that =P.

Using this for a stamina system. I loop thru all units that aren't moving and regen some stamina, then I loop through all units that aren't moving and subtract some stamina ;D.
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Oh yea... and this is stupid except for natural movement >.>
if 0<GetUnitAbilityLevel(GetUnitById(u),'Amov') then

Why does that matter if you are checking for changes in x,y coordinates???? that'll just bug up your own system, gg.


happily, since I only care about natural movement, my own thingie will keep that ^^


edit
more bugs..

patrol and attack

on patrol, unit movement reaches 0 while turning

on attack, the unit may start moving w/o receiving an order

On move-
JASS:
set c=GetUnitCurrentOrder(GetUnitById(u))
if (0!=c and 851972!=c and 851993!=c) then
    //natural movement
endif

edit
serious bug..

if there is an enemy unit by a unit and that unit starts running, it won't register as a natural movement... the current order of that unit running by itself also shows up as 0

edit
Solution
EVENT_UNIT_ACQUIRED_TARGET

: \


Here is the only problem...

after a unit is moving and reaches the target, they stop from an unknown order right. If the target then runs, they start up again from an unknown order, but there is no way to see if the unit currently has a target.


So when a unit acquires a target, we can store the target and then when the unit begins moving from an unknown order, if the unit currently has a target and that target is moving, we can assume the unit is moving.

Now let's say that three units are attacking each other

unit 1 -> unit 2 -> unit 3

and unit 3 is just standing there like a dummy


a spell is cast on these 3 units causing them to move.

unit 3 starts moving, it is known to be unnatural since the unit doesn't have a moving target
unit 2 starts moving. It checks the target for movement. Unit 3 is currently moving, so it registers as natural movement.
unit 1 starts moving. It checks the target for movement. Unit 2 is currently moving, so it registers as natural movement.


Now, we can assume that these units are trying to attack each other on their own even though they aren't really moving naturally... so I guess this is a desirable trait right here ^)^.


anyways, I'll be fixing up my own private version for natural unit movement. Bribe, you better fix yours too cuz urs just plain doesn't work.

You should put a note at the top of the thread that this isn't working. if you don't plan on updating it for awhile, move it to submissions until u get it working =).


But anyways, after I fix this last bug, my natural movement should be 100% working ;D.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Using SetUnitX/Y will crash a unit that doesn't have the Amov ability.

Since you are so passionate about the Natural Movement thing, and have done all this work already, I will trim it from my library.

This thing will not fail with recursion. Think about it, I go down a single loop the whole time, there is no way that loop is going to run more than once every 0.03125 seconds.

EDIT:

Updated with support for AIDS. I may submit this on the helper.

I am not supporting AutoIndex because it would really look ugly with all the necessary static if's.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Oh yea, that's true ^)^.

edit
Here's my own private natural movement implementation. It does not include any events, just 2 lists: moving units and units that aren't moving. It is really set up to just check for natural movement : ).

I've tested this and it appears to work 100%... I've tested spells that target units (making the casting unit have to chase after the unit), units running off on their own, and etc...


I think that the only thing this can't detect is Roam :\.

This was written to be horrifyingly optimal : P

JASS:
library IsUnitMoving uses UnitEvent
globals
    private boolean array o     //ordered
    public integer array n      //next moving
    public integer array p      //prev moving
    public integer array nn     //next not moving
    private integer array np    //previous not moving
    private integer array nu    //next unit
    private integer array pu    //previous unit
    public boolean array m      //is moving
    private real array x        //x coord
    private real array y        //y coord
    private boolean array b     //allocated
    private integer th=0        //event leaks
    private integer ch=0        //events up
    private trigger t           //acquired raneg trig
    private integer array k     //target id
    private conditionfunc f     //target function
endglobals
private function OO takes nothing returns boolean
    local integer i
    if (851972==GetIssuedOrderId() or 851993==GetIssuedOrderId()) then
        set i=GetUnitUserData(GetTriggerUnit())
        set k[i]=0
        if (o[i]) then
            set n[p[i]]=n[i]
            set p[n[i]]=p[i]
            
            set nn[i]=nn[0]
            set np[nn[0]]=i
            set np[i]=0
            set nn[0]=i
            
            set x[i]=GetWidgetX(GetUnitById(i))
            set y[i]=GetWidgetY(GetUnitById(i))
            
            set m[i]=false
            set o[i]=false
        endif
    endif
    return false
endfunction
private function OTO takes nothing returns boolean
    local integer i=GetUnitUserData(GetTriggerUnit())
    set k[i]=GetUnitUserData(GetEventTargetUnit())
    if (b[i] and not o[i]) then
        set nn[np[i]]=nn[i]
        set np[nn[i]]=np[i]
        set n[i]=n[0]
        set p[n[0]]=i
        set p[i]=0
        set n[0]=i
        set o[i]=true
        set m[i]=true
        set x[i]=GetWidgetX(GetUnitById(i))
        set y[i]=GetWidgetY(GetUnitById(i))
    endif
    return false
endfunction
private function OC takes nothing returns boolean
    local integer i=GetUnitUserData(GetTriggerUnit())
    set k[i]=GetUnitUserData(GetSpellTargetUnit())
    if (o[i]) then
        set nn[i]=nn[0]
        set np[nn[0]]=i
        set np[i]=0
        set nn[0]=i
        set n[p[i]]=n[i]
        set p[n[i]]=p[i]
        set o[i]=false
        set m[i]=false
        set x[i]=GetWidgetX(GetUnitById(i))
        set y[i]=GetWidgetY(GetUnitById(i))
    endif
    return false
endfunction
private function T takes nothing returns boolean
    set k[GetUnitUserData(GetTriggerUnit())]=GetUnitUserData(GetEventTargetUnit())
    return false
endfunction
private function ib takes integer u returns nothing
    if 0<GetUnitAbilityLevel(GetUnitById(u),'Amov') then
        set nu[u]=nu[0]
        set pu[nu[0]]=u
        set pu[u]=0
        set nu[0]=u
        set x[u]=GetWidgetX(GetIndexedUnit())
        set y[u]=GetWidgetY(GetIndexedUnit())
        set b[u]=true
        set nn[u]=nn[0]
        set np[nn[0]]=u
        set np[u]=0
        set nn[0]=u
        set k[u]=0
    endif
endfunction
private function db takes integer u returns nothing
    if b[u] then
        if (o[u]) then
            set p[n[u]]=p[u]
            set n[p[u]]=n[u]
            set o[u]=false
        else
            set nn[np[u]]=nn[u]
            set np[nn[u]]=np[u]
        endif
        set m[u]=false
        set b[u]=false
        set nu[pu[u]]=nu[u]
        set pu[nu[u]]=pu[u]
        set k[u]=0
    endif
endfunction
private function I takes nothing returns boolean
    set ch=ch+1
    call ib(GetIndexedUnitId())
    call TriggerRegisterUnitEvent(t,GetIndexedUnit(),EVENT_UNIT_ACQUIRED_TARGET)
    return false
endfunction
private function D takes nothing returns boolean
    local integer i=nu[0]
    set th=th+1
    set ch=ch-1
    call db(GetIndexedUnitId())
    if (th>25+ch) then
        call DestroyTrigger(t)
        set t=CreateTrigger()
        call TriggerAddCondition(t,f)
        set th=0
        loop
            exitwhen 0==i
            call TriggerRegisterUnitEvent(t,GetUnitById(i),EVENT_UNIT_ACQUIRED_TARGET)
            set i=nu[i]
        endloop
    endif
    return false
endfunction
private function R takes nothing returns boolean
    call ib(GetEventUnitId())
    return false
endfunction
private function D2 takes nothing returns boolean
    call db(GetEventUnitId())
    return false
endfunction
private function S takes nothing returns nothing
    local integer u=nu[0]
    local real h
    local real g
    local integer q
    loop
        exitwhen (0==u)
        set h=GetWidgetX(GetUnitById(u))
        set g=GetWidgetY(GetUnitById(u))
        if h==x[u] and g==y[u] then
            if m[u] then
                if (o[u]) then
                    set nn[u]=nn[0]
                    set np[nn[0]]=u
                    set np[u]=0
                    set nn[0]=u
                    set n[p[u]]=n[u]
                    set p[n[u]]=p[u]
                    set o[u]=false
                endif
                set m[u]=false
            endif
        else
            set x[u]=h
            set y[u]=g
            if not m[u] then
                set m[u]=true
                if (not o[u]) then
                    set q=GetUnitCurrentOrder(GetUnitById(u))
                    if ((0!=q and 851972!=q and 851993!=q) or (0==q and 0!=k[u])) then
                        set nn[np[u]]=nn[u]
                        set np[nn[u]]=np[u]
                        set n[u]=n[0]
                        set p[n[0]]=u
                        set p[u]=0
                        set n[0]=u
                        set o[u]=true
                    endif
                endif
            endif
        endif
        set u=nu[u]
    endloop
endfunction
private module Init
    private static method onInit takes nothing returns nothing
        local integer i=15
        local trigger t1=CreateTrigger()
        local trigger t2=CreateTrigger()
        local trigger t3=CreateTrigger()
        local player q
        set t=CreateTrigger()
        set f=Condition(function T)
        call TriggerAddCondition(t,f)
        loop
            set q=Player(i)
            call TriggerRegisterPlayerUnitEvent(t1,q,EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
            call TriggerRegisterPlayerUnitEvent(t2,q,EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
            call TriggerRegisterPlayerUnitEvent(t2,q,EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
            call TriggerRegisterPlayerUnitEvent(t3,q,EVENT_PLAYER_UNIT_SPELL_CHANNEL, null)
            exitwhen 0==i
            set i=i-1
        endloop
        call TriggerAddCondition(t1,Condition(function OO))
        call TriggerAddCondition(t2,Condition(function OTO))
        call TriggerAddCondition(t3,Condition(function OC))
        call UnitEvent.RESURRECT.register(Condition(function R))
        call UnitEvent.ANIMATE.register(Condition(function R))
        call UnitEvent.DEATH.register(Condition(function D2))
        set q=null
        set t1=null
        set t2=null
        set t3=null
        call RegisterUnitIndexEvent(Condition(function I), UnitIndexer.INDEX)
        call RegisterUnitIndexEvent(Condition(function D), UnitIndexer.DEINDEX)
        call TimerStart(CreateTimer(),.031250000,true,function S)
    endmethod
endmodule
struct UnitMoving extends array
    implement Init
endstruct
endlibrary
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
GetUnitX is benchmarked faster than GetWidgetX, but GetWidgetX is faster than GetItemX or GetDestructableX. I assume this is because GetUnitX is not a wrapper for GetWidgetX like GetItem/DestructableX are.

Weep, I tried "knocking back" towers and it spun wc3 into an infinite loop. I did this in patch 1.25 but I don't think that's something they fixed.
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
Weep, I tried "knocking back" towers and it spun wc3 into an infinite loop. I did this in patch 1.25 but I don't think that's something they fixed.
You probably did something else wrong.

The issue is that units with 0 Movement - Speed Base get visually bugged when moved via SetUnitX() and SetUnitY(), because even though they are moved, their model is not updated properly.

Units without the 'Amov' ability can be moved without any problems, provided that they had more than 0 base movement speed.

SetUnitPosition() works properly in both cases.

But anyway, your check should be if 0 < GetUnitDefaultMoveSpeed(GetUnit(u)) then instead of if 0 != GetUnitAbilityLevel(GetUnit(u), 'Amov') then (this check will omit towers and alike as well, because they all have 0 base movement speed).

Also, isn't AIDS_RegisterOnDeallocate(Filter(function thistype.index)) supposed to be AIDS_RegisterOnDeallocate(Filter(function thistype.deindex)) ?
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Bit of an improvement with support for native unit movement detection : )

http://www.hiveworkshop.com/forums/submissions-414/system-unit-movement-201449/#post1980942


edit
Improvements in the above library include better removal of units from actives list
Modules for filtered lists with good protection against recursiveness

2 events on unit

->create a unit (fire stationary)
->stationary->kill unit (fire deactivate)

A look at what happens
Code:
Event 1.0: add to stationary
Event 1.1: remove from stationary
Event 2.1: remove from stationary (uh oh!)
Event 2.0: add to stationary


Other issues can be events firing for deindexed units or removing a unit within a fired event and then creating a new unit (that would kill your system).
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
How exactly would the event fire for a deindexed unit? I pull the unit off the list as soon as the unit deindex event fires off. The only way it would fire for a deindexed unit is if the unit's coordinates are magically set to 0 outside of the same instant the deindex event fires.

I can, however, see that removal of a unit from the callback function would break the system. That's pretty good observation. I'll move this back into submissions pending possible approval of your other system and most likely a graveyard to this in the near future.
 
Top