Trigger is casting twice

Level 37
Joined
Aug 6, 2015
Messages
786
Hello everyone, I’m hoping someone can help me with an issue.
One of my triggers is occasionally firing twice, but this happens very rarely. Some players have reported receiving double or even triple stats from the spell "Agoge" (which is part of the trigger). The map is an 18-player survival game, and there are 3 spells that run a specific trigger that grants the stats when used: Agoge, Life Swell, and Moment of Glory.
I’ve attached a Test Scenario with an example. However, I’ve been unable to replicate the issue myself. I’m primarily a GUI user and not very experienced with coding, so I’m unsure how to diagnose this properly.
Any insights would be greatly appreciated!

The map uses SpellEvent v2.1.0.0
and Dynamic Indexing that i used to give the stats to the user.

I cannot find the problem - or at least what could possible cause the problem.
 

Attachments

  • TestScenario.w3m
    31.8 KB · Views: 7

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
You should never check for an exact value of a Real that has been modified using Arithmetic:
  • Set VariableSet GSS_Counter[GSS_Loop_Integer] = (GSS_Counter[GSS_Loop_Integer] + 0.10)
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • GSS_Counter[GSS_Loop_Integer] Greater than or equal to 0.10
    • Then - Actions
      • Set VariableSet GSS_StatsUser[GSS_Loop_Integer] = GSS_StatsUser[GSS_Index]
      • Set VariableSet GSS_Counter[GSS_Loop_Integer] = GSS_Counter[GSS_Index]
      • Set VariableSet GSS_Index = (GSS_Index - 1)
      • Set VariableSet GSS_Loop_Integer = (GSS_Loop_Integer - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • GSS_Index Equal to 0
        • Then - Actions
          • Trigger - Turn off (This trigger)
        • Else - Actions
    • Else - Actions
There's no guarantee that GSS_Counter will be >= 0.10. It could be 0.999999 after the addition or some extremely close value but not EXACTLY 0.10. You can read about this problem here, it applies to computers in general: Floating Point Imprecision

However, this here is okay because SETTING a Real to an exact value does have exact precision:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • GiveStats_BDR_R[GSS_Loop_Integer] Not equal to 0.00
    • Then - Actions
      • Set VariableSet Back_dmg_reduct[(Player number of GSS_Player[GSS_Loop_Integer])] = (Back_dmg_reduct[(Player number of GSS_Player[GSS_Loop_Integer])] + GiveStats_BDR_R[GSS_Loop_Integer])
      • -------- Text - End --------
      • Set VariableSet GiveStats_BDR_R[GSS_Loop_Integer] = 0.00
    • Else - Actions
So I would change GSS_Counter to be an Integer variable since those are 100% precise. Or a Boolean since that makes more sense in this context.

But I'm curious why you would even use a Periodic Interval for this. Can't you just run GiveStatsAct immediately after setting some of it's desired values?

I don't see why this wouldn't work:
  • Agoge
    • Events
    • Conditions
    • Actions
      • -------- AgogeSettings - 0 --------
      • Set VariableSet AgogeSpellTy = (Ability being cast)
      • Set VariableSet AgogeCaster = (Casting unit)
      • Set VariableSet AgogePlayer = (Owner of AgogeCaster)
      • Set VariableSet AgogePlayer_Nr = (Player number of AgogePlayer)
      • -------- Give Stats - User --------
      • Set VariableSet GiveStatsUser = AgogeCaster
      • -------- DO A BUNCH OF STUFF LIKE SETTING STAT VARIABLES --------
      • -------- DO A BUNCH OF STUFF LIKE SETTING STAT VARIABLES --------
      • -------- DO A BUNCH OF STUFF LIKE SETTING STAT VARIABLES --------
      • Trigger - Run Give Stats <gen> (ignoring conditions)
      • Custom script: call RemoveLocation(udg_HealPos)
  • Give Stats
    • Events
    • Conditions
    • Actions
      • Set VariableSet GSS_Player = (Owner of GiveStatsUser)
      • Set VariableSet GSS_Position = (Position of GSS_StatsUser)
      • -------- - --------
      • Trigger - Run Add Stats To User <gen> (ignoring conditions)
      • Trigger - Run Reset Stat Variables <gen> (ignoring conditions)
  • Add Stats To User
    • Events
    • Conditions
    • Actions
      • -------- HP --------
      • Unit - Set Max HP of GiveStatsUser to ((Max HP of GiveStatsUser) + GiveStats_HP)
      • -------- MP --------
      • Unit - Set Max Mana of GiveStatsUser to ((Max Mana of GiveStatsUser) + GiveStats_MP)
      • -------- ADD THE REST OF THE STATS HERE... --------
  • Reset Stat Variables
    • Events
    • Conditions
    • Actions
      • Set VariableSet GiveStats_HP = 0
      • Set VariableSet GiveStats_MP = 0
      • Set VariableSet GiveStats_HpMp = 0
      • Set VariableSet GiveStats_HP_Regen = 0.00
      • Set VariableSet GiveStats_MP_Regen = 0.00
      • Set VariableSet GiveStats_Armor = 0.00
      • Set VariableSet GiveStats_Damage = 0
      • Set VariableSet GiveStats_Strength = 0
      • Set VariableSet GiveStats_Agility = 0
      • Set VariableSet GiveStats_Inteligence = 0
      • Set VariableSet GiveStats_Experience = 0
      • Set VariableSet GiveStats_Gold = 0
      • Set VariableSet GiveStats_Soul = 0
      • Set VariableSet GiveStats_Spear = 0
      • Set VariableSet GiveStats_CriusDamage = 0.00
      • Set VariableSet GiveStats_CriusChance = 0.00
      • Set VariableSet GiveStats_DolusEvasion = 0.00
      • Set VariableSet GiveStats_BDR = 0.00
 
Last edited:
Level 37
Joined
Aug 6, 2015
Messages
786
You should never check for an exact value of a Real that has been modified using Arithmetic:
  • Set VariableSet GSS_Counter[GSS_Loop_Integer] = (GSS_Counter[GSS_Loop_Integer] + 0.10)
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • GSS_Counter[GSS_Loop_Integer] Greater than or equal to 0.10
    • Then - Actions
      • Set VariableSet GSS_StatsUser[GSS_Loop_Integer] = GSS_StatsUser[GSS_Index]
      • Set VariableSet GSS_Counter[GSS_Loop_Integer] = GSS_Counter[GSS_Index]
      • Set VariableSet GSS_Index = (GSS_Index - 1)
      • Set VariableSet GSS_Loop_Integer = (GSS_Loop_Integer - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • GSS_Index Equal to 0
        • Then - Actions
          • Trigger - Turn off (This trigger)
        • Else - Actions
    • Else - Actions
There's no guarantee that GSS_Counter will be >= 0.10. It could be 0.999999 after the addition or some extremely close value but not EXACTLY 0.10. You can read about this problem here, it applies to computers in general: Floating Point Imprecision

However, this here is okay because SETTING a Real to an exact value does have exact precision:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • GiveStats_BDR_R[GSS_Loop_Integer] Not equal to 0.00
    • Then - Actions
      • Set VariableSet Back_dmg_reduct[(Player number of GSS_Player[GSS_Loop_Integer])] = (Back_dmg_reduct[(Player number of GSS_Player[GSS_Loop_Integer])] + GiveStats_BDR_R[GSS_Loop_Integer])
      • -------- Text - End --------
      • Set VariableSet GiveStats_BDR_R[GSS_Loop_Integer] = 0.00
    • Else - Actions
So I would change GSS_Counter to be an Integer variable since those are 100% precise. Or a Boolean since that makes more sense in this context.
I never used dynamic indexing before, I made it based on this example
But I'm curious why you would even use a Periodic Interval for this. Can't you just run GiveStatsAct immediately after setting some of it's desired values?

I don't see why this wouldn't work:
  • Agoge
    • Events
    • Conditions
    • Actions
      • -------- AgogeSettings - 0 --------
      • Set VariableSet AgogeSpellTy = (Ability being cast)
      • Set VariableSet AgogeCaster = (Casting unit)
      • Set VariableSet AgogePlayer = (Owner of AgogeCaster)
      • Set VariableSet AgogePlayer_Nr = (Player number of AgogePlayer)
      • -------- Give Stats - User --------
      • Set VariableSet GiveStatsUser = AgogeCaster
      • -------- DO A BUNCH OF STUFF LIKE SETTING STAT VARIABLES --------
      • -------- DO A BUNCH OF STUFF LIKE SETTING STAT VARIABLES --------
      • -------- DO A BUNCH OF STUFF LIKE SETTING STAT VARIABLES --------
      • Trigger - Run Give Stats <gen> (ignoring conditions)
      • Custom script: call RemoveLocation(udg_HealPos)
  • Give Stats
    • Events
    • Conditions
    • Actions
      • Set VariableSet GSS_Player = (Owner of GiveStatsUser)
      • Set VariableSet GSS_Position = (Position of GSS_StatsUser)
      • -------- - --------
      • Trigger - Run Add Stats To User <gen> (ignoring conditions)
      • Trigger - Run Reset Stat Variables <gen> (ignoring conditions)
  • Add Stats To User
    • Events
    • Conditions
    • Actions
      • -------- HP --------
      • Unit - Set Max HP of GiveStatsUser to ((Max HP of GiveStatsUser) + GiveStats_HP)
      • -------- MP --------
      • Unit - Set Max Mana of GiveStatsUser to ((Max Mana of GiveStatsUser) + GiveStats_MP)
      • -------- ADD THE REST OF THE STATS HERE... --------
  • Reset Stat Variables
    • Events
    • Conditions
    • Actions
      • Set VariableSet GiveStats_HP = 0
      • Set VariableSet GiveStats_MP = 0
      • Set VariableSet GiveStats_HpMp = 0
      • Set VariableSet GiveStats_HP_Regen = 0.00
      • Set VariableSet GiveStats_MP_Regen = 0.00
      • Set VariableSet GiveStats_Armor = 0.00
      • Set VariableSet GiveStats_Damage = 0
      • Set VariableSet GiveStats_Strength = 0
      • Set VariableSet GiveStats_Agility = 0
      • Set VariableSet GiveStats_Inteligence = 0
      • Set VariableSet GiveStats_Experience = 0
      • Set VariableSet GiveStats_Gold = 0
      • Set VariableSet GiveStats_Soul = 0
      • Set VariableSet GiveStats_Spear = 0
      • Set VariableSet GiveStats_CriusDamage = 0.00
      • Set VariableSet GiveStats_CriusChance = 0.00
      • Set VariableSet GiveStats_DolusEvasion = 0.00
      • Set VariableSet GiveStats_BDR = 0.00
I have several instances that call the GiveStatsUser function. I initially believed that using dynamic indexing would prevent potential bugs, even if GiveStatsUser is triggered multiple times simultaneously.

Like 18 players that could get the stats at the same time.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
I never used dynamic indexing before, I made it based on this
What I mentioned about the Real precision is unrelated to Dynamic Indexing. None of your triggers should ever be doing anything like this:
  • Set Variable MyReal = (MyReal + 1.00)
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • MyReal Equal to 1.00
This is NOT guaranteed to work all of the time.

But I see the confusion. That Dynamic Indexing example is actually making a major mistake, but in it's case the worst outcome will be that the unit will deal an extra damage tick with it's Siphon Life ability. In your case, your worst outcome is much more noticeable (doubling stats).

Like 18 players that could get the stats at the same time.
It all depends on the Actions being used since they can cause other Events to occur. Then it depends on how those Events behave. Will the Events be executed immediately or will they be pushed to the end of the trigger queue?

So you should ask yourself a series of questions related to this.

For instance, does increasing the Strength of a Hero cause any Events to run? Answer: No, no such Event exists, so it's perfectly safe.

Continue asking this question and getting the answer for every single type of stat related action.

For example, giving a hero Experience can cause it to Level up which does have an associated Event:
  • Unit - A unit Gains a level
If this Event executes immediately, which I found after testing that it DOES, then you can run into issues. The issues could occur if any triggers using that Event were to use the GSS system as well, since the previous GSS variables haven't been reset yet, therefore you can give unwanted stats to your unit.

Hopefully that makes sense. You don't need any kind of Waits/Periodic Intervals, you just need to ensure that the variables aren't shared, which can be accomplished with your Dynamic Indexing approach (minus the delay stuff).

Another thing to note, you should never be referencing GSS_User outside of the GSS system. You set the GSS Variables then you immediately Run the GSS system trigger, that's it. Don't reference GSS_User in your Agoge trigger for example, that's what the Agoge_Caster variable is for.

I attached a map showcasing my own approach using local variables. It's not as GUI friendly since it relies heavily on Custom Script but it should never run into issues, assuming you use your GSS system properly like I described above.
 

Attachments

  • GSS System Fixed 1.w3m
    30.3 KB · Views: 3
Last edited:
Level 37
Joined
Aug 6, 2015
Messages
786
What I mentioned about the Real precision is unrelated to Dynamic Indexing. None of your triggers should ever be doing anything like this:
  • Set Variable MyReal = (MyReal + 1.00)
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • MyReal Equal to 1.00
This is NOT guaranteed to work all of the time.

But I see the confusion. That Dynamic Indexing example is actually making a major mistake, but in it's case the worst outcome will be that the unit will deal an extra damage tick with it's Siphon Life ability. In your case, your worst outcome is much more noticeable (doubling stats).


It all depends on the Actions being used since they can cause other Events to occur. Then it depends on how those Events behave. Will the Events be executed immediately or will they be pushed to the end of the trigger queue?

So you should ask yourself a series of questions related to this. For instance, does increasing the Strength of a Hero cause any Events to run? No, no such Event exists, so it's perfectly safe. Continue asking this question and getting the answer for every single type of stat related action.

For example, giving a Hero Experience can cause it to Level up which does have an associated Event:
  • Unit - A unit Gains a level
If this Event executes immediately, which I found after testing that it DOES, then you can run into issues. The issues could occur if any triggers using that Event were to use the GSS system as well, since the previous GSS variables haven't been reset yet, therefore you can give unwanted stats to your unit.

So you don't need any kind of Waits/Periodic Intervals. You just need to ensure that the variables aren't shared, which can be accomplished with your Dynamic Indexing approach (minus the delay stuff).

Another thing to note, you should never be referencing GSS_User outside of the GSS system. You set this Variable then you Run the GSS system trigger, that's it. Don't reference it in your Agoge trigger for example, that's what the Agoge_Caster variable is for.

I attached a map showcasing my own approach using local variables. It's not as GUI friendly since it relies heavily on Custom Script but it should never run into issues, assuming you use your GSS system properly like I described above.
GSS is specifically used only in this system.
Thank you for providing a fix - i will definently apply it.
Some examples of how i am using the GiveStats run from spells/item casts (caster/targets of the spells)

Screenshot_77.png


Screenshot_76.png
Screenshot_75.png
 
Level 37
Joined
Aug 6, 2015
Messages
786
May I ask another small tweak to the system? (I initially didn't put up the floating text features in the examples - can you please add them to the system too?) - I attached them into the map under the events. - I am unsure how to do it effectively. (was also struggling on how to show the floating text to specific players under these circumstances)
For example
if a unit casts a spell on an ally - how to show the floating text to the caster and target of the spell.
 

Attachments

  • GSS System Fixed 1.w3m
    43.3 KB · Views: 3

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
There's an Action to Show/Hide Floating Text for a group of Players.

Floating Text is visible to everyone by default, so you would group your desired players, create the text, hide the text for everyone, and show the text to your group:
  • Player Group - Add (Owner of (Casting unit)) to TextPG
  • Player Group - Add (Owner of (Target unit of ability being cast)) to TextPG
  • Floating Text - Create floating text...
  • Floating Text - Hide (Last created floating text) for (All players)
  • Floating Text - Show (Last created floating text) for TextPG
  • Custom script: call DestroyForce( udg_TextPG )
 
Level 37
Joined
Aug 6, 2015
Messages
786
There's an Action to Show/Hide Floating Text for a group of Players.

Floating Text is visible to everyone by default, so you would group your desired players, create the text, hide the text for everyone, and show the text to your group:
  • Player Group - Add (Owner of (Casting unit)) to TextPG
  • Player Group - Add (Owner of (Target unit of ability being cast)) to TextPG
  • Floating Text - Create floating text...
  • Floating Text - Hide (Last created floating text) for (All players)
  • Floating Text - Show (Last created floating text) for TextPG
  • Custom script: call DestroyForce( udg_TextPG )
I understand that, but in this system specifically, since there are local vars used , how do i change it to work with it.
Here is a piece of floating text for example. (riped from the map atm and pasted in the updated system)
 

Attachments

  • Screenshot_78.png
    Screenshot_78.png
    113.5 KB · Views: 8

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
You could create a new String variable, let's call it GSS_String, and set it up to reference the local variable.

Then use it as your String in your Floating Text action:
  • Custom script: set udg_GSS_String = I2S(hp) + " Health"
  • Floating Text - Create floating text that reads GSS_String above GSS_User...
The above example will display "X Health" where X is the hp variable that was set to GiveStats_HP.

I2S() converts an Integer into a String. R2S() converts a Real into a String. Edit: Fixed, thanks Rheiko, me tired.
 
Last edited:
Level 37
Joined
Aug 6, 2015
Messages
786
You could create a new String variable, let's call it GSS_String, and set it up to reference the local variable.

Then use it as your String in your Floating Text action:
  • Custom script: set udg_GSS_String = I2S(hp) + " Health"
  • Floating Text - Create floating text that reads GSS_String above GSS_User...
The above example will display "X Health" where X is the hp variable that was set to GiveStats_HP.

I2S() converts an Integer into a String. R2S() converts a Real into a String. Edit: Fixed, thanks Rheiko, me tired.
Not sure what I am doing wrong, but the text is only shown for the caster and only once, after the next casts, no text is shown.
(also creating text above unit - doesn't that leak?)

EDIT : Happens after using at the end of the GiveStats Trigger
  • Custom script: call DestroyForce( udg_TextPG )
Maybe i should use this instead?
  • Player Group - Remove all players from TextPG.

Doing this way seems to work. (A spell that is supposed to give stats to caster + target)
Screenshot_81.png
 

Attachments

  • GSS System Fixed 1.w3m
    43.8 KB · Views: 5
Last edited:
Top