• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Problem with ability recharge system

Level 4
Joined
Apr 16, 2025
Messages
47
However, it's hard to call it a system.In short, take a look at the map. I have several problems.First, why does everything stop working if I remove the 0.01 second delay?

Second, why doesn't the cooldown of the Infernal ability get reduced if I press the Infernal ability and spam the Fan of Knives ability? No, I understand that this is because of the 0.01 second delay, but why isn't a separate local variable created for each trigger run? How can I fix this?

And third, if I turn on the second reload trigger and repeat the steps from the second point, the Fan of Knives cooldown is reduced by 4 times, meaning the trigger fires twice for the last ability used, which once again confirms that a local variable is not created for each ability used. What should I do?

P.S. the trigger "finishes using the ability" does not solve the problem, since the hero has a cast time, and if you perform any action immediately after using the ability, then nothing will work.
 

Attachments

  • TEST.w3m
    17.5 KB · Views: 9
Last edited:
Level 29
Joined
Sep 26, 2009
Messages
2,613
why does everything stop working if I remove the 0.01 second delay?
I think spell cast flow is programmed like this:
1. Start ability effect
2. Run triggers listening to "Starts the effect of an ability"
3. Reduce caster's mana by mana cost
4. Start spell's cooldown

Without the wait you basically start your reduced cooldown in step 2, but then steps 3 and 4 run, with step 4 starting new cooldown using the original (object editor) values.
When you put in the wait, you essentially postpone execution of step two:
1. Start ability effect
2. Run triggers listening to "Starts the effect of an ability" - this starts waiting
3. Reduce caster's mana by mana cost
4. Start spell's cooldown
5. Waiting trigger resumes after 0.01 seconds, starting new cooldown (the reduced cooldown)

Also, the 0.01 wait is not exact, it's more around ~0.3 seconds since waits are inaccurate.

I can see two options:

Option #1: In the Rune of Speed trigger modify (Ability being cast)'s cooldown. This way you modify cooldown in step 2 and step 4 will use that new value:
  • Rune of Speed1 Copy
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Set VariableSet abilLevel = ((Level of (Ability being cast) for (Triggering unit)) - 1)
      • Ability - Set Ability: (Unit: (Triggering unit)'s Ability with Ability Code: (Ability being cast))'s Real Level Field: Cooldown ('acdn') of Level: abilLevel to ((Cooldown of (Ability being cast), Level: abilLevel.) x 0.50)
I am using the "Game - Ability Cooldown" instead of "Unit - Ability Cooldown" to get the default cooldown value.

Option #2: Use a 0 second timer. Timers are accurate and it offsets execution to next frame, so basically directly after step 4. But in this case you need to keep track of all casts that may happen between current and next frame in arrays:
  • Rune of Speed1 Copy
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Set VariableSet abilityCount = (abilityCount + 1)
      • Set VariableSet abilityCodes[abilityCount] = (Ability being cast)
      • Set VariableSet abilityUnits[abilityCount] = (Triggering unit)
      • Set VariableSet abilityNewCooldowns[abilityCount] = ((Ability Cooldown of (Triggering unit) for ability (Ability being cast), Level: ((Level of (Ability being cast) for (Triggering unit)) - 1).) / 2.00)
      • Countdown Timer - Start abilityTimer as a One-shot timer that will expire in 0.00 seconds
  • Start Modified Cooldowns
    • Events
      • Time - abilityTimer expires
    • Conditions
    • Actions
      • For each (Integer A) from 1 to abilityCount, do (Actions)
        • Loop - Actions
          • Unit - For Unit abilityUnits[(Integer A)], start cooldown of ability abilityCodes[(Integer A)] " over "abilityNewCooldowns[(Integer A)] seconds.
      • Set VariableSet abilityCount = 0
why doesn't the cooldown of the Infernal ability get reduced if I press the Infernal ability and spam the Fan of Knives ability?
Your triggers modify cooldown of ability being cast. You don't modify cooldown of any other ability in those triggers. So casting Fan of Knives will reduce only cooldown of itself. This has nothing to do with variables as far as I see.

And third, if I turn on the second reload trigger and repeat the steps from the second point, the Fan of Knives cooldown is reduced by 4 times, meaning the trigger fires twice for the last ability used, which once again confirms that a local variable is not created for each ability used. What should I do?
That's not true. Each trigger runs once, so the cooldown is reduced only two times:
  • Rune of speed1 halves the base second cooldown of 9.00 seconds down to 4.50 seconds
  • Rune of speed2 halves the remaining cooldown of 4.50 seconds down to 2.25 seconds
 
Level 4
Joined
Apr 16, 2025
Messages
47
  • Rune of speed1 halves the base second cooldown of 9.00 seconds down to 4.50 seconds
  • Rune of speed2 halves the remaining cooldown of 4.50 seconds down to 2.25 seconds
What an amazing analysis! I'll have to try this out, but first I'll say no. The Rune of Speed2 trigger ability triggers twice. I personally tested this by disabling the Rune of Speed1 trigger. I pressed the Infernal and immediately the Fan of Knives. The trigger triggered twice on the last ability cast.
 
Level 29
Joined
Sep 26, 2009
Messages
2,613
The Rune of Speed2 trigger ability triggers twice. I personally tested this by disabling the Rune of Speed1 trigger
Ok, now I understand what you meant. Since in your original post you never mentioned that you've disabled the first trigger and then I saw that the cooldown after both triggers was reduced to 25% of the original value, it made me think that this was why you thought it ran 4 times.

In that regard, you are correct.
(Casting unit) and (Ability being cast) are globals. As I also wrote, the 0.01 wait is not accurate and is more around ~0.3 seconds, so if you cast different spell during that time window, then the first cast's values are overwritten - as you have already noticed.

But all is not lost. While (Casting unit) is global, (Triggering unit) is not, yet it achieves the same thing. So it is safe to replace (Casting unit) for (Triggering unit).
As for (Ability being cast), you could use variable shadowing. Create variable of type AbilityCode, give it name for example AbilityBeingCast and then do the following in your trigger:
  • Rune of Speed1
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Custom script: local integer udg_AbilityBeingCast
      • Set VariableSet AbilityBeingCast = (Ability being cast)
      • Wait 0.01 game-time seconds
      • Unit - For Unit (Triggering unit), start cooldown of ability AbilityBeingCast " over "((Ability Cooldown of (Triggering unit) for ability AbilityBeingCast, Level: ((Level of AbilityBeingCast for (Triggering unit)) - 1).) / 2.00) seconds.
Notice that the variable "AbilityBeingCast" is a global variable. It is referenced in the remaining parts of the trigger (especially in the action that starts cooldown), but it will behave like local variable does.
The trick is to create a local variable at the start of the script which has exactly the same name as the global variable, but is prefixed with "udg_".

Although other solutions in my previous post are better, I still wanted to mention that it is possible to solve the issue with Waits.
 
Level 4
Joined
Apr 16, 2025
Messages
47
Ok, now I understand what you meant. Since in your original post you never mentioned that you've disabled the first trigger and then I saw that the cooldown after both triggers was reduced to 25% of the original value, it made me think that this was why you thought it ran 4 times.

In that regard, you are correct.
(Casting unit) and (Ability being cast) are globals. As I also wrote, the 0.01 wait is not accurate and is more around ~0.3 seconds, so if you cast different spell during that time window, then the first cast's values are overwritten - as you have already noticed.

But all is not lost. While (Casting unit) is global, (Triggering unit) is not, yet it achieves the same thing. So it is safe to replace (Casting unit) for (Triggering unit).
As for (Ability being cast), you could use variable shadowing. Create variable of type AbilityCode, give it name for example AbilityBeingCast and then do the following in your trigger:
  • Rune of Speed1
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Custom script: local integer udg_AbilityBeingCast
      • Set VariableSet AbilityBeingCast = (Ability being cast)
      • Wait 0.01 game-time seconds
      • Unit - For Unit (Triggering unit), start cooldown of ability AbilityBeingCast " over "((Ability Cooldown of (Triggering unit) for ability AbilityBeingCast, Level: ((Level of AbilityBeingCast for (Triggering unit)) - 1).) / 2.00) seconds.
Notice that the variable "AbilityBeingCast" is a global variable. It is referenced in the remaining parts of the trigger (especially in the action that starts cooldown), but it will behave like local variable does.
The trick is to create a local variable at the start of the script which has exactly the same name as the global variable, but is prefixed with "udg_".

Although other solutions in my previous post are better, I still wanted to mention that it is possible to solve the issue with Waits.
Everything worked! My suspicions that the local variable is not created were confirmed. In general, interesting information about one formulation referring to a global variable, and the other to a local one. There is a gap in my knowledge. I have sometimes seen how mapmakers create local variables via "local integer udg_", but I have not yet mastered this myself. I bow low to you here.
 
Level 4
Joined
Apr 16, 2025
Messages
47
Ok, now I understand what you meant. Since in your original post you never mentioned that you've disabled the first trigger and then I saw that the cooldown after both triggers was reduced to 25% of the original value, it made me think that this was why you thought it ran 4 times.

In that regard, you are correct.
(Casting unit) and (Ability being cast) are globals. As I also wrote, the 0.01 wait is not accurate and is more around ~0.3 seconds, so if you cast different spell during that time window, then the first cast's values are overwritten - as you have already noticed.

But all is not lost. While (Casting unit) is global, (Triggering unit) is not, yet it achieves the same thing. So it is safe to replace (Casting unit) for (Triggering unit).
As for (Ability being cast), you could use variable shadowing. Create variable of type AbilityCode, give it name for example AbilityBeingCast and then do the following in your trigger:
  • Rune of Speed1
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Custom script: local integer udg_AbilityBeingCast
      • Set VariableSet AbilityBeingCast = (Ability being cast)
      • Wait 0.01 game-time seconds
      • Unit - For Unit (Triggering unit), start cooldown of ability AbilityBeingCast " over "((Ability Cooldown of (Triggering unit) for ability AbilityBeingCast, Level: ((Level of AbilityBeingCast for (Triggering unit)) - 1).) / 2.00) seconds.
Notice that the variable "AbilityBeingCast" is a global variable. It is referenced in the remaining parts of the trigger (especially in the action that starts cooldown), but it will behave like local variable does.
The trick is to create a local variable at the start of the script which has exactly the same name as the global variable, but is prefixed with "udg_".

Although other solutions in my previous post are better, I still wanted to mention that it is possible to solve the issue with Waits.
By the way, this solution also does not require a global variable, right? Well, that is, the global variable is instantly written to the local one, and what happens next is no longer important, right?

You also instantly wrote the global of the applied spell to the local one, so everything should work as it should and without "Custom script: local integer udg_AbilityBeingCast", if I'm not mistaken. True, with an amendment to the trick with "variable name" +1
 

Attachments

  • TEST (1).w3m
    17.5 KB · Views: 4
Last edited:
Level 29
Joined
Sep 26, 2009
Messages
2,613
By the way, this solution also does not require a global variable, right?
If you mean the trigger example I posted, then the answer is actually: Yes, global variable is required.
The reason is simple: GUI actions cannot reference a local variable. The trick with variable shadowing is that the editor thinks you are referencing global variable (so you can use GUI for it), but the actual script will use the local variable. Hence why it is important for the local variable to have same name as the global one (on top of having that "udg_" prefix).

In the test map you've posted, I think it will work alright for you the way you have it. In my example I have used arrays which made the solution more generic, but I think under normal circumstances it is basically impossible for two abilities to be cast at the exact point in time.
The only time I think that could be pulled off would be in case you order a group of dummy units to cast spells, but then again, I doubt you would care for cooldown reduction for dummy spells.
 
Top