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

Delayed Action

Status
Not open for further replies.
Level 7
Joined
Oct 20, 2010
Messages
182
I have a trigger that is pretty old, from when I was working on a map from over a year ago. I'm unsure how I cobbled this code together (probably by using another persons work as reference) and now, abruptly, it no longer works. I recall it working before, so I suppose I must've messed something up. The issue I am facing is when running this multiple times, it ends up just breaking.
  • Timed Run
    • Events
    • Conditions
    • Actions
      • Set VariableSet Timed_MaxIndex = (Timed_MaxIndex + 1)
      • Set VariableSet Timed_S_UnitSource[Timed_MaxIndex] = Timed_UnitSource
      • Set VariableSet Timed_S_UnitTarget[Timed_MaxIndex] = Timed_UnitTarget
      • Set VariableSet Timed_S_Duration[Timed_MaxIndex] = Timed_Duration
      • Set VariableSet Timed_S_RunTrigger_END[Timed_MaxIndex] = Timed_RunTrigger
      • Set VariableSet Timed_S_SavedReal[Timed_MaxIndex] = Timed_SavedReal
      • Set VariableSet Timed_Counter[Timed_MaxIndex] = 0
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Timed_MaxIndex Equal to 1
        • Then - Actions
          • Trigger - Turn on Timed Loop <gen>
        • Else - Actions
  • Timed Loop
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • For each (Integer Timed_CurrentIndex) from 1 to Timed_MaxIndex, do (Actions)
        • Loop - Actions
          • -------- Adds 1 to counter of (loop current index) --------
          • Set VariableSet Timed_Counter[Timed_CurrentIndex] = (Timed_Counter[Timed_CurrentIndex] + 1)
          • -------- Next, if the counter is equal to the maximum duration then it executes the code for the end of the counter. --------
          • Set VariableSet Timed_S_Onesecond[Timed_CurrentIndex] = (Timed_S_Onesecond[Timed_CurrentIndex] + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Timed_S_Onesecond[Timed_CurrentIndex] Equal to 33
            • Then - Actions
              • Set VariableSet Timed_UnitSource = Timed_S_UnitSource[Timed_CurrentIndex]
              • Set VariableSet Timed_UnitTarget = Timed_S_UnitTarget[Timed_CurrentIndex]
              • Set VariableSet Timed_SavedReal = Timed_S_SavedReal[Timed_CurrentIndex]
              • Trigger - Run Timed_S_RunTrigger_1Second[Timed_CurrentIndex] (checking conditions)
              • -------- - --------
              • Set VariableSet Timed_S_Onesecond[Timed_CurrentIndex] = 0
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Timed_Counter[Timed_CurrentIndex] Equal to (Integer((Timed_S_Duration[Timed_CurrentIndex] x 33.33)))
            • Then - Actions
              • Set VariableSet Timed_UnitSource = Timed_S_UnitSource[Timed_CurrentIndex]
              • Set VariableSet Timed_UnitTarget = Timed_S_UnitTarget[Timed_CurrentIndex]
              • Set VariableSet Timed_SavedReal = Timed_S_SavedReal[Timed_CurrentIndex]
              • Trigger - Run Timed_S_RunTrigger_END[Timed_CurrentIndex] (checking conditions)
              • -------- - --------
              • -------- - --------
              • -------- - --------
              • -------- - --------
              • Set VariableSet Timed_S_RunTrigger_1Second[Timed_CurrentIndex] = Blank Trigger <gen>
              • Set VariableSet Timed_S_RunTrigger_END[Timed_CurrentIndex] = Blank Trigger <gen>
              • Set VariableSet Timed_S_Onesecond[Timed_CurrentIndex] = 0
              • Set VariableSet Timed_S_UnitSource[Timed_CurrentIndex] = Timed_S_UnitSource[Timed_MaxIndex]
              • Set VariableSet Timed_S_UnitTarget[Timed_CurrentIndex] = Timed_S_UnitTarget[Timed_MaxIndex]
              • Set VariableSet Timed_Counter[Timed_CurrentIndex] = Timed_Counter[Timed_MaxIndex]
              • Set VariableSet Timed_S_Duration[Timed_CurrentIndex] = Timed_S_Duration[Timed_MaxIndex]
              • Set VariableSet Timed_S_SavedReal[Timed_CurrentIndex] = Timed_S_SavedReal[Timed_MaxIndex]
              • Set VariableSet Timed_MaxIndex = (Timed_MaxIndex - 1)
              • Set VariableSet Timed_CurrentIndex = (Timed_CurrentIndex - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Timed_MaxIndex Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
            • Else - Actions
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,514
It's odd, someone else was just having trouble with a trigger using this same form of dynamic indexing. Apparently their trigger works the first time but breaks after multiple uses... The thing is, they're on an older patch so it's not like the most recent update broke the game or something.

Anyway, the only things that look off to me are these:
  • Set VariableSet Timed_S_RunTrigger_1Second[Timed_CurrentIndex] = Blank Trigger <gen>
  • Set VariableSet Timed_S_RunTrigger_END[Timed_CurrentIndex] = Blank Trigger <gen>
  • Set VariableSet Timed_S_Onesecond[Timed_CurrentIndex] = 0
Shouldn't these be treated the same way as the other variables and set to their [Timer_MaxIndex] versions? But maybe I'm mistaken.

And this just looks really inefficient:
  • Timed_Counter[Timed_CurrentIndex] Equal to (Integer((Timed_S_Duration[Timed_CurrentIndex] x 33.33)))
You could put in the exact Duration that you want like so:
  • Timed_Counter[Timed_CurrentIndex] Equal to 99
That would be about 3 seconds.

Or you could use a Variable that you Set at the start of the trigger if this duration is meant to be more dynamic:
  • Timed_Counter[Timed_CurrentIndex] Equal to Timed_End[Timed_CurrentIndex]

Another potential optimization:
  • Trigger - Run Timed_S_RunTrigger_1Second[Timed_CurrentIndex] (checking conditions)
Run Trigger (checking conditions) will only check the Conditions block of the trigger. So if it's Conditions block is empty then it's more efficient to use the (ignoring conditions) version. The ignoring version still checks any conditions found in the Actions block like ones used in If Then Else statements.
 
Last edited:
Level 7
Joined
Oct 20, 2010
Messages
182
I suppose the reason the Counter is set to an indexed duration x33 is that I use this as a system to run multiple things through. Every .03 seconds, the counter increases by 1. So, in 1 second it should increase by 33. Then, when the counter becomes equal to the varying duration it initiates its end sequence. (for Example if Duration = 4, the duration actually needs to be adjusted to 4x33 to accurately convert). If I'm working under convoluted, or needed logic I'd be ecstatic to hear of a simpler way.

This is an example of what I use it for:
  • Pocketcity2
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • (DamageEventTarget has an item of type Pocket-city) Equal to True
    • Actions
      • Unit - Set Armor of DaageEventTarget to ((Armor of DamageEventTarget) + 1.00)
      • Set VariableSet Timed_UnitSource = DamageEventTarget
      • Set VariableSet Timed_SavedReal = 0.00
      • Set VariableSet Timed_Duration = 4.00
      • Set VariableSet Timed_RunTrigger = PocketCityRemove2 <gen>
      • Trigger - Run Timed Run <gen> (checking conditions)
  • PocketCityRemove2
    • Events
    • Conditions
    • Actions
      • Unit - Set Armor of Timed_UnitSource to ((Armor of Timed_UnitSource) - 1.00)

HOWEVER: I am happy to report you're correct, and I somehow made the complete oversight of what the purpose of the setting to Max Index was. I changed them to the following,
  • Set VariableSet Timed_S_RunTrigger_1Second[Timed_CurrentIndex] = Timed_S_RunTrigger_1Second[Timed_MaxIndex]
  • Set VariableSet Timed_S_RunTrigger_END[Timed_CurrentIndex] = Timed_S_RunTrigger_END[Timed_MaxIndex]
  • Set VariableSet Timed_S_Onesecond[Timed_CurrentIndex] = Timed_S_Onesecond[Timed_MaxIndex]
and it works without error!
 
Level 7
Joined
Oct 20, 2010
Messages
182
what is the function supposed to do?
The function is a system that I use to simplify the coding of abilities, whether on heroes or items, such as the "Pocket City" above. It simplifies my workflow being able to index varying things that trigger every second or at the end of duration like this instead of having to individually set up an indexer each time. I'm new to coding, so if this is potentially a bad idea... woops!
  • Trigger - Run Timed_S_RunTrigger_1Second[Timed_CurrentIndex] (checking conditions)
  • Trigger - Run Timed_S_RunTrigger_END[Timed_CurrentIndex] (checking conditions)
The first is running the indexed trigger that should occur every second, while the second runs the indexed trigger intended to only launch at the end of a duration. It's a super primitive Delayed Action and Periodic event system...
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,514
Glad to hear it works now.

But for performance reasons I think it'd be a lot better to rely on a variable then to do that x33.33 arithmetic every 0.03 seconds for X indices.

This:
  • Set VariableSet Timed_S_Duration[Timed_MaxIndex] = Timed_Duration

Becomes:
  • Set VariableSet Timed_S_Duration[Timed_MaxIndex] = (Integer(Timed_Duration x 33.33))

Then you can simply reference Timed_S_Duration in your Loop:
  • If - Conditions
  • Timed_Counter[Timed_CurrentIndex] Equal to Timed_S_Duration[Timed_CurrentIndex]

Timed_S_Duration should be an Integer instead of a Real so it doesn't have to be converted to one when comparing it to Timed_Counter.

This would be a lot better unless I'm missing something.
 
Last edited:
Level 7
Joined
Oct 20, 2010
Messages
182
All changes implemented! Thanks for catching my inefficiencies as well. I'm in the habit of getting things to work, often overlooking such silly things - it's only recently I've gone over my countless old triggers and solved leaks. Most of them, at least.

Anyways, I hope this is not a bad place to ask for follow-up help as opposed to creating another forum post. It's this system I've used for applying a "stackable" buff. Specifically, a stacking DoT that'll keep a buff on a unit until all stacks are worn out (in otherwords the unit is no longer trigging any periodic events)

Just for an example with all the fluff trimmed out, this is how I went about it. First trigger the one that runs every second, and the second the one that runs when the indexing/duration ends.
  • Periodc Trigger
    • Events
    • Conditions
    • Actions
      • Unit Group - Add Timed_UnitTarget to unitgroup
  • Finish Trigger
    • Events
    • Conditions
    • Actions
      • Unit Group - Remove Timed_UnitTarget from unitgroup.
This works, because as long as another periodic trigger runs it'll add them back to unitgroup. But for brief moments in time, they aren't in unitgroup (a fill-in for applying a buff, removing a buff). It's important that I have the damage triggered, and also that the DoT damage stacks. I was figuring the easiest way is adding another Int variable into the System Triggers, but I can't wrap my head around how to keep track of stacks that way.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,514
Dynamic Indexing is useful for stacking effects. That and Unit Indexing or Hashtables which allow you to save data directly to units.

So the number of stacks could be tracked directly on the unit: PoisonStacks[unit id] = 5

But the individual stack durations/effects would be handled in a For Loop using Dynamic Indexing.
 
Level 7
Joined
Oct 20, 2010
Messages
182
Is this what you had in mind? Either way, it works!
  • Initial trigger
    • Events
      • Player - Player 1 (Red) types a chat message containing ignitetest as An exact match
    • Conditions
    • Actions
      • Set VariableSet Indexing = (Custom value of Mountain King 0072 <gen>)
      • Set VariableSet IgniteStacks[ITEM_SYS_Key] = (IgniteStacks[Indexing] + 1)
      • -------- - --------
      • -------- This trigger execution is used for running a Periodic and End trigger --------
      • Set VariableSet Timed_UnitSource = Mountain King 0072 <gen>
      • Set VariableSet Timed_UnitTarget = Mountain King 0072 <gen>
      • Set VariableSet Timed_Duration = 5.00
      • Set VariableSet Timed_RunTrigger = Periodic <gen>
      • Set VariableSet Timed_RunTrigger_END = Finish Trigger <gen>
      • Trigger - Run Timed Run <gen> (checking conditions)
      • -------- - --------
      • -------- This trigger execution is used for applying a buff. --------
      • Set VariableSet BUFF_Source = Mountain King 0072 <gen>
      • Set VariableSet BUFF_Target = Mountain King 0072 <gen>
      • Set VariableSet BUFF_Duration = 99.00
      • Set VariableSet BUFF_Ability = Buff Applier: Ignite
      • Trigger - Run Run ApplyBuff <gen> (checking conditions)
  • Periodic
    • Events
    • Conditions
    • Actions
      • Unit - Cause Timed_UnitSource to damage Timed_UnitSource, dealing 1.00 damage of attack type Spells and damage type Normal
  • Finish Trigger
    • Events
    • Conditions
    • Actions
      • Set VariableSet Indexing = (Custom value of Timed_UnitSource)
      • Set VariableSet IgniteStacks[Indexing] = (IgniteStacks[Indexing] - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • IgniteStacks[Indexing] Equal to 0
        • Then - Actions
          • Unit - Remove Ignited from Timed_UnitSource
        • Else - Actions
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,514
Ehh, that doesn't look right. Visualize: Dynamic Indexing

Here's a map I made that recreates the Warpath ability from Dota using these methods. Note that this map may have unforeseen bugs.

Level up the Paladin's Warpath ability and then cast Thunderclap to generate a stack. Each stack adds damage/movement speed. It has a stack limit built into it but you can easily remove that.
 

Attachments

  • Warpath Example 1.w3m
    23.2 KB · Views: 18
Last edited:
Level 7
Joined
Oct 20, 2010
Messages
182
For the Indexing variable, I am utilizing the Unit Indexer by Bribe and running the loop through the original Timed Run trigger I shared. I was testing it, and all seems to work well. Although I notice running the code every .03 seconds is probably a lot worse for performance compared to using a timer. Are there any issues of my code I should be aware of, despite it working? I am hesitant to shift gears considering it works in a vacuum.
 
Status
Not open for further replies.
Top