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

[Solved] Optimize Fury Swipes

Status
Not open for further replies.
Level 12
Joined
May 16, 2020
Messages
660
Hi all,

I wanted to create Dota 2's Fury Swipe, and the trigger below seems to work.
But I'm never sure if I'm clearing hastables correctly, so would appreciate if someone could check the spell and provide feedback on what I can improve.

Spell description:
Ursa's claws dig deeper wounds in the enemy, causing consecutive attacks to the same enemy to deal more damage. If the same target is not attacked after 10 seconds, the bonus damage is lost.

  • Fury Swipes Setup
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set VariableSet ZA_HashtableStack = (Last created hashtable)

  • Fury Swipes
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (Level of Fury Swipes for DamageEventSource) Greater than 0
      • (DamageEventTarget is A structure) Equal to False
      • DamageEventAttackT Equal to ATTACK_TYPE_HERO
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • Set VariableSet ZA_CV_Caster = (Custom value of DamageEventSource)
      • Custom script: set udg_ZA_Handle = GetHandleId(udg_DamageEventTarget)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (DamageEventSource is in ZA_GroupCaster.) Equal to False
        • Then - Actions
          • Unit Group - Add DamageEventSource to ZA_GroupCaster
        • Else - Actions
      • -------- --------
      • Set VariableSet ZA_StackAmount = (Load ZA_CV_Caster of ZA_Handle from ZA_HashtableStack.)
      • Set VariableSet ZA_StackAmount = (ZA_StackAmount + 1)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in ZA_Group[ZA_CV_Caster]) Equal to 0
        • Then - Actions
          • Custom script: set udg_ZA_Group[udg_ZA_CV_Caster] = CreateGroup()
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ZA_StackAmount Equal to 1
        • Then - Actions
          • Unit - Add Fury Swipes (Dummy) to DamageEventTarget
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (DamageEventTarget is in ZA_Group[ZA_CV_Caster].) Equal to False
            • Then - Actions
              • Unit Group - Add DamageEventTarget to ZA_Group[ZA_CV_Caster]
            • Else - Actions
        • Else - Actions
      • -------- --------
      • Special Effect - Create a special effect attached to the chest of DamageEventTarget using Objects\Spawnmodels\Orc\Orcblood\BattrollBlood.mdl
      • Special Effect - Destroy (Last created special effect)
      • Set VariableSet DamageEventAmount = (DamageEventAmount + (Real((ZA_StackAmount x (10 x (Level of Fury Swipes for DamageEventSource))))))
      • -------- --------
      • Hashtable - Save ZA_StackAmount as ZA_CV_Caster of ZA_Handle in ZA_HashtableStack.
      • -------- --------
      • Set VariableSet ZA_CV_Target = (Custom value of DamageEventTarget)
      • Set VariableSet ZA_StackTimer[ZA_CV_Target] = 10
      • -------- --------
      • -------- START LOOP --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Fury Swipes Loop <gen> is on) Equal to False
        • Then - Actions
          • Game - Display to (All players) the text: ON
          • Trigger - Turn on Fury Swipes Loop <gen>
        • Else - Actions

  • Fury Swipes Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in ZA_GroupCaster and do (Actions)
        • Loop - Actions
          • Set VariableSet ZA_Caster = (Picked unit)
          • Set VariableSet ZA_CV_Caster = (Custom value of ZA_Caster)
          • -------- --------
          • Unit Group - Pick every unit in ZA_Group[ZA_CV_Caster] and do (Actions)
            • Loop - Actions
              • Set VariableSet ZA_Target = (Picked unit)
              • Set VariableSet ZA_CV_Target = (Custom value of ZA_Target)
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Or - Any (Conditions) are true
                    • Conditions
                      • ZA_StackTimer[ZA_CV_Target] Equal to 0
                      • (ZA_Target is alive) Equal to False
                • Then - Actions
                  • Game - Display to (All players) the text: Remove from Group
                  • Unit Group - Remove ZA_Target from ZA_Group[ZA_CV_Caster].
                  • Unit - Remove Fury Swipes (Dummy) from ZA_Target
                  • Unit - Remove Fury Swipes (Debuff) buff from ZA_Target
                  • Custom script: set udg_ZA_Handle = GetHandleId(udg_ZA_Target)
                  • Set VariableSet ZA_StackAmount = (Load ZA_CV_Caster of ZA_Handle from ZA_HashtableStack.)
                  • Set VariableSet ZA_StackAmount = 0
                  • Hashtable - Clear all child hashtables of child ZA_Handle in ZA_HashtableStack.
                  • -------- --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Number of units in ZA_Group[ZA_CV_Caster]) Equal to 0
                    • Then - Actions
                      • Unit Group - Remove ZA_Caster from ZA_GroupCaster.
                      • Custom script: call DestroyGroup (udg_ZA_Group[udg_ZA_CV_Caster])
                    • Else - Actions
                • Else - Actions
                  • Set VariableSet ZA_StackTimer[ZA_CV_Target] = (ZA_StackTimer[ZA_CV_Target] - 1)
                  • Game - Display to (All players) the text: (String(ZA_StackTimer[ZA_CV_Target]))
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in ZA_GroupCaster) Equal to 0
        • Then - Actions
          • Game - Display to (All players) the text: OFF
          • Trigger - Turn off (This trigger)
        • Else - Actions
Thanks!
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Looks good to me. The only thing that seems strange is this Condition that checks whether or not it should create a new group:
  • (Number of units in ZA_Group[ZA_CV_Caster]) Equal to 0

I guess it works or you'd have mentioned it but this makes more sense to me:
  • Custom script: if udg_ZA_Group[udg_ZA_CV_Caster] == null then
  • Custom script: set udg_ZA_Group[udg_ZA_CV_Caster] = CreateGroup()
  • Custom script: endif

It should be considered null while it doesn't exist/after it's destroyed.
 
Level 12
Joined
May 16, 2020
Messages
660
Thanks! However, when using the Custom Script code, for some reason I get units who stay "marked" indefinitely.

But I don't get why. Isn't the upper one the same as the lower one? One just in GUI and the other in JASS?

  • (Number of units in ZA_Group[ZA_CV_Caster]) Equal to 0

and

  • if udg_ZA_Group[udg_ZA_CV_Caster] == null then
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
When you create a Unit Group variable in the Variable Editor it automatically creates a Unit Group object to pair with it. This object is what contains the units and the variable is what allows you to reference said object. However, Unit Group arrays are different. They don't assume that you're going to use all 32,768 Indices available and as a result they don't create any Unit Group objects. It'd be really inefficient to create 32k Unit Groups JUST IN CASE. This is why you have to manually create your own Unit Group using the CreateGroup() function.

This means that before you run the CreateGroup() function, your Unit Group variable is considered NULL, which means that it has no Unit Group object paired to it. It's referencing nothing. Again, this is only true for Unit Group ARRAYS.

So checking if a Variable is Null is like checking whether or not it's been assigned/set to anything. If ZA_Group[#] isn't assigned to a Unit Group then it will be considered Null. This would be the case after using DestroyGroup() since you'd be destroying it's Unit Group object. Keep in mind that Integers, Reals, Booleans, and Strings can never be Null because they always have a default value (0, 0.00, false, ""). But special types like Items, Units, Unit Groups, etc. can be Null.

Now here's where it gets a little tricky. The reason your Number of units function works must have to do with how blizzard coded it:
vJASS:
// this is the function for (Number of units in Unit Group):
CountUnitsInGroup(g)
vJASS:
// this is how it works:
function CountUnitsInGroup takes group g returns integer
    // If the user wants the group destroyed, remember that fact and clear
    // the flag, in case it is used again in the callback.
    local boolean wantDestroy = bj_wantDestroyGroup
    set bj_wantDestroyGroup = false

    set bj_groupCountUnits = 0
    call ForGroup(g, function CountUnitsInGroupEnum)

    // If the user wants the group destroyed, do so now.
    if (wantDestroy) then
        call DestroyGroup(g)
    endif
    return bj_groupCountUnits
endfunction
If the Unit Group is Null OR Empty then it returns a default value of 0 (bj_groupCountUnits). Otherwise, it proceeds with getting the unit count as normal.

Long story short, directly checking if the Unit Group is NULL is slightly more efficient than checking if the Unit Group is empty (Number of units = 0). It's removing an extra function call and produces the same result. It's also safer because you aren't assuming anything.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
"some reason I get units who stay "marked" indefinitely."
I managed to miss this comment before.

I'm not sure why that is happening, I remember using "null" like this before but maybe I'm mixing things up.
Maybe you need to manually Null the Unit Group for it to work (sort of contradicts what I said in my previous post):
  • Unit Group - Remove ZA_Caster from ZA_GroupCaster.
  • Custom script: call DestroyGroup (udg_ZA_Group[udg_ZA_CV_Caster])
  • Custom script: set udg_ZA_Group[udg_ZA_CV_Caster] = null

Anyway, you have a working method so I would just use that.

Also, one minor thing. Before you clear the Child hashtable in Fury Swipes Loop you have these 2 unnecessary actions:
  • Set VariableSet ZA_StackAmount = (Load ZA_CV_Caster of ZA_Handle from ZA_HashtableStack.)
  • Set VariableSet ZA_StackAmount = 0
The unit will still have the same number of Stacks in the Hashtable after this. ZA_StackAmount is simply copying that value while the Hashtable stacks remain the same. Deleting these 2 and clearing the child in the hashtable is all you need to do.
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Thanks so much Uncle, especially for the easy to understand explanation.
I never understood why sometimes I have to "create groups" and sometimes it magically works - now it makes sense!

"some reason I get units who stay "marked" indefinitely."
I tested some more, and it happens AFTER the trigger is ON and OFF the first time.
So for example, after the 1st "OFF" (that's my text at the end of the third trigger), if I attack again a unit it goes "ON", but then I don't see any of my Display Text afterwards.

  • unitgroup.gif
    Unit Group - Remove ZA_Caster from ZA_GroupCaster.
  • page.gif
    Custom script: call DestroyGroup (udg_ZA_Group[udg_ZA_CV_Caster])
  • page.gif
    Custom script: set udg_ZA_Group[udg_ZA_CV_Caster] = null
This fixed it (in combination with your first suggestion that you wrote makes more sense)! Thanks!


EDIT: I found an issue though. Will update again when I tested some more.

I added a screenshot. When two heroes who have both the Fury Swipe Ability attack the same enemy, the "countdown" duration happens twice as fast. So for example instead of going from 8 to 7 (remaining duration), it goes from 8 to 6...

1638344717913.png


Maybe instead of using the Custom Value of the target, I can use its Handle ID?

  • Set VariableSet ZA_StackTimer[ZA_Handle] = 10
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Update:
I fixed the above problem, meaning the timer doesn't jump "2 units" if two casters attack the same target.
The solution was not using the Target Custom Value to store the timers, but now a second hashtable with the Caster custom value + handle of the Target.

  • Fury Swipes Setup
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set VariableSet FurySwipes_Hashtable_Amount = (Last created hashtable)
      • Hashtable - Create a hashtable
      • Set VariableSet FurySwipes_Hashtable_Timer = (Last created hashtable)
  • Fury Swipes
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (Level of Fury Swipes for DamageEventSource) Greater than 0
      • (DamageEventTarget is A structure) Equal to False
      • DamageEventAttackT Equal to ATTACK_TYPE_HERO
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • Set VariableSet FurySwipes_CV_Caster = (Custom value of DamageEventSource)
      • Custom script: set udg_FurySwipes_Handle = GetHandleId(udg_DamageEventTarget)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (DamageEventSource is in FurySwipes_GroupCaster.) Equal to False
        • Then - Actions
          • Unit Group - Add DamageEventSource to FurySwipes_GroupCaster
        • Else - Actions
      • -------- --------
      • Set VariableSet FurySwipes_StackAmount = (Load FurySwipes_CV_Caster of FurySwipes_Handle from FurySwipes_Hashtable_Amount.)
      • Set VariableSet FurySwipes_StackAmount = (FurySwipes_StackAmount + 1)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in FurySwipes_Group[FurySwipes_CV_Caster]) Equal to 0
        • Then - Actions
          • Custom script: set udg_FurySwipes_Group[udg_FurySwipes_CV_Caster] = CreateGroup()
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FurySwipes_StackAmount Equal to 1
        • Then - Actions
          • Unit - Add Fury Swipes (Dummy) to DamageEventTarget
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (DamageEventTarget is in FurySwipes_Group[FurySwipes_CV_Caster].) Equal to False
            • Then - Actions
              • Unit Group - Add DamageEventTarget to FurySwipes_Group[FurySwipes_CV_Caster]
            • Else - Actions
        • Else - Actions
      • -------- --------
      • Special Effect - Create a special effect attached to the chest of DamageEventTarget using Objects\Spawnmodels\Orc\Orcblood\BattrollBlood.mdl
      • Special Effect - Destroy (Last created special effect)
      • Set VariableSet DamageEventAmount = (DamageEventAmount + (Real((FurySwipes_StackAmount x (10 x (Level of Fury Swipes for DamageEventSource))))))
      • -------- --------
      • Hashtable - Save FurySwipes_StackAmount as FurySwipes_CV_Caster of FurySwipes_Handle in FurySwipes_Hashtable_Amount.
      • -------- --------
      • Hashtable - Save 10 as FurySwipes_CV_Caster of FurySwipes_Handle in FurySwipes_Hashtable_Timer.
      • -------- --------
      • -------- START LOOP --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Fury Swipes Loop <gen> is on) Equal to False
        • Then - Actions
          • Game - Display to (All players) the text: ON
          • Trigger - Turn on Fury Swipes Loop <gen>
        • Else - Actions
  • Fury Swipes Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in FurySwipes_GroupCaster and do (Actions)
        • Loop - Actions
          • Set VariableSet FurySwipes_Caster = (Picked unit)
          • Set VariableSet FurySwipes_CV_Caster = (Custom value of FurySwipes_Caster)
          • -------- --------
          • Unit Group - Pick every unit in FurySwipes_Group[FurySwipes_CV_Caster] and do (Actions)
            • Loop - Actions
              • Set VariableSet FurySwipes_Target = (Picked unit)
              • Custom script: set udg_FurySwipes_Handle = GetHandleId(udg_FurySwipes_Target)
              • Set VariableSet FurySwipes_StackTimer = (Load FurySwipes_CV_Caster of FurySwipes_Handle from FurySwipes_Hashtable_Timer.)
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Or - Any (Conditions) are true
                    • Conditions
                      • FurySwipes_StackTimer Equal to 0
                      • (FurySwipes_Target is alive) Equal to False
                • Then - Actions
                  • Game - Display to (All players) the text: Remove from Group
                  • Unit Group - Remove FurySwipes_Target from FurySwipes_Group[FurySwipes_CV_Caster].
                  • Unit - Remove Fury Swipes (Dummy) from FurySwipes_Target
                  • Unit - Remove Fury Swipes buff from FurySwipes_Target
                  • -------- --------
                  • Hashtable - Clear all child hashtables of child FurySwipes_Handle in FurySwipes_Hashtable_Amount.
                  • Hashtable - Clear all child hashtables of child FurySwipes_Handle in FurySwipes_Hashtable_Timer.
                  • -------- --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Number of units in FurySwipes_Group[FurySwipes_CV_Caster]) Equal to 0
                    • Then - Actions
                      • Unit Group - Remove FurySwipes_Caster from FurySwipes_GroupCaster.
                      • Custom script: call DestroyGroup (udg_FurySwipes_Group[udg_FurySwipes_CV_Caster])
                    • Else - Actions
                • Else - Actions
                  • Set VariableSet FurySwipes_StackTimer = (FurySwipes_StackTimer - 1)
                  • Hashtable - Save FurySwipes_StackTimer as FurySwipes_CV_Caster of FurySwipes_Handle in FurySwipes_Hashtable_Timer.
                  • Game - Display to (All players) the text: (String(FurySwipes_StackTimer))
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in FurySwipes_GroupCaster) Equal to 0
        • Then - Actions
          • Game - Display to (All players) the text: OFF
          • Trigger - Turn off (This trigger)
        • Else - Actions

However, I struggle with stopping the trigger from turning off when 1 timer reaches zero, even though another timer is still ongoing (still referring to the situation where 2 casters attack the same target).

Would you know a solution to this...?
 
Level 12
Joined
May 16, 2020
Messages
660
Mmm it works on same target, but different targets are now broken. Also, I need to keep in mind that I can only remove the ability (to show the debuff) once the Integer is 0 on a target.

So I'm thinking: Does the Integer maybe need an array, based on the Target HandleID?

  • Fury Swipes
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (Level of Fury Swipes for DamageEventSource) Greater than 0
      • (DamageEventTarget is A structure) Equal to False
      • DamageEventAttackT Equal to ATTACK_TYPE_HERO
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • Set VariableSet FurySwipes_CV_Caster = (Custom value of DamageEventSource)
      • Custom script: set udg_FurySwipes_Handle = GetHandleId(udg_DamageEventTarget)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (DamageEventSource is in FurySwipes_GroupCaster.) Equal to False
        • Then - Actions
          • Unit Group - Add DamageEventSource to FurySwipes_GroupCaster
        • Else - Actions
      • -------- --------
      • Set VariableSet FurySwipes_StackAmount = (Load FurySwipes_CV_Caster of FurySwipes_Handle from FurySwipes_Hashtable_Amount.)
      • Set VariableSet FurySwipes_StackAmount = (FurySwipes_StackAmount + 1)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in FurySwipes_Group[FurySwipes_CV_Caster]) Equal to 0
        • Then - Actions
          • Custom script: set udg_FurySwipes_Group[udg_FurySwipes_CV_Caster] = CreateGroup()
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FurySwipes_StackAmount Equal to 1
        • Then - Actions
          • Set VariableSet FurySwipes_Index = (FurySwipes_Index + 1)
          • Game - Display to (All players) the text: (Index: + (String(FurySwipes_Index)))
          • -------- --------
          • Unit - Add Fury Swipes (Dummy) to DamageEventTarget
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (DamageEventTarget is in FurySwipes_Group[FurySwipes_CV_Caster].) Equal to False
            • Then - Actions
              • Unit Group - Add DamageEventTarget to FurySwipes_Group[FurySwipes_CV_Caster]
            • Else - Actions
        • Else - Actions
      • -------- --------
      • Special Effect - Create a special effect attached to the chest of DamageEventTarget using Objects\Spawnmodels\Orc\Orcblood\BattrollBlood.mdl
      • Special Effect - Destroy (Last created special effect)
      • Set VariableSet DamageEventAmount = (DamageEventAmount + (Real((FurySwipes_StackAmount x (10 x (Level of Fury Swipes for DamageEventSource))))))
      • -------- --------
      • Hashtable - Save FurySwipes_StackAmount as FurySwipes_CV_Caster of FurySwipes_Handle in FurySwipes_Hashtable_Amount.
      • -------- --------
      • Hashtable - Save 10 as FurySwipes_CV_Caster of FurySwipes_Handle in FurySwipes_Hashtable_Timer.
      • -------- --------
      • -------- START LOOP --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Fury Swipes Loop <gen> is on) Equal to False
        • Then - Actions
          • Game - Display to (All players) the text: ON
          • Trigger - Turn on Fury Swipes Loop <gen>
        • Else - Actions
  • Fury Swipes Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in FurySwipes_GroupCaster and do (Actions)
        • Loop - Actions
          • Set VariableSet FurySwipes_Caster = (Picked unit)
          • Set VariableSet FurySwipes_CV_Caster = (Custom value of FurySwipes_Caster)
          • -------- --------
          • Unit Group - Pick every unit in FurySwipes_Group[FurySwipes_CV_Caster] and do (Actions)
            • Loop - Actions
              • Set VariableSet FurySwipes_Target = (Picked unit)
              • Custom script: set udg_FurySwipes_Handle = GetHandleId(udg_FurySwipes_Target)
              • Set VariableSet FurySwipes_StackTimer = (Load FurySwipes_CV_Caster of FurySwipes_Handle from FurySwipes_Hashtable_Timer.)
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Or - Any (Conditions) are true
                    • Conditions
                      • FurySwipes_StackTimer Equal to 0
                      • (FurySwipes_Target is alive) Equal to False
                • Then - Actions
                  • Set VariableSet FurySwipes_Index = (FurySwipes_Index - 1)
                  • Game - Display to (All players) the text: (Index: + (String(FurySwipes_Index)))
                  • -------- --------
                  • Game - Display to (All players) the text: Remove from Group
                  • Unit Group - Remove FurySwipes_Target from FurySwipes_Group[FurySwipes_CV_Caster].
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • FurySwipes_Index Equal to 0
                    • Then - Actions
                      • Game - Display to (All players) the text: Remove Debuff
                      • Unit - Remove Fury Swipes (Dummy) from FurySwipes_Target
                      • Unit - Remove Fury Swipes buff from FurySwipes_Target
                      • Hashtable - Clear all child hashtables of child FurySwipes_Handle in FurySwipes_Hashtable_Amount.
                      • Hashtable - Clear all child hashtables of child FurySwipes_Handle in FurySwipes_Hashtable_Timer.
                    • Else - Actions
                  • -------- --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Number of units in FurySwipes_Group[FurySwipes_CV_Caster]) Equal to 0
                    • Then - Actions
                      • Unit Group - Remove FurySwipes_Caster from FurySwipes_GroupCaster.
                      • Custom script: call DestroyGroup (udg_FurySwipes_Group[udg_FurySwipes_CV_Caster])
                    • Else - Actions
                • Else - Actions
                  • Set VariableSet FurySwipes_StackTimer = (FurySwipes_StackTimer - 1)
                  • Hashtable - Save FurySwipes_StackTimer as FurySwipes_CV_Caster of FurySwipes_Handle in FurySwipes_Hashtable_Timer.
                  • Game - Display to (All players) the text: (String(FurySwipes_StackTimer))
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in FurySwipes_GroupCaster) Equal to 0
        • Then - Actions
          • Game - Display to (All players) the text: OFF
          • Trigger - Turn off (This trigger)
        • Else - Actions
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
I needed to make the spell myself to figure out what's best. Here's how I approached it (bug free as far as I know):
  • Fury Swipes Dmg Event
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • IsDamageAttack Equal to True
    • Actions
      • Set VariableSet FurySwipes_Level = (Level of Fury Swipes for DamageEventSource)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FurySwipes_Level Greater than 0
        • Then - Actions
          • Set VariableSet FurySwipes_CV = (Custom value of DamageEventSource)
          • Custom script: set udg_FurySwipes_Handle = udg_DamageEventTarget
          • -------- --------
          • -------- Increase Stacks by 1: --------
          • Set VariableSet FurySwipes_Stacks = ((Load FurySwipes_CV of (Key FurySwipes_Handle.) from FurySwipes_Hash.) + 1)
          • Hashtable - Save FurySwipes_Stacks as FurySwipes_CV of (Key FurySwipes_Handle.) in FurySwipes_Hash.
          • -------- --------
          • -------- Track a new instance of Fury Swipes on the target (it's important that the Child key is -1 so it doesn't interfere with Stacks): --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FurySwipes_Stacks Equal to 1
            • Then - Actions
              • Unit - Add Fury Swipes (Art) to DamageEventTarget
              • Hashtable - Save ((Load -1 of (Key FurySwipes_Handle.) from FurySwipes_Hash.) + 1) as -1 of (Key FurySwipes_Handle.) in FurySwipes_Hash.
            • Else - Actions
          • -------- --------
          • -------- Increase damage dealt: --------
          • Set VariableSet DamageEventAmount = (DamageEventAmount + ((Real(FurySwipes_Stacks)) x (10.00 x (Real(FurySwipes_Level)))))
          • -------- --------
          • -------- Start/restart the timer that will reset Stacks when it expires. (Warning: All of your Key variables used by this system must have different values and be > 0 or this won't work properly ) --------
          • Custom script: call TS_StartUnitInt( udg_DamageEventTarget, udg_FurySwipes_CV, 5.00, false, udg_Key_FurySwipes, gg_trg_FurySwipesCallback )
        • Else - Actions
  • FurySwipesCallback
    • Events
    • Conditions
    • Actions
      • -------- This trigger is using the GUI Timer System. --------
      • -------- TS_Source = Enemy unit with Fury Swipes buff --------
      • -------- TS_Integer = Custom value of Fury Swipe's user (Ursa) --------
      • -------- --------
      • Custom script: set udg_FurySwipes_Handle = udg_TS_Source
      • -------- --------
      • -------- Set Stacks to 0: --------
      • Hashtable - Save 0 as TS_Integer of (Key FurySwipes_Handle.) in FurySwipes_Hash.
      • -------- --------
      • -------- Subtract 1 from Fury Swipe Source count (how many separate sources of Fury Swipes exist on this unit): --------
      • Hashtable - Save ((Load -1 of (Key FurySwipes_Handle.) from FurySwipes_Hash.) - 1) as -1 of (Key FurySwipes_Handle.) in FurySwipes_Hash.
      • -------- --------
      • -------- Remove buff if the unit isn't affected by ANY sources of Fury Swipe: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Load -1 of (Key FurySwipes_Handle.) from FurySwipes_Hash.) Equal to 0
        • Then - Actions
          • Unit - Remove Fury Swipes (Art) from TS_Source
          • Game - Display to (All players) for 30.00 seconds the text: (Name of TS_Source)
        • Else - Actions

I'm using a GUI Timer System I threw together. It gives you access to Jass local timers that you can interact with in GUI.

Edit: After testing, I realized that this only works for 1 Fury Swipe user. It will break if two Fury Swipe users attack the same target. Trying to figure out why.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Fixed it. Everything should work now.

You could optimize some of the Hashtable loading with a variable as I was too lazy to do so.

Also, I realize the Timer System is confusing, but it's not so tough once you get the hang of it.
  • Custom script: call TS_StartUnitInt( udg_DamageEventTarget, udg_FurySwipes_CV, 5.00, false, udg_Key_FurySwipes, gg_trg_FurySwipesCallback )
The TS Setup trigger has a bunch of comments that break down how everything works.

The most difficult thing to understand is the Key system. It works like this:
A Key value of 0 tells the system to create a brand new timer when you call your TS_ function.
This is useful for cases where you don't want to restart the timer.

If you did want to restart the timer, like in the case of Fury Swipes where your attacks do exactly that, you need to provide a Key value that is greater than 0. Not only should it be > 0, but it must have a unique value that is dedicated to whatever it's used for. In this case it's dedicated to Fury Swipes, so I created an Integer variable called Key_FurySwipes and I set it's Initial Value to 1. If you were to make another trigger that uses a Key > 0 then you couldn't use the Key value of 1 since that's already taken. But any number > 1 is still available. It's a fairly easy pattern to follow, it's similar to Unit Indexing where no two units should ever share the same Custom Value.

Lastly, there's the SwapKey boolean. I don't like that I need this as it makes things extra confusing but it was required. Here's two different explanations for it.

Simple explanation:
If your trigger isn't working properly then try changing this value from false to true (or vice versa).

Confusing explanation:
The Timer System uses a Hashtable to save these Timers and the data linked to them. SwapKey determines the Parent of the Timer in said Hashtable. The Parent will either be the first set of data you provided if SwapKey = False or the second set of data you provided if SwapKey = True. By sets of data I mean the data that you want to link to the timer, like the Unit and Integer in TS_StartUnitInt().

For Fury Swipes, I set SwapKey = True because that makes the system use the Custom value of the Fury Swipe's user as the Parent key in the Timer Hashtable. This made things work properly as opposed to setting it to False and using the Handle of the DamageEventTarget. This is because the Custom value was unique to the Fury Swipe's user where as the Target could be shared by multiple Fury Swipe users. It was the difference between a unique Parent and a shared Parent, and we know how things can go wrong if you try to use the same Parent in a Hashtable for multiple things.
 

Attachments

  • Fury Swipes 2.w3x
    68.4 KB · Views: 12
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Thanks so much! I will need to study the Timer System and your implemention properly, but my hope is that this timer system will even help me create Bristle's Quill Spray finally in GUI :)

There is a bug though:
If you attack enemy 1 and then enemy 2 with the hero, the enemy 1 will be marked indefinitely. You then need to attack enemy 1 again for the debuff to disappear.

I'm adding my map as I found it easier to see if the stacking is working correctly with multiple casters attacking one target (it does!)
 

Attachments

  • Land of Legends v0.2.46.w3m
    68.9 MB · Views: 12
Level 12
Joined
May 16, 2020
Messages
660
Thanks Uncle. I think highlevel I understand the principle of the GUI part (the JASS part I have no clue - I will just use your functions to interact with the system).

Regarding the bug: Is the problem maybe that "udg_Key_FurySwipes" has not been defined?
You wrote "This uses a unique Key to allow it to restart...". So since "udg_Key_FurySwipes" is always 0 (not defined), that could be the error?

I added one line to your trigger and this seems to work:
  • Custom script: set udg_Key_FurySwipes = GetHandleId(udg_DamageEventTarget)
  • Custom script: call TS_StartUnitInt( udg_DamageEventTarget, udg_FurySwipes_CV, 10.00, false, udg_Key_FurySwipes, true, gg_trg_FurySwipesCallback )

One ask please: Can you please adjust the timer system so that if the "Target" is dead, the timer for this dead unit stops instantly?
Currently (I think) it counts down the full timer, event if let's say the target dies with the first attack. Hence it uses up some performance for no reason.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Thanks Uncle. I think highlevel I understand the principle of the GUI part (the JASS part I have no clue - I will just use your functions to interact with the system).

Regarding the bug: Is the problem maybe that "udg_Key_FurySwipes" has not been defined?
You wrote "This uses a unique Key to allow it to restart...". So since "udg_Key_FurySwipes" is always 0 (not defined), that could be the error?

I added one line to your trigger and this seems to work:
  • Custom script: set udg_Key_FurySwipes = GetHandleId(udg_DamageEventTarget)
  • Custom script: call TS_StartUnitInt( udg_DamageEventTarget, udg_FurySwipes_CV, 10.00, false, udg_Key_FurySwipes, true, gg_trg_FurySwipesCallback )

One ask please: Can you please adjust the timer system so that if the "Target" is dead, the timer for this dead unit stops instantly?
Currently (I think) it counts down the full timer, event if let's say the target dies with the first attack. Hence it uses up some performance for no reason.
The Keys need to be an Integer > 0 if you want the Timer to be able to Restart instead of always creating a new one. It's value cannot conflict with other Key values so it's not wise to use the Handle of DamageEventTarget. Instead, set it to 1 in the Variable Editor since it's your first Key. Your second Key should have a value of 2, etc...

Regarding the Timer System, I'll think about that but I think it'd make more sense to just use a Periodic Timer and check if the unit is dead yourself.
 
Level 12
Joined
May 16, 2020
Messages
660
The Keys need to be an Integer > 0 if you want the Timer to be able to Restart instead of always creating a new one. It's value cannot conflict with other Key values so it's not wise to use the Handle of DamageEventTarget. Instead, set it to 1 in the Variable Editor since it's your first Key. Your second Key should have a value of 2, etc...

Confused what you mean here. I read your Key explanation above again and see that it's set to 1 specifically for Fury Swipes.
But the fact that the bug is fixed if I create "individual" Keys per unit (by misusing the HandleID), must mean that there is some problem with the Timer system?

Regarding the Timer System, I'll think about that but I think it'd make more sense to just use a Periodic Timer and check if the unit is dead yourself.

Do you mean adding a condition "if TS_Source = alive" here?

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Load -1 of (Key FurySwipes_Handle.) from FurySwipes_Hashtable.) Equal to 0
      • (TS_Source is alive) Equal to False
    • Then - Actions
      • Unit - Remove Fury Swipes (Aura) from TS_Source
      • Unit - Remove Fury Swipes buff from TS_Source
      • Game - Display to (All players) the text: OFF
    • Else - Actions
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Confused what you mean here. I read your Key explanation above again and see that it's set to 1 specifically for Fury Swipes.
But the fact that the bug is fixed if I create "individual" Keys per unit (by misusing the HandleID), must mean that there is some problem with the Timer system?
It works for the wrong reason. Technically it's fine if you never use the Timer System for anything else though.

It may be smarter to just use something other than this system, it's not designed to handle this sort of complexity. That being said, I recommend using the same local timer + hashtable method that the system uses in order to track the duration of fury swipes and the source/target/custom value (whatever information is needed). You can create local timers in Custom script and store them in a hashtable using their handle id as the Parent key. Then save other data to this handle id at the different Child keys.

Regarding your second question, that looks correct aside from a missing OR condition and the fact that you're never destroying the timer:
  • Custom script: call TS_Clear()
That destroys the current expired timer in your Callback trigger. This is only necessary for Repeating timers since the system will automatically destroy One shot timers for you.
 
Level 12
Joined
May 16, 2020
Messages
660
I think I will need the Timer System for more than one hero, because after finishing Ursa/Spirit Breaker, I wanted to tackle Bristleback and his Quill Spray again. And for Bristle I will definitely have the same issue as here, but even more complex because the Quills all have their own timer...

Is there another Timer System for GUIers, or did you write the above system specifically for this?
I checked the Spell Section / Google and found this. But it's for JASS.

That destroys the current expired timer in your Callback trigger. This is only necessary for Repeating timers since the system will automatically destroy One shot timers for you.

So I only need "call TS_Clear()" because of the "wrong reasons" as you write above?
If the Key = 1 (for all targets), then I wouldn't need this line?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
I think I will need the Timer System for more than one hero, because after finishing Ursa/Spirit Breaker, I wanted to tackle Bristleback and his Quill Spray again. And for Bristle I will definitely have the same issue as here, but even more complex because the Quills all have their own timer...

Is there another Timer System for GUIers, or did you write the above system specifically for this?
I checked the Spell Section / Google and found this. But it's for JASS.



So I only need "call TS_Clear()" because of the "wrong reasons" as you write above?
If the Key = 1 (for all targets), then I wouldn't need this line?
The Keys that I use in the Timer System are the Parent key in the TSHashtable where the Timer and it's linked data are saved. So if you use the Handle Id of a unit as the Key for both Fury Swipes AND Quill Spray for example then you can run into a conflict because they'll overwrite one another.

You'd basically be doing this, where the [Index] is the Key:
  • Set Variable TimerSystem[Handle id of unit] = Some timer
  • Set Variable TimerSystem[Handle id of unit] = Some other timer
  • // the first timer is now lost
The SwapKey boolean allows you to swap where this Key is saved in the Hashtable, so if SwapKey is True then you'll save the Timer and it's data to the Child index rather than the Parent index.


And the timer needs to be destroyed because it's finished running. The system can't know when to end your Repeating timers because that is entirely up to you -> A unit is dead, some integer reached 0, a buff is gone, etc. Therefore, you need to manually destroy the Repeating timer with the Clear() function. One shot timers on the other hand don't require this extra step since the system knows when they're finished and will destroy (Clear) them for you. This is entirely unrelated to the Key value.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Understood, thanks! But then so... how can I solve the problem regarding Fury Swipe?

If I use the system with the Handle, I'm limiting the system to be exclusively for Fury Swipe.
But if I set the Key = 1, then Fury Swipe doesn't work correctly...
Like I said before -> I recommend using the same local timer + hashtable method that my system uses in order to track the duration of fury swipes and the source/target/custom value (whatever information is needed). You can create local timers using Custom script and store them in a hashtable using their handle id as the Parent key. Then save the other data to this handle id at the different Child keys.
  • Custom script: set udg_Timer = CreateTimer()
  • ^ now save that to the Fury Swipes Hashtable

Since these Timers would be specific to the Fury Swipes Hashtable it won't have any issues, as opposed to the Timer System which always uses the same Hashtable.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
I believe I came up with a better GUI timer system that should fix some of the MUI problems we were having before. I'm calling it GST, GUI Simple Timers.

You now have the option to use the GST_KeyA and GST_KeyB variables to define the ID of your GST timers. These two Keys are linked to your Timer via Hashtable and will Destroy it whenever you create a new Timer that uses the same Keys. It reproduces the effects of Starting a One-shot timer over and over again, in other words, it Restarts the timer. So for example with Fury Swipes, you could set KeyA to Ursa's custom value and KeyB to the target's custom value. KeyA is the PARENT in the Hashtable and KeyB is the CHILD. The flaw here is that Ursa now can't use these two Keys in combination for other effects without causing a conflict, but that issue can be designed around.

If you don't Set the Keys then the system will create a new Timer and not worry about destroying any existing ones.

I also changed the design to be more GUI friendly by adding the GST_Trigger variable which you set before calling your GST_ Custom Script function. This should be set to the Trigger that you want to run when your newly created Timer expires. For example:
  • Set Variable GST_Trigger = Fireball Expire
  • Custom script: call GST_UnitReal(udg_Caster, udg_Damage, false, 5.00)
  • -------- --------
  • // This stores a UNIT and a REAL to a One-shot timer that will expire in 5.00 seconds, running the Fireball Expire trigger once it does
  • // You can then reference GST_Unit1 and GST_Real1 to get your stored values in that trigger
 

Attachments

  • GUI Simple Timers 2.w3m
    24.5 KB · Views: 12
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Hey Uncle, sorry for not replying sooner, took a bit of a break.
I played around with the system today, but struggle using it in this example though.

Below I use both the GST_IntInt AND the GST_Unit, because when a timer expires I will need
a) Custom Value of Target
b) Custom Value of Caster
c) The Target unit

...but I think this messes up the system, because by the time the trigger reaches the GST_Unit1, its variables have already been cleared (could this be?). This leaves the buff indefinitely on the target and doesn't seem to reset the amount of stacks. In my mind I think I need something like "GST_UnitIntInt" --- but maybe there is a workaround.

Could you please check / provide guidance how I should handle this please?

  • Fury Swipes Damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (Level of Fury Swipes for DamageEventSource) Greater than 0
      • (DamageEventTarget is A structure) Equal to False
      • DamageEventAttackT Equal to ATTACK_TYPE_HERO
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • Set VariableSet FurySwipes_Level = (Level of Fury Swipes for DamageEventSource)
      • Set VariableSet FurySwipes_CV_Caster = (Custom value of DamageEventSource)
      • Set VariableSet FurySwipes_CV_Target = (Custom value of DamageEventTarget)
      • -------- --------
      • -------- Increase Stacks by 1: --------
      • Set VariableSet FurySwipes_Stacks = ((Load FurySwipes_CV_Caster of FurySwipes_CV_Target from FurySwipes_Hashtable.) + 1)
      • Hashtable - Save FurySwipes_Stacks as FurySwipes_CV_Caster of FurySwipes_CV_Target in FurySwipes_Hashtable.
      • -------- --------
      • -------- Track a new instance of Fury Swipes on the target (it's important that the Child key is -1 so it doesn't interfere with Stacks): --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FurySwipes_Stacks Equal to 1
        • Then - Actions
          • Unit - Add Fury Swipes (Aura) to DamageEventTarget
          • Hashtable - Save ((Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.) + 1) as -1 of FurySwipes_CV_Target in FurySwipes_Hashtable.
          • Game - Display to (All players) the text: ON
        • Else - Actions
      • -------- --------
      • -------- Increase damage dealt: --------
      • Set VariableSet DamageEventAmount = (DamageEventAmount + ((Real(FurySwipes_Stacks)) x (10.00 x (Real(FurySwipes_Level)))))
      • Custom script: set udg_Key_FurySwipes = GetHandleId(udg_DamageEventTarget)
      • -------- --------
      • -------- Start/restart the timer that will reset Stacks when it expires --------
      • Set VariableSet GST_Trigger = Fury Swipes Expire <gen>
      • Custom script: call GST_IntInt(udg_FurySwipes_CV_Caster, udg_FurySwipes_CV_Target, false, 10.00)
      • Custom script: call GST_Unit(udg_DamageEventTarget, false, 10.00)
  • Fury Swipes Expire
    • Events
    • Conditions
    • Actions
      • -------- Variables available to use in this trigger: --------
      • -------- GST_Integer1 --------
      • -------- GST_Integer2 --------
      • Game - Display to (All players) for 30.00 seconds the text: (String(GST_Integer1))
      • Game - Display to (All players) for 30.00 seconds the text: (String(GST_Integer2))
      • Game - Display to (All players) for 30.00 seconds the text: (Name of GST_Unit1)
      • -------- --------
      • -------- --------
      • -------- Set Stacks to 0: --------
      • Hashtable - Save 0 as TS_Integer of FurySwipes_CV_Target in FurySwipes_Hashtable.
      • -------- --------
      • -------- Subtract 1 from Fury Swipe Source count (how many separate sources of Fury Swipes exist on this unit): --------
      • Hashtable - Save ((Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.) - 1) as -1 of FurySwipes_CV_Target in FurySwipes_Hashtable.
      • Game - Display to (All players) the text: (String((Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.)))
      • -------- --------
      • -------- Remove buff ability if the unit isn't affected by ANY sources of Fury Swipe: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Or - Any (Conditions) are true
            • Conditions
              • (Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.) Equal to 0
              • (GST_Unit1 is alive) Equal to False
        • Then - Actions
          • Unit - Remove Fury Swipes (Aura) from GST_Unit1
          • Unit - Remove Fury Swipes buff from GST_Unit1
          • Game - Display to (All players) the text: OFF
        • Else - Actions
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Don't call two Timer functions, simply use the GST_UnitUnit() function and store the Caster / Target. You'll have access to their custom values as well.

If the system is ever missing a function you need, it takes about 2 minutes to add your own version. It's extremely basic, you'd just copy and paste an existing function (try to use one that already shares some of your desired parameters) and work from there. The design pattern isn't too hard to follow as it's just using Hashtables but in Jass form.
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
I adjusted the trigger based on the feedback Uncle. But the system seems to run a separate one-shot timer whenever an attack happens. If I understand the above correctly however, this shouldn't happen. Instead, on every attack the timer should restart, as GST_KeyA (=Custom Value CASTER) and GST_KeyB (=Custom Value TARGET) are the same on every attack?

You now have the option to use the GST_KeyA and GST_KeyB variables to define the ID of your GST timers. These two Keys are linked to your Timer via Hashtable and will Destroy it whenever you create a new Timer that uses the same Keys. It reproduces the effects of Starting a One-shot timer over and over again, in other words, it Restarts the timer.

Is this working as intended or am I doing something wrong?

@GIMLI_2: Incinerate is unfortunately bugged, as you can only use an adjusted base ability. If you copy it and create a separate Incinerate, it won't work. Another disadvantage is that you have a lot less control for synergies with crit, lifesteal etc, as Incinerate counts as Orb effect. So I would rather have a triggered Incinerate. There is one in JASS in the spell section, but if I remember correctly, it doesn't work anymore because the damage engine on which it is based on is too old.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
I adjusted the trigger based on the feedback Uncle. But the system seems to run a separate one-shot timer whenever an attack happens. If I understand the above correctly however, this shouldn't happen. Instead, on every attack the timer should restart, as GST_KeyA (=Custom Value CASTER) and GST_KeyB (=Custom Value TARGET) are the same on every attack?



Is this working as intended or am I doing something wrong?

@GIMLI_2: Incinerate is unfortunately bugged, as you can only use an adjusted base ability. If you copy it and create a separate Incinerate, it won't work. Another disadvantage is that you have a lot less control for synergies with crit, lifesteal etc, as Incinerate counts as Orb effect. So I would rather have a triggered Incinerate. There is one in JASS in the spell section, but if I remember correctly, it doesn't work anymore because the damage engine on which it is based on is too old.
You forgot to set KeyA and KeyB:
  • Fury Swipes Damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (Level of Fury Swipes for DamageEventSource) Greater than 0
      • (DamageEventTarget is A structure) Equal to False
      • DamageEventAttackT Equal to ATTACK_TYPE_HERO
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • Set VariableSet FurySwipes_Level = (Level of Fury Swipes for DamageEventSource)
      • Set VariableSet FurySwipes_CV_Caster = (Custom value of DamageEventSource)
      • Set VariableSet FurySwipes_CV_Target = (Custom value of DamageEventTarget)
      • -------- --------
      • -------- Increase Stacks by 1: --------
      • Set VariableSet FurySwipes_Stacks = ((Load FurySwipes_CV_Caster of FurySwipes_CV_Target from FurySwipes_Hashtable.) + 1)
      • Hashtable - Save FurySwipes_Stacks as FurySwipes_CV_Caster of FurySwipes_CV_Target in FurySwipes_Hashtable.
      • -------- --------
      • -------- Track a new instance of Fury Swipes on the target (it's important that the Child key is -1 so it doesn't interfere with Stacks): --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FurySwipes_Stacks Equal to 1
        • Then - Actions
          • Unit - Add Fury Swipes (Aura) to DamageEventTarget
          • Hashtable - Save ((Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.) + 1) as -1 of FurySwipes_CV_Target in FurySwipes_Hashtable.
          • Game - Display to (All players) the text: ON
        • Else - Actions
      • -------- --------
      • -------- Increase damage dealt: --------
      • Set VariableSet DamageEventAmount = (DamageEventAmount + ((Real(FurySwipes_Stacks)) x (10.00 x (Real(FurySwipes_Level)))))
      • -------- --------
      • -------- Start/restart the timer that will reset Stacks when it expires --------
      • Set VariableSet GST_Trigger = Fury Swipes Expire <gen>
      • Set VariableSet GST_KeyA = FurySwipes_CV_Caster
      • Set VariableSet GST_KeyB = FurySwipes_CV_Target
      • Custom script: call GST_UnitUnit(udg_DamageEventSource, udg_DamageEventTarget, false, 10.00)
The Keys act as the Parent/Child hashtable indices of the newly started Timer. When the Keys have been Set it tells the system to save the Timer inside of a Hashtable which is how I'm able to "restart" it. In reality I'm not actually restarting the Timer, I'm simply destroying the previous Timer if one exists and replacing it with a new one.

By not setting any Keys you're telling the system to not save the Timer because you don't want to restart it.
In other words, if you want to create a Timer that can restart -> use Keys. Otherwise, don't use Keys.

Keep in mind the limitation here due to the nature of the Parent/Child design. For instance, if Ursa had access to another ability which used this timer system as well as this same setup for KeyA you could very easily run into a conflict that caused the two Timers to clash.

Also, the Fury Swipes Expire trigger doesn't need to check if GST_Unit2 is Alive when Removing the buff/ability. All buffs and non-permanent abilities are removed from a unit when it dies.


EDIT: I attached a map with a new version of the system that adds a variable, GST_RestartID, which is used to prevent the clashing issue I described before. You need to Set this variable in addition to KeyA and KeyB when making restartable timers and it must have a UNIQUE value >= 0 that isn't being used by another GST Timer. So if you had 3 different triggers that were taking advantage of this system, each with their own restartable timers, you'd need to Set their GST_RestartID's to let's say 0, 1, and 2 for example. So you're basically numbering each restartable timer. Again, non-restartable GST timers don't need to use Keys/RestartID.
 

Attachments

  • GST New 1.w3m
    27.4 KB · Views: 8
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Oh my god! It works now!:grin: (even with 2 Ursas attacking the same enemy!)

But a question: Is it wrong that I use the GST_RestartID=0 here?
My understanding is that the GST_RestartID allows the SAME caster to have for example 3 restartable GST timers on the SAME enemy. So essentially it allows me to differentiate between Timers that would use the same Custom_Value_Caster AND Custom_Value_Target - is that correct?
Thus, I shouldn't need to use the restart for Fury Swipes. But at the same time it doesn't "damage" anything.

And btw: Don't you want to upload this system to the spells section? Maybe this can help someone else.

  • Fury Swipes Damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (Level of Fury Swipes for DamageEventSource) Greater than 0
      • (DamageEventTarget is A structure) Equal to False
      • DamageEventAttackT Equal to ATTACK_TYPE_HERO
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • Set VariableSet FurySwipes_Level = (Level of Fury Swipes for DamageEventSource)
      • Set VariableSet FurySwipes_CV_Caster = (Custom value of DamageEventSource)
      • Set VariableSet FurySwipes_CV_Target = (Custom value of DamageEventTarget)
      • -------- --------
      • -------- Increase Stacks by 1: --------
      • Set VariableSet FurySwipes_Stacks = ((Load FurySwipes_CV_Caster of FurySwipes_CV_Target from FurySwipes_Hashtable.) + 1)
      • Hashtable - Save FurySwipes_Stacks as FurySwipes_CV_Caster of FurySwipes_CV_Target in FurySwipes_Hashtable.
      • -------- --------
      • -------- Track a new instance of Fury Swipes on the target (it's important that the Child key is -1 so it doesn't interfere with Stacks): --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FurySwipes_Stacks Equal to 1
        • Then - Actions
          • Unit - Add Fury Swipes (Aura) to DamageEventTarget
          • Hashtable - Save ((Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.) + 1) as -1 of FurySwipes_CV_Target in FurySwipes_Hashtable.
          • Game - Display to (All players) the text: ON
        • Else - Actions
      • -------- --------
      • -------- Increase damage dealt: --------
      • Set VariableSet DamageEventAmount = (DamageEventAmount + ((Real(FurySwipes_Stacks)) x (10.00 x (Real(FurySwipes_Level)))))
      • -------- --------
      • -------- Start/restart the timer that will reset Stacks when it expires --------
      • Set VariableSet GST_Trigger = Fury Swipes Expire <gen>
      • Set VariableSet GST_KeyA = FurySwipes_CV_Caster
      • Set VariableSet GST_KeyB = FurySwipes_CV_Target
      • Set VariableSet GST_RestartID = 0
      • Custom script: call GST_UnitUnit(udg_DamageEventSource, udg_DamageEventTarget, false, 10.00)
I could write one for you in vJass if it helps.
Thanks a lot for offering! But I will stick to the GUI version provided by Uncle. I don't really feel comfortable with JASS, and with GUI I can at least adjust the system to some degree if needed.
 

Attachments

  • Land of Legends Test Map v0.0.01.w3m
    1.1 MB · Views: 16

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
To answer your question, you're correct and it's smart to use 0 there.

And I don't see this as finished yet so maybe once I'm happy with it I'll submit it. The thing is I don't really know how to write optimal Jass since I primarily use Lua.

EDIT: But forget all of that. I attached a new version that automates RestartID removing the need for it.
I also renamed a bunch of things (sorry). I renamed the KeyA and KeyB variables to GST_ParentKey and GST_ChildKey as it makes more sense.
I also renamed the GST_Clear() function to GST_DestroyTimer().

The rules are as follows:
For restartable timers, GST_ParentKey must have a value >= 0 and < 100,000. GST_ChildKey can have any value that fits in a Hashtable.

Regarding your Fury Swipes trigger, you simply have to go back to what you were doing before I added RestartID.
GST_ParentKey will be Ursa's custom value and GST_ChildKey will be the enemy unit's custom value.
Also, since I renamed some variables I recommend wiping all traces of the system before importing this new version.
 

Attachments

  • GST New 2.w3m
    27.7 KB · Views: 13
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Thanks for all the optimizations, works perfectly now and it's so easy to use. Will mark this as solved:thumbs_up:

Here the final trigger for anyone needing this in the future:

  • Fury Swipes Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set VariableSet FurySwipes_Hashtable = (Last created hashtable)
  • Fury Swipes Damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (Level of Fury Swipes for DamageEventSource) Greater than 0
      • (DamageEventTarget is A structure) Equal to False
      • DamageEventAttackT Equal to ATTACK_TYPE_HERO
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • Set VariableSet FurySwipes_Level = (Level of Fury Swipes for DamageEventSource)
      • Set VariableSet FurySwipes_CV_Caster = (Custom value of DamageEventSource)
      • Set VariableSet FurySwipes_CV_Target = (Custom value of DamageEventTarget)
      • -------- --------
      • -------- Increase Stacks by 1: --------
      • Set VariableSet FurySwipes_Stacks = ((Load FurySwipes_CV_Caster of FurySwipes_CV_Target from FurySwipes_Hashtable.) + 1)
      • Hashtable - Save FurySwipes_Stacks as FurySwipes_CV_Caster of FurySwipes_CV_Target in FurySwipes_Hashtable.
      • -------- --------
      • -------- Track a new instance of Fury Swipes on the target (it's important that the Child key is -1 so it doesn't interfere with Stacks): --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FurySwipes_Stacks Equal to 1
        • Then - Actions
          • Unit - Add Fury Swipes (Aura) to DamageEventTarget
          • Hashtable - Save ((Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.) + 1) as -1 of FurySwipes_CV_Target in FurySwipes_Hashtable.
          • Game - Display to (All players) the text: ON
        • Else - Actions
      • -------- --------
      • -------- Increase damage dealt: --------
      • Set VariableSet DamageEventAmount = (DamageEventAmount + ((Real(FurySwipes_Stacks)) x (10.00 x (Real(FurySwipes_Level)))))
      • -------- --------
      • -------- Start/restart the timer that will reset Stacks when it expires --------
      • Set VariableSet GST_Trigger = Fury Swipes Expire <gen>
      • Set VariableSet GST_ParentKey = FurySwipes_CV_Caster
      • Set VariableSet GST_ChildKey = FurySwipes_CV_Target
      • Custom script: call GST_UnitUnit(udg_DamageEventSource, udg_DamageEventTarget, false, 10.00)
  • Fury Swipes Expire
    • Events
    • Conditions
    • Actions
      • -------- Variables available to use in this trigger: --------
      • -------- GST_Integer1 --------
      • -------- GST_Integer2 --------
      • Game - Display to (All players) for 30.00 seconds the text: (Name of GST_Unit1)
      • Game - Display to (All players) for 30.00 seconds the text: (Name of GST_Unit2)
      • -------- --------
      • Set VariableSet FurySwipes_CV_Caster = (Custom value of GST_Unit1)
      • Set VariableSet FurySwipes_CV_Target = (Custom value of GST_Unit2)
      • -------- --------
      • -------- Set Stacks to 0: --------
      • Hashtable - Save 0 as FurySwipes_CV_Caster of FurySwipes_CV_Target in FurySwipes_Hashtable.
      • -------- --------
      • -------- Subtract 1 from Fury Swipe Source count (how many separate sources of Fury Swipes exist on this unit): --------
      • Hashtable - Save ((Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.) - 1) as -1 of FurySwipes_CV_Target in FurySwipes_Hashtable.
      • Game - Display to (All players) the text: (String((Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.)))
      • -------- --------
      • -------- Remove buff ability if the unit isn't affected by ANY sources of Fury Swipe: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Load -1 of FurySwipes_CV_Target from FurySwipes_Hashtable.) Equal to 0
        • Then - Actions
          • Unit - Remove Fury Swipes (Aura) from GST_Unit2
          • Unit - Remove Fury Swipes buff from GST_Unit2
          • Game - Display to (All players) the text: OFF
        • Else - Actions
 
Status
Not open for further replies.
Top