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
333
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.
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      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