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

[Spell] Slow spell that increases Speed Help

Level 6
Joined
May 13, 2023
Messages
44
Hi hive, I have this simple Spell that slows target enemy unit and when an ally attacks the target it gets a speed bonus and it works but 1 small thing is I don't know how to set the level of the Speed bonus ability
Heres the code
Initialization Trigger
  • Divine Deceleration Init
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Divine Deceleration
    • Actions
      • Set DivineDecelerationINDEX = (DivineDecelerationINDEX + 1)
      • Set DivineDecelerationCaster[DivineDecelerationINDEX] = (Triggering unit)
      • Set DivineDecelerationTarget[DivineDecelerationINDEX] = (Target unit of ability being cast)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DivineDecelerationINDEX Equal to 1
        • Then - Actions
          • Trigger - Turn on Divine Deceleration End <gen>
          • Trigger - Turn on Divine Deceleration Bonus <gen>
        • Else - Actions
Condition Trigger
  • Divine Deceleration End
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • For each (Integer DivineDecelerationLOOP) from 1 to DivineDecelerationINDEX, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (DivineDecelerationTarget[DivineDecelerationLOOP] has buff Divine Deceleration ) Equal to True
            • Then - Actions
            • Else - Actions
              • Set DivineDecelerationCaster[DivineDecelerationLOOP] = DivineDecelerationCaster[DivineDecelerationINDEX]
              • Set DivineDecelerationTarget[DivineDecelerationLOOP] = DivineDecelerationTarget[DivineDecelerationINDEX]
              • Set DivineDecelerationINDEX = (DivineDecelerationINDEX - 1)
              • Set DivineDecelerationLOOP = (DivineDecelerationLOOP - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DivineDecelerationINDEX Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                  • Trigger - Turn off Divine Deceleration Bonus <gen>
                • Else - Actions
Buff Bonus Trigger
  • Divine Deceleration Bonus
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • ((Triggering unit) has buff Divine Deceleration ) Equal to True
    • Actions
      • Unit - Create 1 Dummy (Nothing) for (Owner of (Attacking unit)) at (Position of (Attacking unit)) facing Default building facing degrees
      • Unit - Add Divine Deceleration (Dummy) to (Last created unit)
      • Unit - Order (Last created unit) to Undead Necromancer - Cripple (Attacking unit)
      • Unit - Add a 6.00 second Generic expiration timer to (Last created unit)
 
Level 39
Joined
Feb 27, 2007
Messages
5,027
  • A Unit is Attacked is a terrible event you should never use for any reason; it happens before the attack is confirmed to occur or damage is dealt (you can trigger the event repeatedly by spam-right-clicking and ordering the unit to stop). Instead you will want to either detect attacks yourself manually with the new event Any Unit Takes Damage or adopt a complete system that does it all for you.

  • Avoid empty then-blocks; JASS doesn't like them and doesn't always play nice. Just invert your condition and put the actions inside the Then block.

  • 6 second expiration timer on the dummy is wholly unnecessary. It only needs to live long enough to successfully cast, and it's possible to make a dummy do so literally instantly without even having to turn. (Movement: none, speed base: 0, cast backswing: 0, cast point: 0)

  • Your Condition Trigger is entirely pointless and doesn't need to exist. Why does the Buff Bonus trigger even need to be turned on or off? (Its condition will cause it not to execute except when it should.) If for some reason there are other effects/triggers that do make this one necessary, 0.10 is faster frequency than you should ever need. It could be reduced to 0.5 or even 2.0 to match aura refresh rate and it wouldn't affect anything. Literally just delete this trigger and delete all the dynamic indexing you're doing because you don't use any of the indexed data for anything anyway.
As for casting at the correct level you just... set the level of the dummy's ability before casting:
  • Unit - Set level of Divine Deceleration (Dummy) for (Last created unit) to ___
You can easily determine which level of the ability the attacked unit was previously affected by in two different ways:
  1. Give each level of the ability a different unique buff. They can all look exactly the same as long as they have different rawcodes. I don't know exactly how WC3 buff hierarchy works but I believe higher levels should overwrite lower levels as expected. Check over all the buffs in a loop until you find a match. This will put more strain on the trigger that runs when units are attacked because it'll have to check N different buffs on each unit that is attacked for every attack.

  2. Create a unit group array where the index of the array corresponds to which level of the ability the unit was last affected by. When the spell is cast, remove the target from all groups DDGroup[1 ... N], then add it to DDGroup[level of cast]. There is no need to remove the units from these groups manually when the buffs run out, since it won't even check which group a unit is in unless it has the buff anyway; you can most efficiently just remove units from all groups DDGroup[1 ... N] on death.

    Loop over the group indices until you find the unit to be in one such group:
    • For each (Integer A) from 1 to MAX_LEVEL do (Actions)
      • Loop - Actions
        • If (All conditions are true) then do (Then actions) else do (Else actions)
          • If - Conditions
            • (TEST_UNIT is in DDGroup[(Integer A)]) equal to True
          • Then - Actions
            • Set LEVEL = (Integer A)
            • Custom script: exitwhen true //skip the rest of the loop
          • Else - Actions
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,578
I suggest most of what Pyro said but I think your spell is not suitable for Dynamic Indexing. Dynamic Indexing is intended for spells that can stack, which means multiple instances can exist at a time coming from any number of sources (same source included). It allows you to achieve things like a "stacking damage over time" effect.

In your case, you're relying on a single Buff that only ever applies one effect, so the instances that Dynamic Indexing provides are completely useless here. Your spell uses the standard non-stacking behavior that is common in Warcraft 3 -> the most recent application of the Buff overrides the previous Buff. For example, if you cast Howl of Terror (level 3) on a group of enemies, and then 2 seconds later cast Howl of Terror (level 1) on that same group of enemies, the lvl 3 buff will be removed and a fresh lvl 1 buff will be applied. It's not like the enemies can have two of the same buff, and the game isn't going to assume that you want the higher, more powerful buff to remain, so it simply discards the old Buff and applies the new one.

So with that in mind, some simple Unit Indexing will go a long way for a spell like this:
  • Divine Deceleration Init
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Divine Deceleration
    • Actions
      • Set Variable Divine_Decel_CV = (Custom value of (Target unit of ability being cast))
      • Set Variable Divine_Decel_Lvl[Divine_Decel_CV] = (Level of (Ability being cast) for (Triggering unit))
      • Unit Group - Add (Target unit of ability being cast) to Divine_Decel_Group
      • Trigger - Turn on Divine Deceleration End <gen>
      • Trigger - Turn on Divine Deceleration Bonus<gen>
  • Divine Deceleration End
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Divine_Decel_Group and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) has buff Divine Deceleration ) Equal to False
            • Then - Actions
              • Unit Group - Remove (Picked unit) from Divine_Decel_Group.
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Number of units in Divine_Decel_Group) Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                  • Trigger - Turn off Divine Deceleration Bonus <gen>
                • Else - Actions
            • Else - Actions
  • Divine Deceleration Bonus
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • ((Triggering unit) has buff Divine Deceleration ) Equal to True
      • ((Triggering unit) belongs to an enemy of (Owner of (Attacking unit)).) Equal to True
    • Actions
      • Set Variable Divine_Decel_CV = (Custom value of (Triggering unit))
      • Set Variable Divine_Decel_Point = (Position of (Attacking unit))
      • Unit - Create 1 Dummy (Nothing) for (Owner of (Attacking unit)) at Divine_Decel_Point facing Default building facing degrees
      • Custom script: call RemoveLocation( udg_Divine_Decel_Point )
      • Unit - Add Divine Deceleration (Dummy) to (Last created unit)
      • Unit - Set level of Divine Deceleration (Dummy) for (Last created unit) to Divine_Decel_Lvl[Divine_Decel_CV]
      • Unit - Add a 0.20 second Generic expiration timer to (Last created unit)
      • Unit - Order (Last created unit) to Undead Necromancer - Cripple (Attacking unit)
The Divine_Decel_Lvl variable will track the "buff level" of Divine Deceleration on each unit. This is done using the Unit Indexing method which gives each unit a unique custom value. This allows you to give each unit their own unique value inside of your Arrays.

Notes:
  • I think the Attacked event is fine for this application simply because of the nature of the ability. It's really up to you when you want to apply the buff or not, pre-damage or post-damage. In A LOT of cases you should rely on a Damage event instead!
  • The lack of variables makes this less efficient and raises potential risks, but in this case it seems fine.
  • I fixed your Memory Leak -> (Position of (Attacking unit)).
  • Order your Dummy to cast the spell as it's very last Action.
  • Your Dummy unit should not require a 6.00 second expiration timer. This tells me that you probably haven't set it up properly.
A proper Dummy unit can cast a spell instantly without needing to turn to face it's target. It can also cast any number of spells within the same game frame without issues, assuming those spells don't have Casting Time or some kind of Channeling delay built in.

How to create a proper Dummy unit:
Step 1) Copy and paste the Locust. (Undead - Special)
Step 2) Set it's Movement Type to None and Speed Base to 0. The Dummy can now cast spells instantly.
Step 3) Set it's Attacks Enabled to None, Model to an empty path or better yet use Vexorian's custom Dummy model, and set it's Shadow to None.

I don't care what any posts from 2007 say, THIS is how you do it. Unfortunately, a lot of maps are plagued by this mistake. The only exception is if your Dummy unit is being treated like a Missile via triggers. In that case you would need to adjust it's Movement settings in order for it to move around. But that's a simple fix, have one Dummy for casting spells and one Dummy for acting like a Missile. Note that Dummy missiles are obsolete now thanks to improvements to Special Effects, so you may not even need one.
 
Last edited:
Level 6
Joined
May 13, 2023
Messages
44
I fixed your Memory Leak -> (Position of (Attacking unit))
Thank you Uncle
I have few questions
1. Is it better to use variables when addressing point values instead of the GUI's position of unit or target point of ability being cast
2. Is it better to use variables on a trigger that doesn't need MUI like this
  • Blink Bolt
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Blink Bolt
    • Actions
      • Unit - Cause (Triggering unit) to damage (Target unit of ability being cast), dealing (3.00 x (Real((Strength of (Triggering unit) (Include bonuses))))) damage of attack type Spells and damage type Universal
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ((Target unit of ability being cast) is alive) Equal to True
        • Then - Actions
          • Unit - Move (Triggering unit) instantly to (Position of (Target unit of ability being cast))
        • Else - Actions
or should I make variables for the caster and target then do the action like this
  • Unit - Cause Caster to damage Target, dealing (3.00 x (Real((Strength of Caster (Include bonuses))))) damage of attack type Spells and damage type Universal
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,578
Thank you Uncle
I have few questions
1. Is it better to use variables when addressing point values instead of the GUI's position of unit or target point of ability being cast
2. Is it better to use variables on a trigger that doesn't need MUI like this
No problem.

1) There are these things called memory leaks which occur when dealing with Points, Unit Groups, Player Groups, and Special Effects. I recommend checking out this link. Long story short, the reason I used the Divine_Decel_Point variable was so that I could then use the RemoveLocation() custom script to get rid of it. That is how you clean up Point leaks.


2) That trigger is actually MUI by design, but I understand what you're asking.

It's more efficient to reference a variable than to call a function. So if you're calling a function two or more times in a trigger, like you are in your Blink Bolt example with (Triggering unit) and (Target unit of ability being cast), then it's objectively better to Set those to variables first and reference the variables instead. So ideally you would almost always use variables.

HOWEVER, let's not forget that creating a ton of variables will clog up your trigger editor and make things less organized. So you should try to balance when to use variables or not.

There's of course the option to use "temp" variables, which are generic variables that you intend to reuse throughout your triggers. In your Blink Bolt example these would be your Caster and Target variables, which you can use in other triggers that are similar to Blink Bolt.

Just make sure to use these types of variables wisely. You don't want to create conflicts between multiple triggers where one trigger overwrites the value of a variable that another trigger is using, causing unexpected results. This generally doesn't happen but it's possible if you're not careful.
 
Last edited:
Level 6
Joined
May 13, 2023
Messages
44
No problem.

1)
There are these things called memory leaks which occur when dealing with Points, Unit Groups, Player Groups, and Special Effects. I recommend checking out this link. Long story short, the reason I used the Divine_Decel_Point variable was so that I could then use the RemoveLocation() custom script to get rid of it. That is how you clean up Point leaks.

2)
That trigger is actually MUI by design, but I understand what you're asking.

It's more efficient to reference a variable than to call a function. So if you're calling a function two or more times in a trigger, like you are in your example with (Triggering unit) and (Target unit of ability being cast), then it's objectively better to Set those to variables first and reference the variables instead. So ideally you would almost always use variables.

HOWEVER, let's not forget that creating a ton of variables will clog up your trigger editor and make things less organized. So you should try to balance when to use variables or not.

There's of course the option to use "temp" variables, which are generic variables that you intend to reuse throughout your triggers. In your example these would be your Caster and Target variables, which you can use in other triggers that are similar to Blink Bolt. Just make sure to use them wisely so that you don't create conflicts between multiple triggers.
well thank you again
I did think using variables instead of functions would be better and just like you said i was struggling with many triggers requiring many variables, thats why i asked this hoping it wouldn't be too much of a problem to use functions instead of variables :thumbs_up:
 
Level 6
Joined
May 13, 2023
Messages
44
1 more thing I'd like to ask
Can you help me with this ability that I'm trying to make
Spell is simple it puts a link between caster and target and if 1 of them dies the other 1 replenishes it's hp and mana
I based dummy spell on Spirit Link
Because this spell detects unit has buff and I tried to do it with Unit Indexing but i think it also needs dynimac indexing aswell.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,578
1 more thing I'd like to ask
Can you help me with this ability that I'm trying to make
Spell is simple it puts a link between caster and target and if 1 of them dies the other 1 replenishes it's hp and mana
I based dummy spell on Spirit Link
Because this spell detects unit has buff and I tried to do it with Unit Indexing but i think it also needs dynimac indexing aswell.
It can be done with Unit Indexing almost exactly the same way as Divine Deceleration:
  • Spirit Link Init
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Spirit Link
    • Actions
      • Set Variable Spirit_Link_CV = (Custom value of (Triggering unit))
      • Set Variable Spirit_Link_Level[Spirit_Link_CV] = (Level of (Ability being cast) for (Triggering unit))
      • Set Variable Spirit_Link_Caster[Spirit_Link_CV] = (Triggering unit)
      • Set Variable Spirit_Link_Target[Spirit_Link_CV] = (Target unit of ability being cast)
      • -------- --------
      • Set Variable Spirit_Link_CV = (Custom value of (Target unit of ability being cast))
      • Set Variable Spirit_Link_Level[Spirit_Link_CV] = (Level of (Ability being cast) for (Triggering unit))
      • Set Variable Spirit_Link_Caster[Spirit_Link_CV] = (Triggering unit)
      • Set Variable Spirit_Link_Target[Spirit_Link_CV] = (Target unit of ability being cast)
      • -------- --------
      • Unit Group - Add (Triggering unit) to Spirit_Link_Group
      • Unit Group - Add (Target unit of ability being cast) to Spirit_Link_Group
      • Trigger - Turn on Spirit Link End <gen>
      • Trigger - Turn on Spirit Link Death <gen>
  • Spirit Link End
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Spirit_Link_Group and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) has buff Spirit Link ) Equal to False
            • Then - Actions
              • Set Variable Spirit_Link_CV = (Custom value of (Picked unit))
              • Set Variable Spirit_Link_Level[Spirit_Link_CV] = 0
              • -------- --------
              • Unit Group - Remove (Picked unit) from Spirit_Link_Group.
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Number of units in Spirit_Link_Group) Equal to 0
                • Then - Actions
                  • Trigger - Turn off Spirit Link End <gen>
                  • Trigger - Turn off Spirit Link Death <gen>
                • Else - Actions
            • Else - Actions
  • Spirit Link Death
    • Events
      • Unit - A unit Dies
    • Conditions
      • Spirit_Link_Level[(Custom value of (Triggering unit))] Greater than 0
    • Actions
      • Set Variable Spirit_Link_CV = (Custom value of (Triggering unit))
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spirit_Link_Caster[Spirit_Link_CV] Equal to (Triggering unit)
        • Then - Actions
          • Set Variable Spirit_Link_Heal_Target = Spirit_Link_Target[Spirit_Link_CV]
        • Else - Actions
          • Set Variable Spirit_Link_Heal_Target = Spirit_Link_Caster[Spirit_Link_CV]
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spirit_Link_Level[(Custom value of Spirit_Link_Heal_Target)] Greater than 0
        • Then - Actions
          • Unit - Set Life of Spirit_Link_Heal_Target to 100.00%
          • Unit - Set Mana of Spirit_Link_Heal_Target to 100.00%
          • Set Variable Spirit_Link_Level[(Custom value of Spirit_Link_Heal_Target)] = 0
        • Else - Actions
      • -------- --------
      • Set Variable Spirit_Link_Level[Spirit_Link_CV] = 0
Something like that should work. Although, this one can get a little complicated when there's a ton of "interlinked" units, but I think it will still produce acceptable results. You can reference Spirit_Link_Level[Spirit_Link_CV] in the Death trigger to scale the healing based on ability level.

Edit: I've adjusted the triggers a few times now, bear with me. I THINK this design works properly.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,578
The spell works on its first iteration perfectly but not much luck after that

I also learned that Units that have reincarnation doesn't trigger Unit dies event.
Depends on how you want it to work, it should always allow you to link two units together. But if you want the caster to be able to link itself to five different units at the same time then you'll probably need Dynamic Indexing. Maybe a Unit Group array would work well to contain all of the unit's linked to the caster.

Reincarnating units don't die. If you want to detect that you need to use the "undefend" order trick.
 
Level 6
Joined
May 13, 2023
Messages
44
Yeah Thanks for the Help anyway
the Idea was that I had a Hero which had the ability to give the target hero Reincarnation then kill it.
Since this ability killed the target without actually killing it I tried to make it so that if the Target had been Spirit Linked it would heal the Caster after it uses the initial ability

I did achieve this through the first ability trigger
  • Premature Burial
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Premature Burial
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ((Target unit of ability being cast) has buff Spirit Link ) Equal to True
        • Then - Actions
          • Unit - Set life of (Triggering unit) to 100.00%
          • Unit - Set mana of (Triggering unit) to 100.00%
        • Else - Actions
      • Unit - Add Premature Burial (Dummy) to (Target unit of ability being cast)
      • Unit - Set level of Premature Burial (Dummy) for (Target unit of ability being cast) to (Level of Premature Burial for (Triggering unit))
      • Unit - Kill (Target unit of ability being cast)
      • Unit - For Unit (Target unit of ability being cast), end cooldown of ability Premature Burial (Dummy)
      • Unit - Remove Premature Burial (Dummy) from (Target unit of ability being cast)
But since this didn't work without the First ability I asked for the Request
 
Top