Random TriggerSleepAction() Bug

Karawasa

Element Tower Defense
Reaction score
38
In Element TD there are these Undead creeps that have reincarnation. When they revive, I need certain things done to get them working. I register them when they spawn:

JASS:

function SpawnCreep takes integer j returns nothing
    local unit u
    local real hp
   
    if i > 0 then
        set u = CreateUnit(udg_CreepPlayers[j],udg_Spawns[udg_Level - 1],udg_SpawnX[j],udg_SpawnY[j],270.)
        //call SetUnitUserData( u, GetConvertedPlayerId(GetEnumPlayer()) )
        set udg_CreepOwner[GetUnitIndex(u)] = Player(j)
        call UnitAddAbility(u,udg_Spawn_HP[udg_Level - 1])
        set hp = ((udg_Difficulty[j]-1)*.125)+.50
        call SetWidgetLife(u,GetUnitState(u,UNIT_STATE_MAX_LIFE)*hp)
        call SetUnitMaxState(u,UNIT_STATE_MAX_LIFE,GetWidgetLife(u))
        call SetUnitLifePercentBJ(u,100.)
        
        call SetUnitAbilityLevel(u,'A00V',udg_Difficulty[j])
        call SetUnitAbilityLevel(u,'A01R',udg_Level)
        
        if udg_Extreme_Mode_On == true then
            call UnitAddAbility(u,'A03N')
        endif
           
        if IsUnitType(u, UNIT_TYPE_UNDEAD) == true then
            call TriggerRegisterUnitLifeEvent( gg_trg_Undead_Reincarnate, u, GREATER_THAN_OR_EQUAL, 0.405 )
        endif
           
        if IsUnitType(u, UNIT_TYPE_MECHANICAL) == true then
            call TriggerRegisterUnitManaEvent( gg_trg_Mechanical_Activate, u, GREATER_THAN_OR_EQUAL, 12.00 )
            call SetUnitState(u,UNIT_STATE_MANA,GetRandomReal(0.,12.))
        endif
           
        call IssuePointOrder(u,"move",udg_LeakX[j],udg_LeakY[j])           
       
        set u = null
    endif
endfunction


This detects when they resurrect, and then I do the following:

JASS:

function Trig_Undead_Reincarnate_Conditions takes nothing returns boolean
    if GetUnitAbilityLevel(GetTriggerUnit(),'A00U') == 0 then
        return false
    endif
    return true
endfunction

function Trig_Undead_Reincarnate_Actions takes nothing returns nothing
    local unit u
    local integer i
    
    set i = GetPlayerId(udg_CreepOwner[GetUnitUserData(GetTriggerUnit())])
    set u = GetTriggerUnit()
    
    call UnitRemoveAbility(u,'A00U')
    call SetUnitLifePercentBJ(u,33.33)
    call TriggerSleepAction(0.0)
    call IssuePointOrder(u,&quot;move&quot;,udg_LeakX<i>,udg_LeakY<i>)
    
    set u = null
endfunction

//===========================================================================
function InitTrig_Undead_Reincarnate takes nothing returns nothing
    set gg_trg_Undead_Reincarnate = CreateTrigger()
    call TriggerAddCondition( gg_trg_Undead_Reincarnate, Condition( function Trig_Undead_Reincarnate_Conditions ) )
    call TriggerAddAction( gg_trg_Undead_Reincarnate, function Trig_Undead_Reincarnate_Actions )
endfunction
</i></i>


The bug lies with TriggerSleepAction() in my opinion. Resurrected Undead creeps will rarely and randomly not receive the order to move, but all the actions prior to TriggerSleepAction() work fine.

Any help would be much appreciated.
 
Reaction score
332
I guess this may explain the bugs that occur with creep reincarnation in 4.0.. Have you tried replacing the 0. wait with a 0. timer?

EDIT:

Something like this for the second trigger:

JASS:
globals
    trigger gg_trg_Undead_Reincarnate
    real array udg_LeakX
    real array udg_LeakY
    player array udg_CreepOwner
    timer zerotimer = CreateTimer()
    
    integer N = -1
    unit array s_unit
endglobals

function Callback takes nothing returns nothing
    local integer i = N
    local integer id
    loop
        exitwhen i &lt; 0
        set id = GetPlayerId(udg_CreepOwner[GetUnitUserData(s_unit<i>)]) // I&#039;m assuming this doesn&#039;t randomly change in Actions
        call IssuePointOrder(s_unit<i>,&quot;move&quot;,udg_LeakX<i>,udg_LeakY<i>)
        set i = i-1
    endloop
    set N = -1
endfunction

function Trig_Undead_Reincarnate_Conditions takes nothing returns boolean
    return GetUnitAbilityLevel(GetTriggerUnit(),&#039;A00U&#039;) &gt; 0
endfunction

function Trig_Undead_Reincarnate_Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    
    call UnitRemoveAbility(u,&#039;A00U&#039;)
    call SetUnitLifePercentBJ(u,33.33)
    set N = N+1
    set s_unit[N] = u
    call TimerStart(zerotimer, 0., false, function Callback)
    
    set u = null
endfunction

function InitTrig_Undead_Reincarnate takes nothing returns nothing
    set gg_trg_Undead_Reincarnate = CreateTrigger()
    call TriggerAddCondition( gg_trg_Undead_Reincarnate, Condition( function Trig_Undead_Reincarnate_Conditions ) )
    call TriggerAddAction( gg_trg_Undead_Reincarnate, function Trig_Undead_Reincarnate_Actions )
endfunction</i></i></i></i>


EDIT:

Are you sure the unit reincarnates at "GREATER_THAN_OR_EQUAL, 0.405"?
 

Karawasa

Element Tower Defense
Reaction score
38
I tried the code out, but the result was creeps never receiving the move order after reincarnating.

And yes I am sure about the event, because the other two functions always get carried out fine. I think it has something to do with the fact that the problem involves an issued order.
 

Cohadar

master of fugue
Reaction score
209
I had similar problem with Azshara(madam Zin) boss in my map.
I was not able to solve it so I just gave up. (removed reincarnation and added something else)

I believe reincarnation does some sort of cleanup that has unpredictable consequences (in my case it was creeps shield spells not working)

The best way to act when some standard spell makes problems is to make a custom replacement:
The idea here is to create a new unit after delay instead to use reincarnation.
JASS:
scope UndeadReincarnation initializer Init 

globals
    private constant real REINCARNATION_DELAY = 7.0
    private constant string FX = &quot;Abilities\\Spells\\Orc\\Reincarnation\\ReincarnationTarget.mdl&quot;
endglobals

//===========================================================================
private function Conditions takes nothing returns boolean
    if IsUnitType(GetTriggerUnit(), UNIT_TYPE_UNDEAD) then
        if GetUnitAbilityLevel(GetTriggerUnit(), &#039;A00U&#039;) &gt; 0 then // probably don&#039;t need this or make A00U some dummy ability
            return true
        endif
    endif
    return false
endfunction

//===========================================================================
private function Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    local integer unitid = GetUnitTypeId(u)
    local integer i = GetPlayerId(udg_CreepOwner[GetUnitUserData(u)])
    local effect eff = AddSpecialEffect(FX, x, y)
    
    
    call TriggerSleepAction(REINCARNATION_DELAY)
    
    call ShowUnit(u, false)
    call DestroyEffect(eff)
    call SpawnRevivedCreep(i, unitid, x, y)
    
    set u = null
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition( trig, Condition( function Conditions ) )
    call TriggerAddAction( trig, function Actions )
endfunction

endscope


SpawnRevivedCreep is the key here,
it is your function that you will place at the same library as SpawnCreep that will create the unit at the same way as SpawnCreep but without 'A00U' and other stuff not-needed.

This was creating more problems than solving them, try to avoid dynamic event registration, it leaks:
JASS:
        if IsUnitType(u, UNIT_TYPE_UNDEAD) == true then
            call TriggerRegisterUnitLifeEvent( gg_trg_Undead_Reincarnate, u, GREATER_THAN_OR_EQUAL, 0.405 )
        endif


also instead of my:

you probably want to put
JASS:
call TriggerRegisterCreepEventBJ( trig, EVENT_PLAYER_UNIT_DEATH )

where TriggerRegisterCreepEventBJ is you own custom function:
JASS:
function TriggerRegisterCreepEventBJ takes trigger trig, playerunitevent whichEvent returns nothing
    call TriggerRegisterPlayerUnitEvent(trig, Player(8),  whichEvent, null)
    call TriggerRegisterPlayerUnitEvent(trig, Player(9),  whichEvent, null)
    call TriggerRegisterPlayerUnitEvent(trig, Player(10), whichEvent, null)
    call TriggerRegisterPlayerUnitEvent(trig, Player(11), whichEvent, null)
endfunction
 

Karawasa

Element Tower Defense
Reaction score
38
Thanks for the response Cohadar. What do you recommend doing for optimizing the way I handle Mechanical creeps? They are unlike Undead which can be detected easily in death. You recommend PUI + ShieldSpell, not really sure what you meant by this.

Sort of off-topic, but should I update the map to the newest PUI version? Anything that would need to be changed or can I just copy and paste the code over.

Also about the Orb Engine System, a lot of time has passed since you created it and I was wondering if you had any updates or improvements to it.
 

Cohadar

master of fugue
Reaction score
209
Karawasa said:
Thanks for the response Cohadar. What do you recommend doing for optimizing the way I handle Mechanical creeps? They are unlike Undead which can be detected easily in death. You recommend PUI + ShieldSpell, not really sure what you meant by this.
Perhaps it is best we discuss this over MSN

Karawasa said:
Sort of off-topic, but should I update the map to the newest PUI version? Anything that would need to be changed or can I just copy and paste the code over.
Update it, no need to change anything it is 100% backwards compatible.

Karawasa said:
Also about the Orb Engine System, a lot of time has passed since you created it and I was wondering if you had any updates or improvements to it.
No, ORBEngine in ETD is maximally optimized for TD games, the one in Pyramidal is for games with heroes.

I will be on MSN tonight at 21:00 CET time.
 

Cohadar

master of fugue
Reaction score
209
I just looked at what Mechanical Activate does and found out that it is used to activate Force Field.

So if I am getting this right you are registering mana event so you can set timeout on Force Shield?

In that case a better solution would be to modify ORBEngine core like this:
JASS:
//=========================================================================
//  MAIN ENGINE
//=========================================================================
private function UnitDamaged takes nothing returns boolean
    local integer i
    
    if GetEventDamage()&gt;.1 then
        if GetUnitAbilityLevel(GetTriggerUnit(), ORBConfig_ORB_BUFF) &gt; 0 then 
            call UnitRemoveAbility(GetTriggerUnit(), ORBConfig_ORB_BUFF)

            set i = GetUnitUserData(GetEventDamageSource())
            if PUI_AttackIndex<i> == 0 then
                // Orb effects are connected to the unit when unit attacks for the first time
                set PUI_AttackIndex<i> = GetIndexFromUnitType(GetUnitTypeId(GetEventDamageSource()))
            endif
            
            call TriggerExecute(OrbAttackTrigger[PUI_AttackIndex<i>])

            // --- new code --- //
            if IsUnitType(GetTriggerUnit(), UNIT_TYPE_MECHANICAL) then
                if GetUnitState(GetTriggerUnit(), UNIT_STATE_MANA) &gt;= 12.00 then
                call TriggerExecute(gg_trg_Mechanical_Activate)
            endif
            // --- new code --- //
        endif
    endif

    return false
endfunction
</i></i></i>


and remove dynamic event from SpawnCreep
JASS:
//if IsUnitType(u, UNIT_TYPE_MECHANICAL) == true then
    //call TriggerRegisterUnitManaEvent( gg_trg_Mechanical_Activate, u, GREATER_THAN_OR_EQUAL, 12.00 )
    call SetUnitState(u,UNIT_STATE_MANA,GetRandomReal(0.,12.))
//endif
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top