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

[Spell] Warpath "Each stack has its own duration"

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

I want to trigger Dota's Warpath as described here: Bristleback

Bristleback works himself up into a fury every time he casts a spell, increasing his movement speed and damage.
Each stack has its own duration, successive spell casts do not refresh the duration of previous stacks.

I was brainstorming how to implement this, but I struggle finding a solution on how to tie the individual stacks (which I guess should have an index) back to the correct caster once a stack expires...

Would anyone have a good idea?

Thanks.
 
Level 12
Joined
May 16, 2020
Messages
660
Use Dynamic Indexing, and tie each stack to one instance. It does not matter if a unit reuses the ability multiple times since they are considered separate stacks with Dynamic Indexing.

Yeah that's what I was thinking too (basically Warpath_Stack[Warpath_Index]), BUT how do I then track to which caster the stacks belong, if several caster on the map have the ability?

I need to somehow 'link' the Stacks to the correct respective Caster, to reduce the Attack/Movement speed when a stack expires.
 
Level 23
Joined
Apr 3, 2018
Messages
460
you can make a unit array storing the caster corresponding to each instance

or use vjass structs to easier keep track of different variables (caster, duration, level) as part of an "object" for every stack, which you can then put in a single array
 
Last edited:
Yeah that's what I was thinking too (basically Warpath_Stack[Warpath_Index]), BUT how do I then track to which caster the stacks belong, if several caster on the map have the ability?

I need to somehow 'link' the Stacks to the correct respective Caster, to reduce the Attack/Movement speed when a stack expires.
You can track the owner of the caster for each instance. With careful calculation, each stack can provide a bonus independently of another stack, thus when one stack is removed, it only removes that stack.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,517
Trigger - On Cast:
  • Set Var Warpath_Index = Warpath_Index + 1
  • Set Var Warpath_Caster[Warpath_Index] = Triggering Unit
  • Set Var Warpath_Duration[Warpath_Index] = 10.00
  • Add stack to Warpath_Caster[Warpath_Index]
  • Turn on Loop trigger if it's off

Trigger - Loop:
  • Every 0.05 seconds -> For each Integer A from 1 to Warpath_Index do:
  • Set Var Warpath_Duration[A] = Warpath_Duration[A] - 0.05
  • If (Warpath_Duration[A] <= 0.01) then -> Remove stack from Warpath_Caster[A] and De-Index it

Then to handle a stack limit, which in Warpath's case removes the oldest stack of Warpath when applying a new stack while at maximum stacks, you could use Unit Indexing:

Trigger - On Cast:
  • // do this after setting all of the other variables from my previous On Cast example
  • Set Var cv = Custom value of Warpath_Caster[Warpath_Index]
  • Set Var abilLevel = Level of Warpath for Warpath_Caster[Warpath_Index]
  • If (Warpath_Stacks[cv] < Warpath_Limit[abilLevel]) then:
  • Set Var Warpath_Stacks[cv] = Warpath_Stacks[cv] + 1
  • ELSE:
  • For each Integer A from 1 to Warpath_Index do:
  • If (Warpath_Caster[Warpath_Index] Equal to Warpath_Caster[A]) then -> Remove stack from Warpath_Caster[A], De-Index it, and BREAK from the Loop so it doesn't remove any other stacks

Edit: Don't use IntegerA in your Loop, use a unique Integer variable like Warpath_Loop.

"De-Indexing" is the process in which you swap the current Loop's variables with the end of the Loop's variables and then push the Loop back 1 cycle:
Set Warpath_Caster[A] = Warpath_Caster[Warpath_Index]
Set Warpath_Duration[A] = Warpath_Duration[Warpath_Index]
Set Var Warpath_Index = Warpath_Index - 1
Set Var A = A - 1

Something like that should work.
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Thank you all! I followed Uncle's advice above, but have some troubles:

1) The Movement Speed adjustment is not working correctly; I'm using Endurance Aura to both show the current stack as buff, and adjust the movement speed. But the new natives (which worked on all my spells so far) do not adjust the speed.

2) This part isn't 100% clear to me how to implement:
If (Warpath_Caster[Warpath_Index] Equal to Warpath_Caster[A]) then -> Remove stack from Warpath_Caster[A], De-Index it, and BREAK from the Loop so it doesn't remove any other stacks

3) When a stack runs out, the attack damage is not being reduced correctly. I'm using Chopinski's New Bonus [vJASS][LUA] to provide the attack damage bonus. Can JASS formulas use multiplications correctly with '*'? Maybe that's why the damage is not reduced?

Here the triggers:

  • Warpath Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet GA_Config_Ability[0] = Warpath 0
      • Set VariableSet GA_Config_Ability[1] = Warpath 1
      • Set VariableSet GA_Config_Ability[2] = Warpath 2
      • Set VariableSet GA_Config_Ability[3] = Warpath 3
      • Set VariableSet GA_Config_Ability[4] = Warpath 4
      • Set VariableSet GA_Config_Ability[5] = Warpath 5
      • Set VariableSet GA_Config_Ability[6] = Warpath 6
      • Set VariableSet GA_Config_Ability[7] = Warpath 7
      • Set VariableSet GA_Config_Ability[8] = Warpath 8
      • Set VariableSet GA_Config_Ability[9] = Warpath 9
      • -------- --------
      • Set VariableSet GA_Config_Buff[0] = Warpath: 0
      • Set VariableSet GA_Config_Buff[1] = Warpath: 1
      • Set VariableSet GA_Config_Buff[2] = Warpath: 2
      • Set VariableSet GA_Config_Buff[3] = Warpath: 3
      • Set VariableSet GA_Config_Buff[4] = Warpath: 4
      • Set VariableSet GA_Config_Buff[5] = Warpath: 5
      • Set VariableSet GA_Config_Buff[6] = Warpath: 6
      • Set VariableSet GA_Config_Buff[7] = Warpath: 7
      • Set VariableSet GA_Config_Buff[8] = Warpath: 8
      • Set VariableSet GA_Config_Buff[9] = Warpath: 9
      • -------- --------
      • Set VariableSet GA_Config_StacksMax[1] = 5
      • Set VariableSet GA_Config_StacksMax[2] = 7
      • Set VariableSet GA_Config_StacksMax[3] = 9
      • -------- --------
      • Set VariableSet GA_Config_Movement[1] = 0.03
      • Set VariableSet GA_Config_Movement[2] = 0.04
      • Set VariableSet GA_Config_Movement[3] = 0.05
      • -------- --------
      • Set VariableSet GA_Config_Damage[1] = 25
      • Set VariableSet GA_Config_Damage[2] = 30
      • Set VariableSet GA_Config_Damage[3] = 35
  • Warpath
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • And - All (Conditions) are true
        • Conditions
          • (Level of Warpath for (Triggering unit)) Greater than 0
          • Or - Any (Conditions) are true
            • Conditions
              • (Ability being cast) Equal to Quill Spray
              • (Ability being cast) Equal to Viscous Nasal Goo
    • Actions
      • Set VariableSet GA_Index = (GA_Index + 1)
      • Set VariableSet GA_Caster[GA_Index] = (Triggering unit)
      • Set VariableSet GA_StackDuration[GA_Index] = 28
      • -------- --------
      • Set VariableSet GA_CV = (Custom value of GA_Caster[GA_Index])
      • Set VariableSet GA_Level[GA_Index] = (Level of Warpath for GA_Caster[GA_Index])
      • -------- Level will probably need an array, in case the Hero levels Warpath while having stats --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • GA_Stack[GA_CV] Less than GA_Config_StacksMax[GA_Level[GA_Index]]
        • Then - Actions
          • Unit - Remove GA_Config_Ability[GA_Stack[GA_CV]] from GA_Caster[GA_Index]
          • Unit - Remove GA_Config_Buff[GA_Stack[GA_CV]] buff from GA_Caster[GA_Index]
          • Set VariableSet GA_Stack[GA_CV] = (GA_Stack[GA_CV] + 1)
          • -------- --------
          • Unit - Add GA_Config_Ability[GA_Stack[GA_CV]] to GA_Caster[GA_Index]
          • Ability - Set Ability: (Unit: GA_Caster[GA_Index]'s Ability with Ability Code: GA_Config_Ability[GA_Stack[GA_CV]])'s Real Level Field: Movement Speed Increase (%) ('Oae1') of Level: (GA_Level[GA_Index] - 1) to ((Real(GA_Stack[GA_CV])) x GA_Config_Movement[GA_Level[GA_Index]])
          • Custom script: call AddUnitBonus(udg_GA_Caster[udg_GA_Index], BONUS_DAMAGE, udg_GA_Config_Damage[udg_GA_Level[udg_GA_Index]]*udg_GA_Stack[udg_GA_CV])
          • -------- --------
          • Unit - For GA_Caster[GA_Index], Ability GA_Config_Ability[GA_Stack[GA_CV]], Disable ability: True, Hide UI: False
          • Unit - For GA_Caster[GA_Index], Ability GA_Config_Ability[GA_Stack[GA_CV]], Disable ability: False, Hide UI: False
        • Else - Actions
          • For each (Integer GA_Integer) from 1 to GA_Index, do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • GA_Caster[GA_Index] Equal to GA_Caster[GA_Integer]
                • Then - Actions
                  • -------- ??? --------
                • Else - Actions
      • -------- --------
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • GA_Index Equal to 1
        • Then - Actions
          • Countdown Timer - Start GA_Timer as a Repeating timer that will expire in 0.50 seconds
          • Trigger - Turn on Warpath Loop <gen>
        • Else - Actions
  • Warpath Loop
    • Events
      • Time - GA_Timer expires
    • Conditions
    • Actions
      • For each (Integer GA_Integer) from 1 to GA_Index, do (Actions)
        • Loop - Actions
          • Set VariableSet GA_StackDuration[GA_Integer] = (GA_StackDuration[GA_Integer] - 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • GA_StackDuration[GA_Integer] Equal to 0
            • Then - Actions
              • Set VariableSet GA_CV = (Custom value of GA_Caster[GA_Integer])
              • Set VariableSet GA_Level[GA_Integer] = (Level of Warpath for GA_Caster[GA_Integer])
              • -------- --------
              • Unit - Remove GA_Config_Ability[GA_Stack[GA_CV]] from GA_Caster[GA_Integer]
              • Unit - Remove GA_Config_Buff[GA_Stack[GA_CV]] buff from GA_Caster[GA_Integer]
              • Set VariableSet GA_Stack[GA_CV] = (GA_Stack[GA_CV] - 1)
              • -------- --------
              • Unit - Add GA_Config_Ability[GA_Stack[GA_CV]] to GA_Caster[GA_Integer]
              • Ability - Set Ability: (Unit: GA_Caster[GA_Integer]'s Ability with Ability Code: GA_Config_Ability[GA_Stack[GA_CV]])'s Real Level Field: Movement Speed Increase (%) ('Oae1') of Level: (GA_Level[GA_Integer] - 1) to ((Real(GA_Stack[GA_CV])) x GA_Config_Movement[GA_Level[GA_Integer]])
              • Custom script: call AddUnitBonus(udg_GA_Caster[udg_GA_Integer], BONUS_DAMAGE, -(udg_GA_Config_Damage[udg_GA_Level[udg_GA_Integer]]*udg_GA_Stack[udg_GA_CV]))
              • -------- --------
              • Unit - For GA_Caster[GA_Index], Ability GA_Config_Ability[GA_Stack[GA_CV]], Disable ability: True, Hide UI: False
              • Unit - For GA_Caster[GA_Index], Ability GA_Config_Ability[GA_Stack[GA_CV]], Disable ability: False, Hide UI: False
              • -------- --------
              • -------- --------
              • Set VariableSet GA_Caster[GA_Integer] = GA_Caster[GA_Index]
              • Set VariableSet GA_StackDuration[GA_Integer] = GA_StackDuration[GA_Index]
              • Set VariableSet GA_Level[GA_Integer] = GA_Level[GA_Index]
              • -------- --------
              • Set VariableSet GA_Index = (GA_Index - 1)
              • Set VariableSet GA_Integer = (GA_Integer - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • GA_Index Equal to 0
                • Then - Actions
                  • Game - Display to (All players) the text: Warpath OFF
                  • Countdown Timer - Pause GA_Timer
                  • Trigger - Turn off (This trigger)
                • Else - Actions
            • Else - Actions
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,517
1) You're referencing the wrong level of the ability most likely. Pretty sure this should be set to 0:
  • Ability - Set Ability: ... of Level: (GA_Level[GA_Integer] - 1)
GA_Level[GA_Integer] will be some value between 1 and 3 depending on the level of War Path at the time of creating the stack. However, the ability itself will always be level 1.

Also, if Disabling/Enabling the ability doesn't "refresh" it, you should Increase/Decrease the level of the ability as well.

2) You would remove (De-Index) the stack similar to how it's done in the Loop when the duration runs out:
  • If - Conditions
  • GA_StackDuration[GA_Integer] Equal to 0
  • // de-index the stack
Obviously you don't need to check if GA_Index is Equal to 0 at the end because you know you're at maximum stacks in this case.

3) Again, probably has to do with the Levels.

Also, you need to add/remove/edit the Abilities regardless of whether Stacks is at max or not.
The only thing you're putting in that If Then Else statement when Stacks < Max is this line:
  • Set VariableSet GA_Stack[GA_CV] = (GA_Stack[GA_CV] + 1)
Edit: So I figured I'd try throwing together the spell myself in GUI and here's what I came up with. I attached the map below, I THINK it works...

This type of spell would be a lot easier in Lua/Jass.
 

Attachments

  • Warpath Example 1.w3m
    23.2 KB · Views: 20
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Thank you Uncle! Your test map seems to work perfectly, will try in my map now. But questions:

1) Did you add the "increase and decrease" of the ability only to instantly update the current MS stack? It seems to work just with Disabling/Enabling, so I guess I can give the dummy abilities just 1 level?
2) In the "Timer" you added the "SPECIAL CASE" if the duration is -1. What exactly is this for...?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,517
Thank you Uncle! Your test map seems to work perfectly, will try in my map now. But questions:

1) Did you add the "increase and decrease" of the ability only to instantly update the current MS stack? It seems to work just with Disabling/Enabling, so I guess I can give the dummy abilities just 1 level?
2) In the "Timer" you added the "SPECIAL CASE" if the duration is -1. What exactly is this for...?
1) That's how I always "refreshed" abilities. Whether it's needed or not after Disabling/Enabling is another story. Try it without and see what happens.

2) I designed it so that whenever you gain a stack while at maximum stacks it finds the oldest stack associated with the caster and sets it's Duration to 0 (-1 once the Loop subtracts 1 from it). Then the Loop checks for -1 Duration stacks and removes them in a special way. I was having issues getting the De-Indexing thing to work inside of the Cast trigger and this method of "marking them for removal" seemed to do the trick.

That being said, I didn't test it enough to truly confirm whether or not it's working properly so keep an eye on it.
 
Level 12
Joined
May 16, 2020
Messages
660
Quick update:
1) That's how I always "refreshed" abilities. Whether it's needed or not after Disabling/Enabling is another story. Try it without and see what happens.
For the Damage Bonus ability, In- and Decreasing the ability seems to be the only way to instantly adjust the values. But for auras, Disabling/Enabling alone works.

From the test so far this seems to work properly :-D
Thank you!
 
Status
Not open for further replies.
Top