• 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.

Periodic Timer Variable

Status
Not open for further replies.
Level 5
Joined
Apr 22, 2022
Messages
55
Heyo! I'm working on a map similar to zerg wars in SC2 although just the pve aspect of it. The zerg have upgrades to reduce the spawn time of their units being sent. Currently I'm doing this for undead and they are set to a strict periodic time trigger and the upgrades just increase the amount spawned. Is there a way I could get it working like zerg wars and have the upgrades reduce the timer on the spawn? I've been pondering it the last few days but I'm not quite sure where to even begin, I've become more familiar with variables but when it comes to timers and all that I'm not entirely sure where to go or start. It would also need to work for multiple undead players, each having their own timer they can upgrade. Thanks in advance if anyone has any ideas!
 

Remixer

Map Reviewer
Level 33
Joined
Feb 19, 2011
Messages
2,112
Hello, I am not familiar with Zerg Wars, but the question is quite simple so I will try to give you a simple answer.

Currently I'm doing this for undead and they are set to a strict periodic time trigger and the upgrades just increase the amount spawned.
The GUI triggering is quite clunky with periodic time. I'd recommend you create a Timer instead and start it as a periodic timer - it is essentially the same thing, but it gives you more options than just the default Time - Event.

With a timer, you would run this event:
  • Time - Repeating_Timer expires
And run your actions (spawn your units).

Whenever you want to change the interval (speed) at which the units spawn, you can change the expiration time of the Timer by essentially re-starting it:
  • Countdown Timer - Start Repeating_Timer as a Repeating timer that will expire in Whatever_Value_You_Want seconds
Where the Whatever_Value_You_Want is any real number (0.00, 1.24, 14.20 for example) you want to set.
 
Level 5
Joined
Apr 22, 2022
Messages
55
Thanks for the response! Would this work for multiple undead players? For example if they both have ghouls spawning every 30 seconds but Player One upgrades to reduce his to 28 seconds, would Player Two still have it spawning every 30?
 

Remixer

Map Reviewer
Level 33
Joined
Feb 19, 2011
Messages
2,112
Thanks for the response! Would this work for multiple undead players? For example if they both have ghouls spawning every 30 seconds but Player One upgrades to reduce his to 28 seconds, would Player Two still have it spawning every 30?
In that case, you would want to create two separate timers, one for each player, and change them accordingly.
 

Remixer

Map Reviewer
Level 33
Joined
Feb 19, 2011
Messages
2,112
Ah so it goes up to 6 players, so I'd need one for each one if they did pick Undead?
Yes, then you would want to run 6 timers. One timer for each unique player.

Alternatively, you can run a static repeating timer that adds value to a counter - let's say a Timer that loops every 1.00 second, adding +1 to a real called "Time" - essentially counting seconds since it was last reset. When that counter reaches certain values - lets say 30, you would trigger an event for individual players - but for that use you would probably want to Index that real variable for each player, so I am not sure if this would be easier for you in practice.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Woo boy alright. Maybe making this harder in my head. Guess I'll dive in and see if I can make it all make sense. xD Thanks.
Timers are no more complicated than a stopwatch. I'm sure you can figure it out with some trial and error.

Anyway, I want to throw out a few notes, hopefully this isn't too much to digest at once. These may not apply to your situation!

If you use a Timer array then make sure to set it's Initial Size to the highest Player Number in your map. You can go with 24 to be safe (representing up to Player 24). You would do this inside of the Variable Editor when you create the Variable (It MUST be done there). This rule only applies to Unit Group, Player Group, and Timer arrays, as they need special treatment due to their complexity.

An Array is an option that you can enable when you create a Variable. This option converts your Variable into a Variable Array, which gives it [ ] brackets after it's name. You'll notice that you can type an Integer inside of these brackets. This Integer is known as the Index of the Array and must be a number between 0 and 32,768. Each Index can store it's own value, so a single Variable Array can hold up to 32,768 different values. If you ever catch yourself creating what feels like way too many Variables, you're probably making a mistake and should be using Arrays!

If you're confused, just think of the Array as a way to turn a single variable into many, and it works much like a grocery list where you number the different items on the list -> 1: Apples, 2: Chicken, 3: Milk, etc...

This Array design is very useful and will make your life far easier once you get the hang of it. For instance, if you have data that is shared between multiple Players then you can use Arrays to make that data easier to manage. In this example I am using a player's Number as the Index of an Array Variable to keep track of the player's score (let's just say that in my map I want to award players extra score points for completing certain tasks):
  • Actions
    • Set Variable Player_Score[1] = 100
    • Set Variable Player_Score[2] = 250
    • Set Variable Player_Score[6] = 50
^ I gave Player 1 a score of 100, Player 2 a score of 250, and Player 6 a score of 50. You can change the Player_Score variable to be any kind of variable type, this is just an example.
  • Events
    • Unit - A unit dies
  • Conditions
  • Actions
    • Set Variable PN = (Player number of (Owner of (Killing unit)))
    • Set Variable Player_Score[PN] = Player_Score[PN] + 10
^ Then here's an example of how I can interact with and adjust the Player_Score array. In this case I'm increasing it by 10 whenever a Player kills a unit. Note how this trigger doesn't need to know who specifically killed a unit in order to work. Instead, it's completely generic and will work for any number of players. One trigger and one variable get the job done all thanks to Arrays!
 
Last edited:
Level 5
Joined
Apr 22, 2022
Messages
55
Ima be honest I'm not sure what to set the Index as lol assuming I have this right.
1697730300901.png
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Yes, you've successfully made a Timer array :peasant-ok-hand:

Now remember, each Index will track it's own Timer. So Ghoul_Timer[1] will use a different Timer than Ghoul_Timer[2]. Ghoul_Timer[3] will use a different Timer than both [1] and [2]. That's what makes an Array so useful, it's one variable that acts as many.

You can think of the Array like a grocery list:
[1] = Apples
[2] = Oranges
[3] = Milk

In the list you're numbering different things. But in this case your groceries are actually Timers, lol.
[1] = A timer
[2] = Another timer
[3] = And another timer

Now with that in mind, try to be creative and use these different Indexes in a way that will accomplish your goal.

We know that you want each Player to have their own Ghoul_Timer, so think about the Players in the game. You have Player 1, Player 2, Player 3, etc... All of these Players already have a number associated with them. Now think, how can I use that number in combination with this Index to achieve my goal?
 
Last edited:
Level 5
Joined
Apr 22, 2022
Messages
55
Yes, you've successfully made a Timer array :peasant-ok-hand:

Now remember, each Index will track it's own Timer. So Ghoul_Timer[1] will use a different Timer than Ghoul_Timer[2]. Ghoul_Timer[3] will use a different Timer than both [1] and [2]. That's what makes an Array so useful, it's one variable that acts as many.

You can think of the Array like a grocery list:
[1] = Apples
[2] = Oranges
[3] = Milk

In the list you're numbering different things. But in this case your groceries are actually Timers, lol.
[1] = A timer
[2] = Another timer
[3] = And another timer

Now with that in mind, try to be creative and use these different Indexes in a way that will accomplish your goal.

We know that you want each Player to have their own Ghoul_Timer, so think about the Players in the game. You have Player 1, Player 2, Player 3, etc... All of these Players already have a number associated with them. Now think, how can I use that number in combination with this Index to achieve my goal?
Thanks Uncle :p Carrying the grocery analogy lol, should the timer not be unit specific then? So it would be like;
[1] = Ghouls
[2] = Crypt Fiends ect.
Or would I want like a Player Number kind of thing where the "groceries" are the players. Would def be easier I think to have a timer for each unit that covers each player in one trigger then having a bunch of different ones for each player.. if that makes sense?
 

Remixer

Map Reviewer
Level 33
Joined
Feb 19, 2011
Messages
2,112
Thanks Uncle :p Carrying the grocery analogy lol, should the timer not be unit specific then? So it would be like;
[1] = Ghouls
[2] = Crypt Fiends ect.
Or would I want like a Player Number kind of thing where the "groceries" are the players. Would def be easier I think to have a timer for each unit that covers each player in one trigger then having a bunch of different ones for each player.. if that makes sense?
I think a clearer grocery analogy would be if you would be buying groceries, let's say food, for your pets let's say like this:
Food[PetName], where the Food is variable and [PetName] is the array, for example, Food[Dog] and Food[Cat] are both 'food' but refer to different things. Perhaps you have a third pet Food[Cow] would thus be needed. In this example, the Indices (Dog, Cat, Cow) could be replaced by 1, 2 and 3.

When learning triggers or coding, it is easiest to first create something in a way that you understand, rather than trying to do the most complex and advanced (yet most efficient) thing first, if you struggle to understand it.

Continuing this analogy, you can think of what suits your needs best and is clearest to you. For example, if you would be buying different things to each pet, it might be smarter to make the pet's name the variable and the index refer to the specific item, like this:
Dog[1] = Leash
Dog[2] = Food
Dog[3] = Bowl
Dog[4] = Bones
This would be smarter, rather than creating variables Leash, Food, Bowl, and Bones and using the index [Dog].

In your scenario, since you are tracking research, which is by default player-based, tracking the player in a variable would be quite an obvious choice. Then again, note that the variables aren't linked to each other and nothing stops you from doing both: linking the wave timers to specific players and even linking specific unit types to player-bound timers: TimerGhouls[1] would be the timers that Ghouls use for Player(1), for example.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Having everything in one trigger would be nice, but now we're getting into even more advanced territory.

For now, let's focus on getting what we have to work:

The Variable itself represents the different Units. The Index represents the different Players (technically their Player Numbers).

Here's how I could start the Ghoul timers, note that you would most likely do this differently in your map:
  • Start Ghoul Timers
    • Events
      • Player - Player 1 (Red) types a chat message containing -spawn as An exact match
      • Player - Player 2 (Blue) types a chat message containing -spawn as An exact match
      • Player - Player 3 (Teal) types a chat message containing -spawn as An exact match
    • Conditions
    • Actions
      • Set VariableSet Spawn_PN = (Player number of (Triggering player))
      • Countdown Timer - Start Ghoul_Timer[Spawn_PN] as a Repeating timer that will expire in 1.00 seconds
(Triggering player) is the Player that typed -spawn. This starts their respective Ghoul_Timer by using their Player Number as the [Index].
1 = Player 1, 2 = Player 2, 3 = Player 3, etc. Spawn_PN is an Integer variable.

Then here's how the ghouls would be created for Player 1:
  • Ghoul P1
    • Events
      • Time - Ghoul_Timer[1] expires
    • Conditions
    • Actions
      • Unit - Create 1 Ghoul for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
And for Player 2:
  • Ghoul P2
    • Events
      • Time - Ghoul_Timer[2] expires
    • Conditions
    • Actions
      • Unit - Create 1 Ghoul for Player 2 (Blue) at (Center of (Playable map area)) facing Default building facing degrees
This is a bit annoying though since we will need 8 of these triggers, one for each Player.

The next step is combining these Ghoul Timers into one trigger:
  • Ghoul Spawn
    • Events
      • Time - Ghoul_Timer[1] expires
      • Time - Ghoul_Timer[2] expires
      • Time - Ghoul_Timer[3] expires
      • Time - Ghoul_Timer[4] expires
      • Time - Ghoul_Timer[5] expires
      • Time - Ghoul_Timer[6] expires
      • Time - Ghoul_Timer[7] expires
      • Time - Ghoul_Timer[8] expires
    • Conditions
    • Actions
      • Set VariableSet Ghoul_Timer[0] = (Expiring timer)
      • For each (Integer Spawn_PN) from 1 to 8, do (Actions)
        • Loop - Actions
          • Custom script: if udg_Ghoul_Timer[0] == udg_Ghoul_Timer[udg_Spawn_PN] then
          • Custom script: exitwhen true
          • Custom script: endif
      • Set VariableSet Spawn_Player = (Player(Spawn_PN))
      • Unit - Create 1 Ghoul for Spawn_Player at Spawn_Point[Spawn_PN] facing Default building facing degrees
So now we're detecting when any of the 8 Ghoul timers expire inside of one central trigger.

In the Actions I use a For Each Loop to determine which Timer it was that expired by comparing the (Expiring timer) to each of the 8 Ghoul timers. Once the Timer has found it's match then the For Loop will stop - leaving Spawn_PN at the correct Timer index. This means that Spawn_PN will be equal to the [index] of the Timer that expired.

Unfortunately, Blizzard didn't make this easy to do in GUI (non-code) since there's no Timer Comparison condition. Instead, we need to rely on Custom Script to make this comparison.

I also introduced a Point array variable called Spawn_Point which represents each Player's spawn location - where units will be created. You would Set these Point variables beforehand in some kind of "setup" trigger.
 
Last edited:
Level 5
Joined
Apr 22, 2022
Messages
55
Okay this is making a lot more sense I think. I have a region for the ghouls to spawn in that's the same for all players so I think we're good there. Probably more than I needed :xxd: I have a computer (Mint) that controls the spawned undead. Just needed it to spawn them for Mint in accordance with the Player's upgrades.
 
Level 5
Joined
Apr 22, 2022
Messages
55
1697737996812.png

Would I be on the right track with this upgrade wise? In theory this would reduce the ghoul spawn timer by 5?
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
View attachment 451527
Would I be on the right track with this upgrade wise? In theory this would reduce the ghoul spawn timer by 5?
That looks correct to me, but it's assuming that Ghoul_Timer[player number] has already started once before. If this is the first time it's ever run then the Initial time will be at the default value of 0.00 seconds and it will crash the game (A timer given a negative value will crash and 0 - 5 = -5).
 
Level 5
Joined
Apr 22, 2022
Messages
55
That looks correct to me, but it's assuming that Ghoul_Timer[player number] has already started once before. If this is the first time it's ever run then the Initial time will be at the default value of 0.00 seconds and it will crash the game (A timer given a negative value will crash and 0 - 5 = -5).
Got it, yeah the initial timer will be like 30 seconds set at the start and then additional upgrades would shorten the time.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Got it, yeah the initial timer will be like 30 seconds set at the start and then additional upgrades would shorten the time.
That should work nicely. Just don't forget to Set the Initial Size of Ghoul_Timer to be equal to 8. This tells the game to create a new Timer at Indexes 1 through 8. If you fail to do this then you won't be able to use Ghoul_Timer[2] and up. The default size is 1 so [0] and [1] will be available to use by default.

This only applies to Timer, Unit Group, and Player Group arrays. Every other variable type can remain at it's default size - the game will automatically manage these other types for you.

I assume this Size rule exists to help reduce memory usage because Timers, Unit Groups, and Player Groups are more complicated than the other variable types.
 
Last edited:
Level 5
Joined
Apr 22, 2022
Messages
55
That should work nicely. Just don't forget to Set the Initial Size of Ghoul_Timer to be equal to 8. This tells the game to create a new Timer at Indexes 1 through 8. If you fail to do this then you won't be able to use Ghoul_Timer[2] and up. The default size is 1 so [0] and [1] will be available to use by default.

This only applies to Timer, Unit Group, and Player Group arrays. Every other variable type can remain at it's default size - the game will automatically manage these other types for you.
Just tested it and it does work :D At least for myself lol assuming it should work if there are multiple Undead players with varying upgrade levels. I set the size to 6 since it's only up 6 players, so we're good right?
 

Remixer

Map Reviewer
Level 33
Joined
Feb 19, 2011
Messages
2,112
Just kidding, I'm probably stupid but when I set it to 6 and come back to it, it defaults back to 1 lol.
The value of the variable at any given index will still return the default value before you set it. The indexes [0] -> [6] just means that you need to change the index that you are declaring:
  • Example
    • Events
      • Map initialization
    • Conditions
    • Actions
      • For each (Integer PlayerNumber) from 1 to 6, do (Actions)
        • Loop - Actions
          • Set VariableSet TimerDuration[PlayerNumber] = 30.00
Edit:
To change the default number of array in GUI you have to do it in the Edit Variable menu (see picture):
KNVtRzW[1].png
 
Level 5
Joined
Apr 22, 2022
Messages
55
The value of the variable at any given index will still return the default value before you set it. The indexes [0] -> [6] just means that you need to change the index that you are declaring:
  • Example
    • Events
      • Map initialization
    • Conditions
    • Actions
      • For each (Integer PlayerNumber) from 1 to 6, do (Actions)
        • Loop - Actions
          • Set VariableSet TimerDuration[PlayerNumber] = 30.00
Edit:
To change the default number of array in GUI you have to do it in the Edit Variable menu (see picture):
View attachment 451528
Ohhh did not know that was a thing! Okay thanks!
 
Level 5
Joined
Apr 22, 2022
Messages
55
Well.. I almost have it.. After upgrades though I'm getting multiple spawns at once and the times seem to be all over the place lol. Crypt is built>Timers Start>Player can purchase upgrade from Crypt>Timer should be reduced by 5 right? What am I missing xD
1697742612105.png
1697742591522.png
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
If it says 6 then it is working.

Anyway, you're restarting the timers each time the player constructs a Crypt, is that intentional? If you can build more than one Crypt then it's going to cause problems.

How about after the first Crypt is built you instead increase the number of spawned units rather than messing with the Timer itself.
  • Set Variable Ghouls_Spawned[X] = Ghouls_Spawned[X] + 1
  • Unit - Create Ghouls_Spawned[X] Ghoul for Player...
You can use an If Then Else to check whether the Timer has started or not. We know that if Ghouls_Spawned[X] is equal to 0 then the Crypt you just built was your first and you can start the timer. Otherwise, increase the amount spawned for the pre-existing timer.
 
Level 5
Joined
Apr 22, 2022
Messages
55
Oh man that's a good point lol. I mean I guess I could limit it 1 Crypt per player but. Idk I guess I thought restarting it was the only option lol.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Oh man that's a good point lol. I mean I guess I could limit it 1 Crypt per player but. Idk I guess I thought restarting it was the only option lol.
I edited my last post with more information on a solution.

However, if you want each Crypt to have it's own unique timer then I would suggest something a bit different. I attached a map with a system for easing the use of GUI timers, it's called GST and I created it for this very situation. The map also has a Unit Indexer which is a useful system for attaching data to units. The Unit Indexer system is not necessary but it would work well with your situation by allowing you to attach Timers directly to your Crypts and other unit-spawning buildings.

Also, remember that if you can destroy these buildings then you'll want to also destroy their attached timer - and in the case of my Integer array solution, subtract from their spawn count.

The logic for the Integer array solution may sound confusing but it's rather simple:
  • Player constructs their first Crypt --> Start timer and set the associated Spawn variable to however many units you want to create.
  • Player constructs a Crypt that ISN'T their first --> Increase the associated Spawn variable.
  • Player's Crypt dies --> If it was the last one remaining, destroy the timer and reset Spawn variable. Otherwise, subtract from the associated Spawn variable.

There's also Hashtables if you wish to enter even more advanced territory, they're basically an extended version of an Array and are much more versatile but also more complicated to use. Hive has many tutorials on the subject.
 

Attachments

  • GST Latest 1.w3m
    29.4 KB · Views: 5
Last edited:
Level 5
Joined
Apr 22, 2022
Messages
55
Let this be a lesson on good organization xD I still had the original Fiend and Gargoyle triggers enabled as well. Disabled those and it seems to be working as intended. Although when the Crypt is built I'd like the timers to start spawning so they don't need to use an initial upgrade. But I don't want it to start stacking as multiple Crypts are built.. but I'd prefer to let players build multiple Crypts if they need to manually summon additional units.
 
Level 5
Joined
Apr 22, 2022
Messages
55
Is there a check or condition I could put here to check when the corresponding timer expires to check if they have a Crypt built?
1697749647278.png
 
Level 5
Joined
Apr 22, 2022
Messages
55
Got to try it in a real game, I think the timers were accurate but the colors of the units were not :( Just stayed the color red.

1697776681011.png
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
1) You can't have a (Picked player) without picking a player -> Pick Every Player in Player Group.

2) You don't want to order the entire unit group to attack-move inside of that loop. You're already looping over the entire unit group in the first place so now you're doing it AGAIN for each unit. Just order the (Picked unit) to attack-move.

3) (Random point in Base <gen>) is a memory leak. Solution: Set variable Point = (random point...) -> Create 5 Ghoul at Point -> Remove Point.

4) One of the ways to get the Player whose timer expired is to use that For Loop method I showed earlier. That's probably the easiest method.
 
Level 5
Joined
Apr 22, 2022
Messages
55
Thanks Uncle I will give these a shot tonight. :) Guess I'm learning more on leaks lol so on 3. Anytime I point to a random point in a region I have a memory leak? So I just need to go back and set the variable point as a random point in region, have units move to that variable and then destroy it?
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Thanks Uncle I will give these a shot tonight. :) Guess I'm learning more on leaks lol so on 3. Anytime I point to a random point in a region I have a memory leak? So I just need to go back and set the variable point as a random point in region, have units move to that variable and then destroy it?
Yeah, you're basically looking at a memory leak anytime you see these words: Position/Center of

But if you store that position in a Point variable, use it, and then Remove it, the memory leak will be cleaned up.
 
Level 5
Joined
Apr 22, 2022
Messages
55
Yeah, you're basically looking at a memory leak anytime you see these words: Position/Center of

But if you store that position in a Point variable, use it, and then Remove it, the memory leak will be cleaned up.
Thank you for your patience Uncle :) I think I have a solid trigger now based off what you had? How can I incorporate a set color action using that loop you used earlier?
1697843986423.png
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Thank you for your patience Uncle :) I think I have a solid trigger now based off what you had? How can I incorporate a set color action using that loop you used earlier?
So instead of using the single line Pick Every Unit action, use the Pick Every Unit multiple action which lets you apply multiple Actions to each (Picked unit).

You were pretty much correct here:
1697850858885.png


You were just missing the 1 to 6 Loop that Set Spawn_PN which then Set Spawn_Player which was then used in the Set Color action as the Player whose color you want to use.
  • Unit - Change color of (Picked unit) to (Color of Spawn_Player)
Don't forget to set Spawn_Player!
 
Level 5
Joined
Apr 22, 2022
Messages
55
So instead of using the single line Pick Every Unit action, use the Pick Every Unit multiple action which lets you apply multiple Actions to each (Picked unit).

You were pretty much correct here:
View attachment 451663

You were just missing the 1 to 6 Loop that Set Spawn_PN which then Set Spawn_Player which was then used in the Set Color action as the Player whose color you want to use.
  • Unit - Change color of (Picked unit) to (Color of Spawn_Player)
Don't forget to set Spawn_Player!
So for setting Spawn_Player.. Is that variable a Player then? When I go to set it, I don't seem to have an option for the latter part, Set VariableSet_Spawn_Player = (Player(Spawn_PN))
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
So for setting Spawn_Player.. Is that variable a Player then? When I go to set it, I don't seem to have an option for the latter part, Set VariableSet_Spawn_Player = (Player(Spawn_PN))
Yes, that's a Player variable, and it's one of the many functions available -> Convert Player Index

It converts a Player Number into a Player, so 1 = Red, 2 = Blue, 3 = Teal, etc.
 
Status
Not open for further replies.
Top