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

[Trigger] Reward once per player

Level 4
Joined
Jan 9, 2024
Messages
28
I'm having some problems creating a trigger that gives a reward to a player after a specific unit is killed. The main problem I have is that the reward can only be obtained once per player. (The map has 6 Players)
I already made a trigger that seems to work but not all the time!
  • arena kills T1
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Unit-type of (Dying unit)) Equal to Broll Bearmantle (Night Elf Form)
    • Actions
      • For each (Integer A) from 1 to 6, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Owner of (Killing unit)) Equal to (Player((Integer A)))
            • Then - Actions
              • Set VariableSet arena_kills_count_1[(Integer A)] = (arena_kills_count_1[(Integer A)] + 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • arena_kills_count_1[(Integer A)] Equal to 1
                • Then - Actions
                  • Game - Display to (Player group((Player((Integer A))))) the text: Reward - 750 XP
                  • Hero - Add 750 experience to player_unit[(Player number of (Owner of (Killing unit)))], Show level-up graphics
                • Else - Actions
            • Else - Actions
So I made one Integer for each boss I have that counts the kill, but sometimes it doesn't count somehow. Maybe someone can come up with a better trigger that always works. I'm new to the editor so I only know how to use the GUI.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
You're on the right track but there's some unnecessary stuff here.

Here's how I would design the trigger:
  • arena kills T1
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Unit-type of (Dying unit)) Equal to Broll Bearmantle (Night Elf Form)
    • Actions
      • Set Variable Kill_PN = (Player number of (Owner of (Killing unit)))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Has_Killed_Broll_Bearmantle[Kill_PN] Equal to False
        • Then - Actions
          • Set Variable Has_Killed_Broll_Bearmantle[Kill_PN] = True
          • Game - Display to (Player group(Player(Kill_PN))) the text: Reward - 750 XP
          • Hero - Add 750 experience to player_unit[Kill_PN], Show level-up graphics
        • Else - Actions
Notes about our triggers:

1) The For Loop is unnecessary here since you can interact with the "Killing Player" directly. But even if you did want to interact with multiple Players at once you should be relying on a Player Group variable instead. You can control which Players are contained inside of a Player Group, meaning you can Remove players that have left the game as well fill the Player Group with players that were active from the very start of the game. This gives you precise control over who you interact with. An example of why the For Loop is bad: If you were playing your map solo the For Loop would run logic for 5 other Players that aren't even in the game. It's a waste of processing power and could lead to bugs and unwanted side effects.

2) arena_kills_count_1 being an Integer is not ideal in my opinion. When dealing with states that are either off/on you should use a Boolean variable. A Boolean always defaults to False which comes in handy and you can name it in a logical way that helps keep things clear -> Has, Is, Was, etc... In this case I went with Has_Killed_Broll_Bearmantle.

3) Kill_PN is an Integer variable I introduced that helps optimize the trigger. Using variables like this can speed things up since you don't have to rely on so many different functions. I was able to reference Kill_PN multiple times throughout the trigger with one click rather than having to dig through multiple menus to get the correct function.

4) This creates a memory leak:
  • (Player group(Player(Kill_PN)))
It's not the end of the world but may be something you want to look into. You can think of Memory Leaks like the deleted files that are in your Recycle Bin. Until you "empty" the Recycle Bin they will continue to exist taking up space. So you have the choice to manually get rid of them for good, but you don't HAVE to. All the memory leaks really do is bog down your computer which is fine until you build up A LOT of them.
 
Last edited:
Level 20
Joined
Feb 27, 2019
Messages
592
The unit-type in the condition says (Night Elf Form) which implies that it can transform into another unit-type. In that case the alternate unit-type must also be added to the condition within an or condition.
  • Or - Any (Conditions) are true
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Druid of the Claw (Night Elf Form)
      • (Unit-type of (Triggering unit)) Equal to Druid of the Claw (Bear Form)
 
Level 4
Joined
Jan 9, 2024
Messages
28
Thank you very much for the quick response! I just tested it and it works as it should. This will also help me create other triggers, as I'm not quite familiar with Boolean yet.
 
Level 4
Joined
Jan 9, 2024
Messages
28
4) This creates a memory leak:
  • (Player group(Player(Kill_PN)))
It's not the end of the world but may be something you want to look into. You can think of Memory Leaks like the deleted files that are in your Recycle Bin. Until you "empty" the Recycle Bin they will continue to exist taking up space. So you have the choice to manually get rid of them for good, but you don't HAVE to. All the memory leaks really do is bog down your computer which is fine until you build up A LOT of them.
So if I destroy the player group at the end with a custom script, will the trigger still work? I also used this player group in another trigger.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
So if I destroy the player group at the end with a custom script, will the trigger still work? I also used this player group in another trigger.
You would want to do something like this:
  • Set Variable TempPG = (Player group(Player(Kill_PN)))
  • Game - Display to TempPG the text: Reward - 750 XP
  • Custom script: call DestroyForce( udg_TempPG )
So you create and store the desired Player Group inside of a variable, reference said variable, then destroy the Player Group contained inside of said variable. In the Custom Script we refer to the Player Group as a Force (DestroyForce) but it means the same thing.

But there is a nicer solution that relieves you from needing to deal with this particular memory leak in such an annoying way. Instead, you can create a Player Group array variable and have each individual Player contained inside of their own group.

But while we're here let's take full advantage of Player Groups to manage more than just memory leaks:
  • Setup Player Groups
    • Events
      • Time - Elapsed game time is 0.01 seconds
    • Conditions
    • Actions
      • -------- It's important to do this after Map Initialization in order to avoid a potential desync! --------
      • -------- --------
      • Player Group - Pick every player in (All players) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked player) controller) Equal to User
            • Then - Actions
              • -------- Add the player to the different Player Groups based on their status: --------
              • -------- --------
              • Player Group - Add (Picked player) to Player_Group_All
              • Player Group - Add (Picked player) to Player_Group_Text[(Player number of (Picked player))]
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ((Picked player) slot status) Equal to Is playing
                • Then - Actions
                  • Player Group - Add (Picked player) to Player_Group_Playing
                • Else - Actions
                  • Player Group - Add (Picked player) to Player_Group_Leavers
              • -------- --------
              • -------- You can add Events to a trigger during runtime. Let's manage what happens when this player leaves the game: --------
              • Trigger - Add to Player Leaves Game <gen> the event (Player - (Picked player) leaves the game)
            • Else - Actions
I should clarify that these groups only contain USERS. You will need to add your Computers to separate groups or adjust the trigger to allow them as well.

Now we have references to four separate but important groups of players:

Player_Group_All: All of our Players, regardless of their playing status.
Player_Group_Text: Individual Players which we can use to avoid the aforementioned memory leak.
Player_Group_Playing: Players that are actively playing the game. You would probably use this one the most.
Player_Group_Leavers: Players that have left the game.

Let's not forget to manage these Player Groups when someone leaves the game:
  • Player Leaves Game
    • Events
    • Conditions
    • Actions
      • -------- Events are added to this trigger in the Setup Player Groups trigger! --------
      • -------- --------
      • Player Group - Remove (Triggering player) from Player_Group_Playing.
      • Player Group - Add (Triggering player) to Player_Group_Leavers
Now our leaving player (aka Triggering player) will be taken out of the Playing group and put in the Leavers group. This means that any triggers which reference Player_Group_Playing will no longer affect this player - very useful!

And finally an example of how to avoid the memory leak when sending your Text Message:
  • Example
    • Events
      • Time - Elapsed game time is 1.00 seconds
    • Conditions
    • Actions
      • -------- This will send a message to Player 1 and Player 4 without any memory leaks: --------
      • Game - Display to Player_Group_Text[1] for 30.00 seconds the text: It works!
      • Game - Display to Player_Group_Text[4] for 30.00 seconds the text: It works!
IMPORTANT! The Player_Group_Text variable will not work for everyone if you fail to set it's (Size) inside of the Variable Editor:
1704836614579.png

In this case I set it's Size to 24 because the game supports up to 24 players. This creates 24 instances of the Player Group so we can reference indexes [1] to [24]. This manual sizing of an Array is only ever necessary for Unit Groups, Player Groups, and Timers due to their more complicated design. For every other type of variable you can leave the Size at the default value of 1.
 

Attachments

  • Player Groups Example.w3m
    18.2 KB · Views: 1
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
This guide and example is perfect for my multiplayer map. Thank you sir!
No problem, glad it helped.

Here's one last example to show why we'd want to do all of this:
  • Events
    • Time - Every 1.00 seconds of game time
  • Conditions
  • Actions
    • Player Group - Pick every player in Player_Group_Playing and do (Actions)
      • Loop - Actions
        • Hero - Add 750 experience to player_unit[(Player number of (Picked player)], Show level-up graphics
So here we're periodically adding 750 experience to each Hero that belongs to an active Player.

So if you were in a match with 2 other players, all three of you would benefit from this. If Player 2 were to leave the game then only you and Player 3 would benefit from this experience reward. This is more efficient and helps avoid unwanted results.

Then if you still want to interact with that leaver you can simply use Player_Group_All instead of Player_Group_Playing. Pick the group that fits what you're trying to accomplish. Hopefully you can master this dynamic design of triggers, it can save a lot of future headaches, reduce trigger count/complexity, reduce variable usage, yield better performance, and so on.
 
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
I have another question, I tested my map with other players today. I have a respawn system with 2 triggers. One player died but did not revive somehow, and I have no idea what caused it. Do you know what could have caused the leak?

Player dies trigger:

  • player dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Dying unit) is A Hero) Equal to True
      • ((Dying unit) has an item of type Soulstone) Equal to False
      • ((Owner of (Dying unit)) is in Player_Group_Playing.) Equal to True
    • Actions
      • Set VariableSet unitdie = (Triggering unit)
      • Game - Display to Player_Group_Playing the text: ((Name of (Owner of (Dying unit))) + |c00FF0000 died!|r)
      • Game - Display to Player_Group_Text[(Player number of (Owner of unitdie))] the text: ((String((10 x (Hero level of unitdie)))) + |c00FFFF00Gold|r lost!)
      • Player - Add (-10 x (Hero level of unitdie)) to (Owner of unitdie).Current gold
      • Set VariableSet Timer[(Player number of (Owner of unitdie))] = ((Hero level of unitdie) x (2 + 2))
Player revives trigger:

  • player revives
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Timer[(Player number of (Owner of unitdie))] Greater than 0
        • Then - Actions
          • Set VariableSet Timer[(Player number of (Owner of unitdie))] = (Timer[(Player number of (Owner of unitdie))] - 1)
          • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[(Player number of (Owner of unitdie))] to (Revive in + (String(Timer[(Player number of (Owner of unitdie))])))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Timer[(Player number of (Owner of unitdie))] Equal to 0
            • Then - Actions
              • Hero - Instantly revive unitdie at (Center of Hero Respawn <gen>), Show revival graphics
              • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[(Player number of (Owner of unitdie))] to |c0000FF00Alive|r
              • Camera - Pan camera for (Owner of unitdie) to (Center of Hero Respawn <gen>) over 0.00 seconds
            • Else - Actions
        • Else - Actions
          • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[(Player number of (Owner of unitdie))] to |c0000FF00Alive|r
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
There's some issues here.

1) Your unitdie variable is a global variable. It can only contain one value at a time and will be overwritten whenever you set it again. This means it is NOT safe to track over time and should only ever be used during the same game frame.

2) You're trying to get the functionality of a Timer but are relying on Periodic Intervals and Integers. Instead, you should literally use a Timer variable.

3) Your math for Timer is wrong, you're adding 2 and 2 together to get 4, then multiplying the Hero level by 4. I assume you want to do (Hero level * 2) + 2. The trick is to use the Arithmetic function again on the first value to end up with (X + Y) x Z.

So here's what you need to do (or just open my demo map and copy the folder over):

Step 1: Go into your Variable Editor and find Timer. Change it's Variable Type to Timer, rename it to Revive_Timer, and set it's Size to 24.

Step 2: Create the following variables: Revive_PN (integer), Revive_Time_Remaining (integer array).

Step 3: Update your Hero Dies trigger to do the following:
  • Hero Dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is A Hero) Equal to True
    • Actions
      • Set VariableSet Revive_PN = (Player number of (Owner of (Triggering unit)))
      • Set VariableSet Revive_Time_Remaining[Revive_PN] = (((Hero level of (Triggering unit)) x 2) + 2)
      • Countdown Timer - Start Revive_Timer[Revive_PN] as a Repeating timer that will expire in 1.00 seconds
      • -------- Subtract gold / display text messages / update your multiboard to say this unit is Dead --------
Of course don't forget to add your missing Conditions/Actions. I don't have your map and didn't feel like recreating everything.

Step 4: Update your Hero Revives trigger to do the following:
  • Hero Revives
    • Events
      • Time - Revive_Timer[1] expires
      • Time - Revive_Timer[2] expires
      • Time - Revive_Timer[3] expires
      • Time - Revive_Timer[4] expires
    • Conditions
    • Actions
      • -------- Note: Add a Timer Event for each Player. In this example I'm covering Players 1 -> 4. --------
      • Set VariableSet Revive_PN = 0
      • Custom script: loop
      • Custom script: set udg_Revive_PN = udg_Revive_PN + 1
      • Custom script: exitwhen GetExpiredTimer() == udg_Revive_Timer[udg_Revive_PN]
      • Custom script: endloop
      • -------- --------
      • -------- The Revive_PN variable is now equal to the Player Number of the reviving player! --------
      • Set VariableSet Revive_Time_Remaining[Revive_PN] = (Revive_Time_Remaining[Revive_PN] - 1)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Revive_Time_Remaining[Revive_PN] Equal to 0
        • Then - Actions
          • Countdown Timer - Pause Revive_Timer[Revive_PN]
          • -------- Revive hero, update multiboard, pan camera --------
        • Else - Actions
You now have a Revive system that relies on Timer variables which are guaranteed to be precise. The Periodic Interval setup will always have the potential to be off by a whole intervals-worth of time since ALL of your Players are relying on it and can die at different times. These Timers are also only ever running while a Hero is dead.
 

Attachments

  • Revive Timer 1.w3m
    17.8 KB · Views: 1
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
Thank you! So basically it's bad to use global unit variables in more than one trigger. Will keep that in mind.

And now that you mention it, i got the second Arithmetic function wrong! Thanks for pointing it out.

I also believe that the revive system could help other map makers aswell, if there aren't already other working revive systems on this platform. I couldn't find a working system combined with a multiboard, so I figured it out myself. Now I can make it work without leaks thanks to you. :)

Edit: How do I make the multiboard count down the remaining time for the hero revive?
 
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
So I tested the Trigger and it's working. I also made a third trigger for the multiboard count. However I'm not sure if it's leaking or not.

Hero Dies:
  • player dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is A Hero) Equal to True
      • ((Triggering unit) has an item of type Soulstone) Equal to False
      • ((Owner of (Triggering unit)) is in Player_Group_Playing.) Equal to True
    • Actions
      • Set VariableSet Revive_PN = (Player number of (Owner of (Triggering unit)))
      • Set VariableSet unitdie[Revive_PN] = (Triggering unit)
      • Set VariableSet Revive_Time_Remaining[Revive_PN] = (((Hero level of (Triggering unit)) x 2) + 2)
      • Countdown Timer - Start Revive_Timer[Revive_PN] as a Repeating timer that will expire in 1.00 seconds
      • Game - Display to Player_Group_Playing the text: ((Name of (Owner of unitdie[Revive_PN])) + |c00FF0000 died!|r)
      • Game - Display to Player_Group_Text[(Player number of (Owner of unitdie[Revive_PN]))] the text: ((String((10 x (Hero level of unitdie[Revive_PN])))) + |c00FFFF00Gold|r lost!)
      • Player - Add (-10 x (Hero level of unitdie[Revive_PN])) to (Owner of unitdie[Revive_PN]).Current gold
      • Trigger - Turn on multiboard revive count <gen>
Multiboard Revive Count:
  • multiboard revive count
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[Revive_PN] to (Revive in + (String(Revive_Time_Remaining[Revive_PN])))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Revive_Time_Remaining[Revive_PN] Equal to 0
        • Then - Actions
          • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[Revive_PN] to |c0000FF00Alive|r
          • Trigger - Turn off (This trigger)
        • Else - Actions
Hero Revives:
  • player revives
    • Events
      • Time - Revive_Timer[1] expires
      • Time - Revive_Timer[2] expires
      • Time - Revive_Timer[3] expires
      • Time - Revive_Timer[4] expires
      • Time - Revive_Timer[5] expires
      • Time - Revive_Timer[6] expires
    • Conditions
    • Actions
      • Set VariableSet Revive_PN = 0
      • Custom script: loop
      • Custom script: set udg_Revive_PN = udg_Revive_PN + 1
      • Custom script: exitwhen GetExpiredTimer() == udg_Revive_Timer[udg_Revive_PN]
      • Custom script: endloop
      • Set VariableSet Revive_Time_Remaining[Revive_PN] = (Revive_Time_Remaining[Revive_PN] - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Revive_Time_Remaining[Revive_PN] Equal to 0
        • Then - Actions
          • Hero - Instantly revive unitdie[Revive_PN] at (Center of Hero Respawn <gen>), Show revival graphics
          • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[Revive_PN] to |c0000FF00Alive|r
          • Camera - Pan camera for (Owner of unitdie[Revive_PN]) to (Center of Hero Respawn <gen>) over 0.00 seconds
        • Else - Actions
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Thank you! So basically it's bad to use global unit variables in more than one trigger. Will keep that in mind
Not exactly. Your issue is that you're putting time (Wait, Timer, Periodic Interval, Elapsed Time) between when you Set the variable and when you try to reference it. During this time the variable can be set again, potentially changing it's value and changing your results. You can't assume that Revive_PN or unitdie are going to have the same value 1.00 second after setting them. So global variables can be shared throughout triggers as long as you remove the time element (Wait, Timer, Periodic Interval, Elapsed Time).

You also want to ensure that your triggers are executing in an ordered queue. This queue behavior, where instances of triggers are created and executed one after the other is how triggers behave 99% of the time. HOWEVER, there are some rare edge cases that you might run into. The main culprit is the "A unit dies" Event because this Event will break the rules and run immediately. That's why I try to use unique variables inside of triggers using this Event.

Summary: It's nice to reuse variables in order to keep things organized, it's just a battle of knowing when you should and should not do it. Most of the time it's perfectly safe (assuming there's no timing issues). If a trigger breaks then you can always try implementing unique variables to see if that resolves the problem. I recommend searching for a tutorial that explains how triggers work. I also suggest adding more "debug" text messages to your triggers since this will give you a visual representation of how and when your game logic is executing. This really helped me understand what was happening "under the hood".

Regarding the Revive system:

You don't need the new trigger (multiboard revive count). Look at the player revives trigger, it's subtracting 1 from Revive_Time_Remaining every 1.00 second. Immediately after the subtraction is when you would adjust your Multiboard -> Set text to Revive_Time_Remaining. You can also update the Multiboard to show the initial Time remaining inside of the player dies trigger. Always try to do things as soon as possible.

Did you just set a variable? Update anything related to that variable.
Did a unit die? Remove it from Unit Groups / update it's variables.
Did a Player leave? Remove it from Player Groups / update it's variables.

You want everything to be Event driven and happen IMMEDIATELY (when able). This whole "Every 1.00 second check random game states to see if anything has changed" is a buggy and imprecise design choice that I often see newbies making. You already know EXACTLY when the game state changes and that's when you should be updating these things.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Here is how both triggers should look (the comments are optional of course):
  • player dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is A Hero) Equal to True
      • ((Triggering unit) has an item of type Soulstone) Equal to False
      • ((Owner of (Triggering unit)) is in Player_Group_Playing.) Equal to True
    • Actions
      • Set VariableSet Revive_PN = (Player number of (Owner of (Triggering unit)))
      • Set VariableSet Revive_Time_Remaining[Revive_PN] = (((Hero level of player_hero[Revive_PN]) x 2) + 2)
      • Countdown Timer - Start Revive_Timer[Revive_PN] as a Repeating timer that will expire in 1.00 seconds
      • -------- --------
      • -------- Subtract gold / display text messages / update multiboard --------
      • Game - Display to Player_Group_Playing the text: ((Name of (Owner of player_hero[Revive_PN])) + |c00FF0000 died!|r)
      • Game - Display to Player_Group_Text[Revive_PN] the text: ((String((10 x (Hero level of player_hero[Revive_PN])))) + |c00FFFF00Gold|r lost!)
      • Player - Add (-10 x (Hero level of player_hero[Revive_PN])) to (Owner of player_hero[Revive_PN]).Current gold
      • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[Revive_PN] to (Revive in + (String(Revive_Time_Remaining[Revive_PN]))
  • player revives
    • Events
      • Time - Revive_Timer[1] expires
      • Time - Revive_Timer[2] expires
      • Time - Revive_Timer[3] expires
      • Time - Revive_Timer[4] expires
      • Time - Revive_Timer[5] expires
      • Time - Revive_Timer[6] expires
    • Conditions
    • Actions
      • -------- Note: Add a Timer Event for each Player. --------
      • Set VariableSet Revive_PN = 0
      • Custom script: loop
      • Custom script: set udg_Revive_PN = udg_Revive_PN + 1
      • Custom script: exitwhen GetExpiredTimer() == udg_Revive_Timer[udg_Revive_PN]
      • Custom script: endloop
      • -------- --------
      • -------- The Revive_PN variable is now equal to the Player Number of the reviving player! --------
      • Set VariableSet Revive_Time_Remaining[Revive_PN] = (Revive_Time_Remaining[Revive_PN] - 1)
      • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[Revive_PN] to (Revive in + (String(Revive_Time_Remaining[Revive_PN]))
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Revive_Time_Remaining[Revive_PN] Equal to 0
        • Then - Actions
          • -------- Revive hero, update multiboard, pan camera --------
          • Countdown Timer - Pause Revive_Timer[Revive_PN]
          • Multiboard - Set the text for MultiBoard item in column 3, row Multiboard_Spots[Revive_PN] to |c0000FF00Alive|r
          • Set VariableSet Revive_Point = (Center of Hero Respawn <gen>)
          • Camera - Pan camera for (Owner of player_unit[Revive_PN]) to Revive_Point over 0.00 seconds
          • Hero - Instantly revive player_unit[Revive_PN] at Revive_Point, Show revival graphics
          • Custom script: call RemoveLocation( udg_Revive_Point )
        • Else - Actions
1) I referenced your player_unit variable throughout the triggers since that variable will always be set to the player's Hero. The idea is that as long as we have a reference to a Player, we will also have their Player Number, which in return gets us their Hero -> player_unit[player number].
2) I deleted the unitdie variable as it was unnecessary.
3) I added a new Point variable called Revive_Point which I use to manage the two location-based memory leaks in the revive trigger.
4) I added some missing Actions like the Multiboard stuff and Pausing the revive timer.
 
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
Thank you for the detailed explanation.
So by creating Integer variables like Revive_PN or Kill_PN I can use the player_unit variable for all kind of triggers which refer to the player's hero?

I try to understand the trigger a bit.
We set the revive time with
  • Set VariableSet Revive_Time_Remaining[Revive_PN] = (((Hero level of player_hero[Revive_PN]) x 2) + 2)
Then we set a countdown timer which expires in 1 second which starts the second trigger.
  • Countdown Timer - Start Revive_Timer[Revive_PN] as a Repeating timer that will expire in 1.00 seconds
The following substracts 1 second from the remaining time right?
  • Set VariableSet Revive_Time_Remaining[Revive_PN] = (Revive_Time_Remaining[Revive_PN] - 1)
What does the loop do exactly?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
So by creating Integer variables like Revive_PN or Kill_PN I can use the player_unit variable for all kind of triggers which refer to the player's hero?
You don't NEED the Integer variables necessarily, they're just an efficient and clean way of handling things.

This is efficient:
  • Set VariableSet Revive_Time_Remaining[Revive_PN] = (((Hero level of player_hero[Revive_PN]) x 2) + 2)
This is less efficient and more difficult to read / setup:
  • Set VariableSet Revive_Time_Remaining[(Player number of (Owner of (Triggering unit)))] = (((Hero level of player_hero[(Player number of (Owner of (Triggering unit)))]) x 2) + 2)
But they achieve the same result.

However, in the case of the player revives trigger the Revive_PN variable IS necessary because of how I've set it up. That ties into your question about the Custom script loop stuff. Custom Script gives us access to special functions that only exist in code.

Basically, any Actions you put between this line:
  • Custom script: loop
And this line:
  • Custom script: endloop
Will be repeated or "looped" until you tell it to stop.

To do that we use exitwhen:
  • Custom script: exitwhen GetExpiredTimer() == udg_Revive_Timer[udg_Revive_PN]
This tells our loop to stop or "exit when" the condition is met.

The condition here is a comparison to see if the Timer that just expired (GetExpiredTimer) is equal (==) to Revive_Timer[1], [2], [3], etc.

You can also see that I set Revive_PN to 0 right before the loop starts and then increment it by 1 each loop cycle. That way I take advantage of the loops cyclic design and ask a new question each cycle: Did Revive_Timer[1] expire? Did Revive_Timer[2] expire? Did Revive_Timer[3] expire? And so on for all 6 timers.

The moment we find the Revive_Timer that expired we exit the loop and proceed with the rest of the trigger. At this stage Revive_PN will be equal to the number associated with the Timer (the player number) so we can now reference our player's Hero using player_unit[Revive_PN].

Summary: The loop increases Revive_PN by 1. It then checks to see if this value matches the Revive_Timer[] that expired. If these values match then we can stop increasing Revive_PN and proceed with the rest of the trigger. If it doesn't match, the loop cycles again and again until it eventually finds it's match. This results in getting us the Player Number of our reviving player.
 
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
Thanks for the explanation, I understand the trigger a bit better now. Now I also need to read a guide about custom scripts, I've only used the destroy function so far.
However my first map is close to a beta test now! tyty
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Thanks for the explanation, I understand the trigger a bit better now. Now I also need to read a guide about custom scripts, I've only used the destroy function so far.
However my first map is close to a beta test now! tyty
No problem, and to answer your other questions:
We set the revive time with
  • Set VariableSet Revive_Time_Remaining[Revive_PN] = (((Hero level of player_hero[Revive_PN]) x 2) + 2)
Then we set a countdown timer which expires in 1 second which starts the second trigger.
  • Countdown Timer - Start Revive_Timer[Revive_PN] as a Repeating timer that will expire in 1.00 seconds
The following substracts 1 second from the remaining time right?
  • Set VariableSet Revive_Time_Remaining[Revive_PN] = (Revive_Time_Remaining[Revive_PN] - 1)
Yes, you're correct about what's happening.

Just know that it's a Repeating timer so it expires every 1 second. A one-shot timer would expire in 1 second and stop itself automatically (no pause needed). Timers are also designed so that when you start one that is already running it will restart. This can be useful in certain situations.
 
Level 4
Joined
Jan 9, 2024
Messages
28
I have another question regarding Unit Groups and random Units from Unit Groups. I'm making a timed Arena Event. So when the timer expires The Heroes of all Players get teleported to the arena, then, one random Hero is chosen to fight in the arena. I'm posting the trigger I made but I don't really know how to make it work.

  • Arena Event 1
    • Events
      • Time - waves_timer expires
    • Conditions
      • waves_lvl Equal to 5
    • Actions
      • Trigger - Turn on Bear Fight <gen>
      • Set VariableSet Arena_Sound_Volume = 200.00
      • Set VariableSet Arena_Unit_Group = (Units in (Playable map area) owned by Player 1 (Red))
      • Countdown Timer - Destroy waves_timer_window
      • Game - Display to Player_Group_Playing the text: Arena Event!
      • Sound - Play NewTournament <gen>
      • Unit - Pause all units
      • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Unit - Remove All except expiration timer buffs from (Picked unit))
      • Unit Group - Pick every unit in Arena_Unit_Group and do (Actions)
        • Loop - Actions
          • Unit - Move (Picked unit) instantly to (Center of Arena SpectatorA <gen>), facing (Center of Arena SpectatorB <gen>)
          • Unit - Set life of (Picked unit) to 100.00%
          • Unit - Set mana of (Picked unit) to 100.00%
          • Unit - Reset ability cooldowns for (Picked unit).
          • Camera - Pan camera for (Owner of (Picked unit)) to (Center of Arena Region <gen>) over 0.00 seconds
      • Wait 5.00 seconds
      • Unit - Create 1 Arena_Boss_List[1] for Player 12 (Brown) at (Center of Arena NPC <gen>) facing (Center of Arena Player <gen>)
      • Set VariableSet Arena_Boss_1 = (Last created unit)
      • Unit - Pause Arena_Boss_1
      • Set VariableSet Arena_Fighter = (Random unit from Arena_Unit_Group)
      • Unit - Move Arena_Fighter instantly to (Center of Arena Player <gen>), facing (Center of Arena NPC <gen>)
      • Unit - Pause Arena_Fighter
      • Game - Display to Player_Group_Playing the text: 5 seconds until the...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: 4...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: 3...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: 2...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: 1...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: Fight!
      • Sound - Play DruidOfTheClawMorphedYesAttack1 <gen> at Arena_Sound_Volume% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Unit - Unpause Arena_Boss_1
      • Unit - Unpause Arena_Fighter
      • Unit - Order Arena_Boss_1 to Attack-Move To (Center of Arena Player <gen>)
Do I need Array Variables and custom scripts for this?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Do I need Array Variables and custom scripts for this?
You know for a fact that only one instance of this trigger will ever be active at a time, so most of the rules about making things MUI/MPI are no longer necessary. But if this trigger could somehow run twice in a row then you'd have a problem, but it can't as far as I can tell.

Anyway, you're forgetting about the variables we've created before. You already have access to every Player and their Heroes at all times using these:
Player_Group_Playing = Contains each active Player.
player_unit[] = Contains each Player's hero.

So with those two variables in mind, it should be easy to teleport all of the Heroes to the arena:
  • Player Group - Pick every player in Player_Group_Playing and do actions
    • Loop - Actions
      • Set Variable PN = (Player number of (Picked player))
      • Unit - Move player_unit[PN] to...
      • Unit - Set life of player_unit[PN] to...
      • Unit - Set mana of player_unit[PN] to...
Then you can get a random Player and their Hero like this:
  • Set Variable Random_Player = (Random player from Player_Group_Playing)
  • Set Variable Arena_Fighter = player_unit[(Player number of Random_Player)]
Your triggers should almost always be using these variables, assuming that they're interacting with the Players and their Heroes.
 
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
  • Player Group - Pick every player in Player_Group_Playing and do actions
    • Loop - Actions
      • Set Variable PN = (Player number of (Picked player))
      • Unit - Move player_unit[PN] to...
      • Unit - Set life of player_unit[PN] to...
      • Unit - Set mana of player_unit[PN] to...
Aaaah so I can use the "Pick every player" Trigger here, that's why I got stuck because I tried to accomplish it with "Pick every unit".
So when the Boss is killed or the Player who is fighting dies, I can use the "Pick every player" again because I already have the assigned Variables to relocate everyone in the group. That makes it easy. Thank you!
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Aaaah so I can use the "Pick every player" Trigger here, that's why I got stuck because I tried to accomplish it with "Pick every unit".
So when the Boss is killed or the Player who is fighting dies, I can use the "Pick every player" again because I already have the assigned Variables to relocate everyone in the group. That makes it easy. Thank you!
Yep (but it's not a trigger, it's an Action), try to think of your map in phases:

There's the initial setup phase where Players are detected and their Heroes (once chosen) are tracked -> We set variables for Player Groups, Unit Groups, Units, etc.

Then there's the gameplay phase, which contains the meat of your triggers that should basically always happen AFTER the setup phase -> Custom spells, arena duels, waves of enemies, etc.

Lastly, there's the end phase, where you handle things like victory/defeat.

The setup phase is the foundation that all other phases are built upon. So when you're creating a new trigger, ask yourself, which phase is this for? Then try to use the tools that you've created previously in order to accomplish your goal. Perhaps you'll need to create another "tool" to get this done properly or maybe you can rely on an existing "tool".
 
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
So this is going to be my last question (probably). I want to pause a certain Timer and then resume it again with another Trigger. I already found out that the "Resume Countdown Timer" does not work. So I looked it up and apparently it's only working with "Start Timer as a One-shot timer...".
This is the trigger that causes the Timer to resume.

  • Arena W
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Unit-type of (Dying unit)) Equal to Broll Bearmantle (Night Elf Form)) or (((Unit-type of (Dying unit)) Equal to Razormane Chieftain) or (((Unit-type of (Dying unit)) Equal to Owatanka) or (((Unit-type of (Dying unit)) Equal to Hogger) or (((Unit-type of (Dying unit)) Equal to Sneed) or (((Unit-type of (Dying unit)) Equal to Giant
    • Actions
      • Game - Display to Player_Group_Playing the text: Victory!
      • Sound - Play GoodJob02 <gen>
      • Wait 2.00 seconds
      • Player Group - Pick every player in Player_Group_Playing and do (Actions)
        • Loop - Actions
          • Set VariableSet Arena_PN = (Player number of (Picked player))
          • Unit - Move player_unit[Arena_PN] instantly to (Center of Arena End <gen>)
          • Unit - Set life of player_unit[Arena_PN] to 100.00%
          • Unit - Set mana of player_unit[Arena_PN] to 100.00%
          • Camera - Pan camera for (Owner of player_unit[Arena_PN]) to (Center of Arena End <gen>) over 0.00 seconds
      • Unit - Unpause all units
      • Unit Group - Pick every unit in (Units in Arena Battlefield <gen>) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) is A Hero) Equal to False
            • Then - Actions
              • Unit - Remove (Picked unit) from the game
            • Else - Actions
      • Set VariableSet TimerTime = (Remaining time for waves_timer)
      • Countdown Timer - Start waves_timer as a One-shot timer that will expire in TimerTime seconds
      • Trigger - Turn off Ogre Fight Loop <gen>
      • Trigger - Turn off Felstalker Loop <gen>
      • Trigger - Turn off Loramus Loop <gen>
So basically whenever a boss in the arena dies, the Timer should resume. I tested it and it worked after I beat the first boss. Not so after the second boss. I already tested a few changes to that trigger but the Timer always stopped working.

Edit: The Trigger is initally turned off and gets turned on by the Trigger which pauses the Timer.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
I assume the issue is that you're Starting waves_timer as a One-shot timer. So it runs only once and then never repeats. You need to tell it to Repeat again after it has expired as a One-shot. You can use a Boolean variable to track this "one-shot" state:
  • Set VariableSet TimerTime = (Remaining time for waves_timer)
  • Set Variable Waves_Timer_Is_Oneshot = True
  • Countdown Timer - Start waves_timer as a One-shot timer that will expire in TimerTime seconds
Then in your trigger that runs when waves_timer expires, use an If Then Else to check the state of this Boolean and if it's equal to True then Set it to False and Repeat the timer again:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Waves_Timer_Is_Oneshot) Equal to True
    • Then - Actions
      • Set Variable Waves_Timer_Is_Oneshot = False
      • Countdown Timer - Start waves_timer as a Repeating timer that will expire in X seconds
    • Else - Actions
So you're basically resetting it back to it's normal behavior. X should be whatever amount of time you normally repeat it at (you may need to track this in a variable as well if it changes).

I attached a map with a demo showcasing how you can Pause/Resume a Repeating timer using this same method.
Press Escape in-game to test it out -> Pause/Resume.

Also, I suggest using Or, Multiple Conditions, it'll save you many headaches:
  • Or - Any (Conditions) are true
    • Conditions
      • (Unit-type of (Dying unit)) Equal to Footman
      • (Unit-type of (Dying unit)) Equal to Knight
      • (Unit-type of (Dying unit)) Equal to Rifleman
Always choose the "Multiple" option when it's available.
 

Attachments

  • Pause Repeating Timer.w3m
    17.8 KB · Views: 2
Level 4
Joined
Jan 9, 2024
Messages
28
I have the following 2 Triggers which do work:

  • pause command
    • Events
      • Player - Player 1 (Red) types a chat message containing -pause as An exact match
    • Conditions
      • (Remaining time for waves_timer) Greater than 1.00
    • Actions
      • Countdown Timer - Pause waves_timer
  • resume command
    • Events
      • Player - Player 1 (Red) types a chat message containing -resume as An exact match
    • Conditions
      • (Remaining time for waves_timer) Greater than 1.00
    • Actions
      • Set VariableSet TimerTime = (Remaining time for waves_timer)
      • Countdown Timer - Start waves_timer as a One-shot timer that will expire in TimerTime seconds
The Timer gets paused and then resumed at where it stopped. So I don't understand why the same method didn't work with the other Trigger I made.
 
Level 4
Joined
Jan 9, 2024
Messages
28
Okay I'm posting the whole Trigger chain.

This is the Trigger which starts the waves in the map with a Timer and a Timer Window:

  • wave start
    • Events
      • Time - Elapsed game time is 15.00 seconds
    • Conditions
    • Actions
      • Countdown Timer - Start waves_timer as a One-shot timer that will expire in 150.00 seconds
      • Set VariableSet waves_lvl = (waves_lvl + 1)
      • Countdown Timer - Create a timer window for waves_timer with title (Wave + (String(waves_lvl)))
      • Set VariableSet waves_timer_window = (Last created timer window)
      • Trigger - Turn on skip command <gen>
This is the Trigger which starts a wave each time the Timer expires:

  • waves
    • Events
      • Time - waves_timer expires
    • Conditions
      • waves_lvl Not equal to 4
      • waves_lvl Not equal to 8
      • waves_lvl Not equal to 10
      • waves_lvl Not equal to 16
      • waves_lvl Not equal to 20
      • waves_lvl Not equal to 21
      • waves_lvl Not equal to 25
      • waves_lvl Not equal to 28
      • waves_lvl Not equal to 29
      • waves_lvl Not equal to 32
      • waves_lvl Not equal to 35
      • waves_lvl Not equal to 38
      • waves_lvl Not equal to 41
      • waves_lvl Not equal to 46
      • waves_lvl Not equal to 51
    • Actions
      • Countdown Timer - Destroy waves_timer_window
      • Game - Display to Player_Group_Playing the text: (|cffFF0303Wave + (String(waves_lvl)))
      • Sound - Play Warning <gen>
      • Trigger - Run wave unit amount <gen> (checking conditions)
      • Set VariableSet waves_amount = temp_num
      • Unit - Create 1 Footman for Player 11 (Dark Green) at (Random point in waves_region[3]) facing Default building facing degrees
      • Set VariableSet Footman = (Last created unit)
      • AI - Ignore Footman's guard position
      • Unit - Order Footman to Attack-Move To (Center of Cinematic D <gen>)
      • Unit - Create 1 Footman for Player 11 (Dark Green) at (Random point in waves_region[3]) facing Default building facing degrees
      • Set VariableSet Footman2 = (Last created unit)
      • AI - Ignore Footman2's guard position
      • Unit - Order Footman2 to Attack-Move To (Center of Horde Spawn 3 <gen>)
      • Cinematic - Ping minimap for Player_Group_Playing at (Center of waves_region[1]) for 1.00 seconds
      • Cinematic - Ping minimap for Player_Group_Playing at (Center of waves_region[2]) for 1.00 seconds
      • For each (Integer waves_temp_int) from 1 to waves_amount, do (Actions)
        • Loop - Actions
          • Wait 0.50 seconds
          • For each (Integer B) from 1 to 2, do (Actions)
            • Loop - Actions
              • Unit - Create 1 wave_type[waves_lvl] for Player 20 (Lavender) at (Random point in waves_region[(Integer B)]) facing Default building facing degrees
              • AI - Ignore (Last created unit)'s guard position
              • Unit - Order (Last created unit) to Attack-Move To (Position of Jaina Proudmoore 0012 <gen>)
              • Unit - Change color of (Last created unit) to Coal
              • Unit - Set Unit: (Last created unit)'s Integer Field: Gold Bounty Awarded - Base ('ubba') to Value: ((Unit: (Last created unit)'s Integer Field: Gold Bounty Awarded - Base ('ubba')) / 2)
              • Unit - Set Unit: (Last created unit)'s Integer Field: Gold Bounty Awarded - Sides per Die ('ubsi') to Value: 0
              • Unit - Set Unit: (Last created unit)'s Integer Field: Gold Bounty Awarded - Number of Dice ('ubdi') to Value: 0
      • Countdown Timer - Start waves_timer as a One-shot timer that will expire in 60.00 seconds
      • Set VariableSet waves_lvl = (waves_lvl + 1)
      • Countdown Timer - Create a timer window for waves_timer with title (Wave + (String(waves_lvl)))
      • Set VariableSet waves_timer_window = (Last created timer window)
And now we get to the problem; A new Timer (Arena) fires which starts the Arena events after a certain Waves Lvl is expired (I have 10 of these Arena events, so 10 of the following Triggers) After 90 seconds (or 60) the Waves Timer gets paused and the Arena begins.

  • Arena Event 1
    • Events
      • Time - waves_timer expires
    • Conditions
      • waves_lvl Equal to 4
    • Actions
      • Trigger - Turn on Arena W <gen>
      • Trigger - Turn on Arena D <gen>
      • Countdown Timer - Start arena_timer as a One-shot timer that will expire in 90.00 seconds
      • Trigger - Turn on Arena Timer <gen>
      • Game - Display to Player_Group_Playing the text: Arena Event starts ...
      • Wait 90.00 seconds
      • Countdown Timer - Pause waves_timer
      • Set VariableSet Arena_Sound_Volume = 200.00
      • Game - Display to Player_Group_Playing the text: Arena Event!
      • Sound - Play NewTournament <gen>
      • Unit - Pause all units
      • Player Group - Pick every player in Player_Group_Playing and do (Actions)
        • Loop - Actions
          • Set VariableSet Arena_PN = (Player number of (Picked player))
          • Unit - Move player_unit[Arena_PN] instantly to (Center of Arena SpectatorA <gen>), facing (Center of Arena SpectatorB <gen>)
          • Unit - Remove All except expiration timer buffs from player_unit[Arena_PN]
          • Unit - Set life of player_unit[Arena_PN] to 100.00%
          • Unit - Set mana of player_unit[Arena_PN] to 100.00%
          • Unit - Reset ability cooldowns for player_unit[Arena_PN].
          • Camera - Pan camera for (Owner of player_unit[Arena_PN]) to (Center of Arena Region <gen>) over 0.00 seconds
      • Cinematic - Send transmission to Player_Group_Playing from Rehgar Earthfury (Spectator) 0217 <gen> named Rehgar Earthfury: Play No sound and display Welcome to the Aren.... Modify duration: Add 0.00 seconds and Wait
      • Wait 5.00 seconds
      • Trigger - Turn on Bear Fight <gen>
      • Unit - Create 1 Arena_Boss_List[1] for Player 12 (Brown) at (Center of Arena NPC <gen>) facing (Center of Arena Player <gen>)
      • Set VariableSet Arena_Boss = (Last created unit)
      • Unit - Pause Arena_Boss
      • Set VariableSet Random_Player = (Random player from Player_Group_Playing)
      • Set VariableSet Arena_Rnd_PN = (Player number of Random_Player)
      • Set VariableSet Arena_Fighter = player_unit[Arena_Rnd_PN]
      • Game - Display to Player_Group_Playing the text: ((String(Arena_Rnd_PN)) + has been chosen to fight in the Arena!)
      • Unit - Move Arena_Fighter instantly to (Center of Arena Player <gen>), facing (Center of Arena NPC <gen>)
      • Game - Display to Player_Group_Playing the text: 5 seconds until the...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: 4...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: 3...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: 2...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: 1...
      • Sound - Play BattleNetTick <gen> at 100.00% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Wait 1.00 seconds
      • Game - Display to Player_Group_Playing the text: Fight!
      • Sound - Play DruidOfTheClawMorphedYesAttack1 <gen> at Arena_Sound_Volume% volume, located at (Center of Arena Region <gen>) with Z offset 0.00
      • Unit - Unpause Arena_Boss
      • Unit - Unpause Arena_Fighter
      • Unit - Order Arena_Boss to Attack-Move To (Center of Arena Player <gen>)
Then I have 2 Triggers which either start after the Arena Boss died or the Hero is killed in the Arena. In these 2 Triggers I want the Waves Timer to be resumed.

  • Arena W
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Unit-type of (Dying unit)) Equal to Broll Bearmantle (Night Elf Form)) or (((Unit-type of (Dying unit)) Equal to Razormane Chieftain) or (((Unit-type of (Dying unit)) Equal to Owatanka) or (((Unit-type of (Dying unit)) Equal to Hogger) or (((Unit-type of (Dying unit)) Equal to Sneed) or (((Unit-type of (Dying unit)) Equal to Giant
    • Actions
      • Game - Display to Player_Group_Playing the text: Victory!
      • Sound - Play GoodJob02 <gen>
      • Wait 2.00 seconds
      • Player Group - Pick every player in Player_Group_Playing and do (Actions)
        • Loop - Actions
          • Set VariableSet Arena_PN = (Player number of (Picked player))
          • Unit - Move player_unit[Arena_PN] instantly to (Center of Arena End <gen>)
          • Unit - Set life of player_unit[Arena_PN] to 100.00%
          • Unit - Set mana of player_unit[Arena_PN] to 100.00%
          • Camera - Pan camera for (Owner of player_unit[Arena_PN]) to (Center of Arena End <gen>) over 0.00 seconds
      • Unit - Unpause all units
      • Unit Group - Pick every unit in (Units in Arena Battlefield <gen>) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) is A Hero) Equal to False
            • Then - Actions
              • Unit - Remove (Picked unit) from the game
            • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Remaining time for waves_timer) Greater than 1.00
        • Then - Actions
          • Set VariableSet TimerTime_Remaining = (Remaining time for waves_timer)
          • Countdown Timer - Start waves_timer as a One-shot timer that will expire in TimerTime_Remaining seconds
        • Else - Actions
      • Trigger - Turn off Ogre Fight Loop <gen>
      • Trigger - Turn off Felstalker Loop <gen>
      • Trigger - Turn off Loramus Loop <gen>
  • Arena D
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is A Hero) Equal to True
      • ((Triggering unit) has an item of type Soulstone) Equal to False
      • ((Owner of (Triggering unit)) is in Player_Group_Playing.) Equal to True
      • (Arena Region <gen> contains (Triggering unit)) Equal to True
    • Actions
      • Unit - Move (Triggering unit) instantly to (Center of Hero Respawn <gen>)
      • Camera - Pan camera for (Owner of (Triggering unit)) to (Center of Hero Respawn <gen>) over 1.50 seconds
      • Game - Display to Player_Group_Playing the text: Failure!
      • Sound - Play QuestFailed <gen>
      • Wait 2.00 seconds
      • Player Group - Pick every player in Player_Group_Playing and do (Actions)
        • Loop - Actions
          • Set VariableSet Arena_PN = (Player number of (Picked player))
          • Unit - Move player_unit[Arena_PN] instantly to (Center of Arena End <gen>)
          • Unit - Set life of player_unit[Arena_PN] to 100.00%
          • Unit - Set mana of player_unit[Arena_PN] to 100.00%
          • Camera - Pan camera for (Owner of player_unit[Arena_PN]) to (Center of Arena End <gen>) over 0.00 seconds
      • Unit - Unpause all units
      • Unit Group - Pick every unit in (Units in Arena Battlefield <gen>) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) is A Hero) Equal to False
            • Then - Actions
              • Unit - Remove (Picked unit) from the game
            • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Remaining time for waves_timer) Greater than 1.00
        • Then - Actions
          • Set VariableSet TimerTime_Remaining = (Remaining time for waves_timer)
          • Countdown Timer - Start waves_timer as a One-shot timer that will expire in TimerTime_Remaining seconds
        • Else - Actions
      • Trigger - Turn off Ogre Fight Loop <gen>
      • Trigger - Turn off Felstalker Loop <gen>
      • Trigger - Turn off Loramus Loop <gen>
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
This condition looks unnecessary:
  • (Remaining time for waves_timer) Greater than 1.00
Doesn't this mean if the waves_timer is less than 1.00 second then you'll never Start the timer again? I don't see why it's necessary in the first place.


This doesn't make any sense to me:
  • Wait 90.00 seconds
  • Countdown Timer - Pause waves_timer
waves_timer shouldn't be running, why would it need to be paused? It expired ~90.00 seconds ago and hasn't been running since.

One-shot timers stop the moment they expire so there's no need to Pause it after that (who knows what that'd even do, break things most likely).


Also, your Arena Boss starts after 90 + 60 seconds, so it's delayed by 150 seconds. You have to wait 60 seconds for waves_timer to expire, then another 90.00 second Wait action.

Also, use the Wait game-time action, I believe the normal Wait will continue running even while the game is paused. Furthermore, understand that Waits need to be synced in multiplayer meaning that they'll never be as precise as timers, and should always take longer than you'd expect.

Also, as far as I can tell, arena_timer doesn't actually do anything since I can't see the trigger it expires in.

You could probably combine the Arena triggers into one single trigger with clever use of Array variables. That's assuming that the outcome will be mostly the same for each Arena battle -> One hero vs One boss, for example.
 
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
This doesn't make any sense to me:
  • zzz.gif
    Wait 90.00 seconds
  • timer.gif
    Countdown Timer - Pause waves_timer
waves_timer shouldn't be running, why would it need to be paused? It expired ~90.00 seconds ago and hasn't been running since.

One-shot timers stop the moment they expire so there's no need to Pause it after that (who knows what that'd even do, break things most likely).


Also, your Arena Boss starts after 90 + 60 seconds, so it's delayed by 150 seconds. You have to wait 60 seconds for waves_timer to expire, then another 90.00 second Wait action.

Also, use the Wait game-time action, I believe the normal Wait will continue running even while the game is paused. Furthermore, understand that Waits need to be synced in multiplayer meaning that they'll never be as precise as timers, and should always take longer than you'd expect.
I used the Wait here because I want the Arena Timer to pause at a time when the Wave Timer is active and running. And not at the moment when the Timer is destroyed so that it can actually be paused.

But still, the issue is that, after the second Arena boss is killed, the Waves Timer stops at around 12 seconds remaining. And I don't know how I can fix it.

Basically I want Arena Events happen during the Waves, and when an Arena Event happens the Waves get paused. After the Arena Event is done, the waves resume.
 
Level 4
Joined
Jan 9, 2024
Messages
28
You could probably combine the Arena triggers into one single trigger with clever use of Array variables. That's assuming that the outcome will be mostly the same for each Arena battle -> One hero vs One boss, for example.
I have one Arena event where all Players fight vs. one boss, but yeah 9/10 are basically the same; one hero vs. one boss.
 
Level 4
Joined
Jan 9, 2024
Messages
28
Edit: I got it fixed! It was a stupid mistake I somehow overlooked.

So basically I still had this condition which prevented the waves to continue after the second arena boss.

  • waves_lvl Not equal to 10
I'm sorry I wasted your time with this mistake. Thanks for trying to help me out tho!
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
I think I understand what you're trying to do now. If you want Waves to be pause-able then you'll need to remove all Waits from them since those will continue to run regardless, it's not like they're tied to the Timer (the trigger is already running). Also, turning off a trigger doesn't stop any active instances of it that are running.

I'll throw a map together for you, give me 15 minutes to update this post with an attached file.
 

Attachments

  • Wave System 1.w3m
    23.4 KB · Views: 1

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,552
Edit: I got it fixed! It was a stupid mistake I somehow overlooked.

So basically I still had this condition which prevented the waves to continue after the second arena boss.

  • waves_lvl Not equal to 10
I'm sorry I wasted your time with this mistake. Thanks for trying to help me out tho!
Glad you got it fixed. Feel free to check out my map (above post) to see a more advanced design.

I have it setup to do the following:
1) Remove all Wait actions. (They tend to cause problems and aren't precise)
2) Use more Array variables to manage the different data for each Wave / Arena Boss. This can be extended even further with more variables.
3) Spawn units using a Repeating Timer.
4) Pause the Spawning process if an Arena Boss starts.
5) Resume the Spawning process if an Arena Boss dies.

It's designed so that you'll NEVER run any Spawn logic when an Arena starts, even if you start the two timers at the same exact time. Note that it's currently lacking logic for when a Player dies to an Arena Boss, but you can just copy and paste the Arena Boss Dies trigger and adjust it where needed (most of the logic remains the same I imagine).

With that in mind, I think your current triggers may run into issues since you rely so heavily on Waits. For instance, if an Arena starts in the middle of a Wave, it'll pause all existing units, but not any units spawned in the future. And units will continue to spawn since Waits can't be stopped.
 
Last edited:
Level 4
Joined
Jan 9, 2024
Messages
28
Thank you! I'll take a look at it when I have time. Although my triggers are working now they may not work in certain circumstances as you determined. I try to redo the triggers with your wave system map! Can I give you rep other than just liking your post for all your help?
 
Last edited:
Top