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

"Bind" Unit to Building

Status
Not open for further replies.
Level 7
Joined
May 30, 2018
Messages
290
Hey people :)

I made following triggers -> issues follow below

  • Spawn
    • Events
      • Unit - A unit Finishes construction
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Farm
    • Actions
      • Set VariableSet BuildingLoc = (Position of (Triggering unit))
      • Set VariableSet RandomReg = (Region centered at BuildingLoc with size (700.00, 700.00))
      • Set VariableSet SpawnLoc[0] = (Random point in RandomReg)
      • Set VariableSet SpawnLoc[1] = (Random point in RandomReg)
      • Set VariableSet SpawnLoc[2] = (Random point in RandomReg)
      • Wait 2.00 seconds
      • Unit - Create 1 Footman for Player 1 (Red) at SpawnLoc[0] facing Default building facing degrees
      • Hashtable - Save Handle OfSpawnLoc[0] as (Key (Last created unit).) of 0 in Hashtable_Patrol. (I though of something like this, but I really have no idea :c )
      • Unit - Change ownership of (Last created unit) to Player 11 (Dark Green) and Retain color
      • Wait 2.00 seconds
      • Unit - Create 1 Footman for Player 1 (Red) at SpawnLoc[1] facing Default building facing degrees
      • Hashtable - Save Handle OfSpawnLoc[1] as (Key (Last created unit).) of 1 in Hashtable_Patrol.
      • Unit - Change ownership of (Last created unit) to Player 11 (Dark Green) and Retain color
      • Wait 2.00 seconds
      • Unit - Create 1 Footman for Player 1 (Red) at SpawnLoc[2] facing Default building facing degrees
      • Hashtable - Save Handle OfSpawnLoc[2] as (Key (Last created unit).) of 2 in Hashtable_Patrol.
      • Unit - Change ownership of (Last created unit) to Player 11 (Dark Green) and Retain color
  • Untitled Trigger 001
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet UG_Footman = (Units of type Footman)
  • WanderCheck
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in UG_Footman and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Current order of (Picked unit)) Equal to (Order(attack))
            • Then - Actions
              • Unit - Remove Wander (Neutral) from (Picked unit)
            • Else - Actions
              • Unit - Order (Picked unit) to Move To (Load (Key (Picked unit).) of 0 in Hashtable_Patrol.) (heres the part where I don't know how to properly load my saved handle)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                • Then - Actions
                  • Unit - Add Wander (Neutral) to (Picked unit)
                • Else - Actions
Now here are my issues:
1. The trigger does not seem to recognize my Unit Group variable "UG_Footman" (only works when I dont use my varibale)

2. general question: I have no idea about hashtables. How do I save a handle from a location and load it up in another trigger?? My plan is to have a building which spawns units, that are bound to said building and its location (via hashtable) and make the units move back to said building after the attack is over. (+ the location is an array if that matters)
I dont know how to load the saved handle (if it even is the right one) accordingly in the other trigger (higlighted above).
 
Last edited:
Level 7
Joined
May 30, 2018
Messages
290
Hey people :)

I made following triggers -> issues follow below

  • Spawn
    • Events
      • Unit - A unit Finishes construction
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Farm
    • Actions
      • Set VariableSet BuildingLoc = (Position of (Triggering unit))
      • Set VariableSet RandomReg = (Region centered at BuildingLoc with size (700.00, 700.00))
      • Set VariableSet SpawnLoc[0] = (Random point in RandomReg)
      • Set VariableSet SpawnLoc[1] = (Random point in RandomReg)
      • Set VariableSet SpawnLoc[2] = (Random point in RandomReg)
      • Wait 2.00 seconds
      • Unit - Create 1 Footman for Player 1 (Red) at SpawnLoc[0] facing Default building facing degrees
      • Hashtable - Save Handle OfSpawnLoc[0] as (Key (Last created unit).) of 0 in Hashtable_Patrol. (I though of something like this, but I really have no idea :c )
      • Unit - Change ownership of (Last created unit) to Player 11 (Dark Green) and Retain color
      • Wait 2.00 seconds
      • Unit - Create 1 Footman for Player 1 (Red) at SpawnLoc[1] facing Default building facing degrees
      • Hashtable - Save Handle OfSpawnLoc[1] as (Key (Last created unit).) of 1 in Hashtable_Patrol.
      • Unit - Change ownership of (Last created unit) to Player 11 (Dark Green) and Retain color
      • Wait 2.00 seconds
      • Unit - Create 1 Footman for Player 1 (Red) at SpawnLoc[2] facing Default building facing degrees
      • Hashtable - Save Handle OfSpawnLoc[2] as (Key (Last created unit).) of 2 in Hashtable_Patrol.
      • Unit - Change ownership of (Last created unit) to Player 11 (Dark Green) and Retain color
  • Untitled Trigger 001
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet UG_Footman = (Units of type Footman)
  • WanderCheck
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in UG_Footman and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Current order of (Picked unit)) Equal to (Order(attack))
            • Then - Actions
              • Unit - Remove Wander (Neutral) from (Picked unit)
            • Else - Actions
              • Unit - Order (Picked unit) to Move To (Load (Key (Picked unit).) of 0 in Hashtable_Patrol.) (heres the part where I don't know how to properly load my saved handle)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                • Then - Actions
                  • Unit - Add Wander (Neutral) to (Picked unit)
                • Else - Actions
Now here are my issues:
1. The trigger does not seem to recognize my Unit Group variable "UG_Footman" (only works when I dont use my varibale)

2. general question: I have no idea about hashtables. How do I save a handle from a location and load it up in another trigger?? My plan is to have a building which spawns units, that are bound to said building and its location (via hashtable) and make the units move back to said building after the attack is over. (+ the location is an array if that matters)
I dont know how to load the saved handle (if it even is the right one) accordingly in the other trigger (higlighted above).
bump
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
1:
Storing all of the footman in UG_Footman during Map Initialization is a one time thing. Your trigger checks for any footman that are currently on the map during map initialization (during the loading screen) and adds them to a group. Any new footman, whether they be trained or created from a trigger are not included in this unit group. You need to add the new footman to the group after you've created them.
  • Unit - Create a footman
  • Unit - Add (Last created unit) to UG_Footman

It's often easier to create a trigger that detects when a Footman enters the map and then add them to the unit group in response.
  • Events:
  • Unit - A unit enters the playable map area
  • Conditions:
  • Unit-type of Triggering unit Equal to Footman
  • Actions:
  • Unit - Add (Triggering unit) to UG_Footman


2:
You're using the Parent key wrong in your Hashtable. In this case you don't need to increase the Parent key by 1 for each thing you save to the Hashtable.
All of your Points (locations) can be saved at Parent key 0.

So when you save your data to the hashtable you should change this:
  • Hashtable - Save Handle OfSpawnLoc[0] as (Key (Last created unit).) of 0 in Hashtable_Patrol
  • Hashtable - Save Handle OfSpawnLoc[1] as (Key (Last created unit).) of 1 in Hashtable_Patrol
  • Hashtable - Save Handle OfSpawnLoc[2] as (Key (Last created unit).) of 2 in Hashtable_Patrol

To this (notice how I'm saving the data at parent key 0 for all three footman instead of 0, 1, 2):
  • Hashtable - Save Handle OfSpawnLoc[0] as (Key (Last created unit).) of 0 in Hashtable_Patrol
  • Hashtable - Save Handle OfSpawnLoc[1] as (Key (Last created unit).) of 0 in Hashtable_Patrol
  • Hashtable - Save Handle OfSpawnLoc[2] as (Key (Last created unit).) of 0 in Hashtable_Patrol

Also, make sure that when you Load data from your Hashtable that you're loading from the correct Parent key. In your triggers you're always loading from Parent key 0, which is correct in this situation, but in the future you may have data saved at other Parent keys.

Maybe this will help you understand how Hashtables work. Here's what you're essentially doing when saving data to a hashtable:
  • Hashtable - Save Handle of (Data) as (Child Key) of (Parent Key) in Hashtable

So you save your data as the child key of a parent key. Keys are just another word for indexes, so they're no different than the indexes used in your SpawnLoc array. If you're unsure of what an index is, it's the [brackets] that come after your variable.

The naming of parent/child starts to make sense when you understand this concept. A parent can have many children and these children all belong to a single parent. This relationship allows you to store data in a structured way with much more control than a standard 1-dimensional array. Dimensions are how many [indexes] an array has, so a 2-dimensional array would look like SpawnLoc[0][0]. A 3-dimensional array would look like SpawnLoc[0][0][0].

Here's another way you can imagine a Hashtable. It's basically just a 2-dimensional array:
  • Unit - Create a footman
  • Hashtable_Patrol[Last created unit][0] = SpawnLoc[0]
  • Unit - Create a footman
  • Hashtable_Patrol[Last created unit][0] = SpawnLoc[1]
  • Unit - Create a footman
  • Hashtable_Patrol[Last created unit][0] = SpawnLoc[2]
^ That's essentially what you're doing when you use a Hashtable. The reason this works is because each unit has it's own unique handle, so the first [index] is different for each of them.


Handles are an integer id so what you're really saving is something like this:
  • Unit - Create a footman
  • Hashtable_Patrol[1000000][0] = SpawnLoc[0]
  • Unit - Create a footman
  • Hashtable_Patrol[1000001][0] = SpawnLoc[1]
  • Unit - Create a footman
  • Hashtable_Patrol[1000002][0] = SpawnLoc[2]
^ The long number in the first [index] is the unit's handle. No two units will EVER have the same handle which is why this all works properly.

So you might be asking yourself, what's the Parent key used for if I'm always setting it to 0? Understand that you're only using 0 in THIS particular case because we don't need to use the other Parent keys here.

But let's say we wanted to save some additional data to our hashtable. For example, we want to save the Owner of the Footman in our Hashtable as well. Rather than creating an entirely new Hashtable we can instead use a different Parent key in Hashtable_Patrol:
  • Unit - Create a footman
  • Hashtable - Save Handle OfSpawnLoc[0] as (Key (Last created unit).) of 0 in Hashtable_Patrol
  • Hashtable - Save Handle of Player 1 (Red) as (Key (Last created unit).) of 1 in Hashtable_Patrol

See why the Parent key is useful? We can now save multiple sets of data in our hashtable to the same unit.

I realize saving the Player is a little weird since this information is readily available but it's just an example. With a single Hashtable you can save near endless amounts of data to multiple units.
 
Last edited:
Level 7
Joined
May 30, 2018
Messages
290
1:
Storing all of the footman in UG_Footman during Map Initialization is a one time thing. Your trigger checks for any footman that are currently on the map during map initialization (during the loading screen) and adds them to a group. Any new footman, whether they be trained or created from a trigger are not included in this unit group. You need to add the new footman to the group after you've created them.
  • Unit - Create a footman
  • Unit - Add (Last created unit) to UG_Footman

It's often easier to create a trigger that detects when a Footman enters the map and then add them to the unit group in response.
  • Events:
  • Unit - A unit enters the playable map area
  • Conditions:
  • Unit-type of Triggering unit Equal to Footman
  • Actions:
  • Unit - Add (Triggering unit) to UG_Footman


2:
You're using the Parent key wrong in your Hashtable. In this case you don't need to increase the Parent key by 1 for each thing you save to the Hashtable.
All of your Points (locations) can be saved at Parent key 0.

So when you save your data to the hashtable you should change this:
  • Hashtable - Save Handle OfSpawnLoc[0] as (Key (Last created unit).) of 0 in Hashtable_Patrol
  • Hashtable - Save Handle OfSpawnLoc[1] as (Key (Last created unit).) of 1 in Hashtable_Patrol
  • Hashtable - Save Handle OfSpawnLoc[2] as (Key (Last created unit).) of 2 in Hashtable_Patrol

To this (notice how I'm saving the data at parent key 0 for all three footman instead of 0, 1, 2):
  • Hashtable - Save Handle OfSpawnLoc[0] as (Key (Last created unit).) of 0 in Hashtable_Patrol
  • Hashtable - Save Handle OfSpawnLoc[1] as (Key (Last created unit).) of 0 in Hashtable_Patrol
  • Hashtable - Save Handle OfSpawnLoc[2] as (Key (Last created unit).) of 0 in Hashtable_Patrol

Also, make sure that when you Load data from your Hashtable that you're loading from the correct Parent key. In your triggers you're always loading from Parent key 0, which is correct in this situation, but in the future you may have data saved at other Parent keys.

Maybe this will help you understand how Hashtables work. Here's what you're essentially doing when saving data to a hashtable:
  • Hashtable - Save Handle of (Data) as (Child Key) of (Parent Key) in Hashtable

So you save your data as the child key of a parent key. Keys are just another word for indexes, so they're no different than the indexes used in your SpawnLoc array. If you're unsure of what an index is, it's the [brackets] that come after your variable.

The naming of parent/child starts to make sense when you understand this concept. A parent can have many children and these children all belong to a single parent. This relationship allows you to store data in a structured way with much more control than a standard 1-dimensional array. Dimensions are how many [indexes] an array has, so a 2-dimensional array would look like SpawnLoc[0][0]. A 3-dimensional array would look like SpawnLoc[0][0][0].

Here's another way you can imagine a Hashtable. It's basically just a 2-dimensional array:
  • Unit - Create a footman
  • Hashtable_Patrol[Last created unit][0] = SpawnLoc[0]
  • Unit - Create a footman
  • Hashtable_Patrol[Last created unit][0] = SpawnLoc[1]
  • Unit - Create a footman
  • Hashtable_Patrol[Last created unit][0] = SpawnLoc[2]
^ That's essentially what you're doing when you use a Hashtable. The reason this works is because each unit has it's own unique handle, so the first [index] is different for each of them.


Handles are an integer id so what you're really saving is something like this:
  • Unit - Create a footman
  • Hashtable_Patrol[1000000][0] = SpawnLoc[0]
  • Unit - Create a footman
  • Hashtable_Patrol[1000001][0] = SpawnLoc[1]
  • Unit - Create a footman
  • Hashtable_Patrol[1000002][0] = SpawnLoc[2]
^ The long number in the first [index] is the unit's handle. No two units will EVER have the same handle which is why this all works properly.

So you might be asking yourself, what's the Parent key used for if I'm always setting it to 0? Understand that you're only using 0 in THIS particular case because we don't need to use the other Parent keys here.

But let's say we wanted to save some additional data to our hashtable. For example, we want to save the Owner of the Footman in our Hashtable as well. Rather than creating an entirely new Hashtable we can instead use a different Parent key in Hashtable_Patrol:
  • Unit - Create a footman
  • Hashtable - Save Handle OfSpawnLoc[0] as (Key (Last created unit).) of 0 in Hashtable_Patrol
  • Hashtable - Save Handle of Player 1 (Red) as (Key (Last created unit).) of 1 in Hashtable_Patrol

See why the Parent key is useful? We can now save multiple sets of data in our hashtable to the same unit.

I realize saving the Player is a little weird since this information is readily available but it's just an example. With a single Hashtable you can save near endless amounts of data to multiple units.
Thanks for that in depth reply :) I understand what you wanne say, but there are still some small issues. MAybe you can help out once again.

I made this new trigger to detect if my units are attack to then remove wander:

  • WanderCheck Copy
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Footman
    • Actions
      • Set VariableSet wanderattack = False
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • wanderattack Equal to False
            • Then - Actions
              • Unit - Remove Wander (Neutral) from (Triggering unit)
              • Set VariableSet wandertimer = 3.00
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                • Then - Actions
                  • Trigger - Add to (This trigger) the event (Time - Every 1.00 seconds of game time)
                  • Set VariableSet wandertimer = (wandertimer - 1.00)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • wandertimer Equal to 0.00
                        • Then - Actions
                          • Set VariableSet wanderattack = True
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                            • Then - Actions
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • wanderattack Equal to True
                                • Then - Actions
                                  • Unit - Order (Triggering unit) to Move To (Load (Key (Triggering unit).) of 0 in Hashtable_Patrol.)
                                  • Unit - Add Wander (Neutral) to (Triggering unit)
                                • Else - Actions
                            • Else - Actions
                        • Else - Actions
                    • Else - Actions
                • Else - Actions
            • Else - Actions
        • Else - Actions
So first of: I wanted to ask if I did the periodic check of "wandertimer" right the way I did it.

Second off: I still don't really know how to load the key handles of the footmen (I understodd how to save them accordingly) but now I don't know how to load the key as you can see in the action "Unit-Order Triggering Unit to Move To". The command doesn't get executed in the end. I know that I did it really convoluted, had no other idea :I

Your help is really appreciated!
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
What's the deal with the empty If Then Else statements? Looks like you have 3 or 4 that don't need to be there.

This looks like a disaster, don't do this:
  • Trigger - Add to (This trigger) the event (Time - Every 1.00 seconds of game time)

This line is correct if you saved it properly (show me how you saved it):
  • Unit - Order (Triggering unit) to Move To (Load (Key (Triggering unit).) of 0 in Hashtable_Patrol.)

Just to clarify, the Triggering unit is the ATTACKED unit in this case.

If you want to make this trigger work for multiple footman at once then you'll need a different design. I recommend looking into MUI tutorials.

In this case you can take advantage of a Unit Group and a separate trigger that runs every let's say 0.50 seconds. This trigger picks through your Footman inside of said Unit Group and manages their Wander duration. The Wander duration can be tracked on a per unit basis using the Hashtable.

I'll show you an example in a minute.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
I created what I think you wanted:
  • Create Hashtable
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Don't forget to actually create the Hashtable! --------
      • Hashtable - Create a hashtable
      • Set VariableSet Wander_Hashtable = (Last created hashtable)
      • -------- --------
      • -------- Hashtable Notes: --------
      • -------- Parent Key 0 = X coordinate of spawn position --------
      • -------- Parent Key 1 = Y coordinate of spawn position --------
      • -------- Parent Key 2 = Wander Duration --------
  • Footman Is Attacked
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Footman
    • Actions
      • -------- Add the footman to the wander group and remove it's wander ability: --------
      • Unit Group - Add (Triggering unit) to Wander_Group
      • Unit - Remove Wander (Neutral) from (Triggering unit)
      • -------- --------
      • -------- Reset the wander duration. A value of 1 is equal to 0.50 seconds, so 6 = 3.00 seconds: --------
      • Hashtable - Save 6 as (Key (Triggering unit).) of 2 in Wander_Hashtable.
      • -------- --------
      • -------- Turn on the wander periodic trigger: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Wander Periodic <gen> is on) Equal to False
        • Then - Actions
          • Trigger - Turn on Wander Periodic <gen>
        • Else - Actions
  • Wander Periodic
    • Events
      • Time - Every 0.50 seconds of game time
    • Conditions
    • Actions
      • -------- Go through all of our wandering units and count down their duration: --------
      • Unit Group - Pick every unit in Wander_Group and do (Actions)
        • Loop - Actions
          • -------- Subtract 1 from the picked footman's wander duration: --------
          • Set VariableSet Wander_Duration = (Load (Key (Picked unit).) of 2 from Wander_Hashtable.)
          • Set VariableSet Wander_Duration = (Wander_Duration - 1)
          • Hashtable - Save Wander_Duration as (Key (Picked unit).) of 2 in Wander_Hashtable.
          • -------- --------
          • -------- If it's wander duration is 0 then tell it to return home: --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Wander_Duration Equal to 0
            • Then - Actions
              • Unit - Add Wander (Neutral) to (Picked unit)
              • -------- --------
              • -------- Create a point using the loaded X/Y coordinates from the hashtable: --------
              • Set VariableSet Wander_X = (Load (Key (Picked unit).) of 0 from Wander_Hashtable.)
              • Set VariableSet Wander_Y = (Load (Key (Picked unit).) of 1 from Wander_Hashtable.)
              • Set VariableSet Wander_Point = (Point(Wander_X, Wander_Y))
              • Unit - Order (Picked unit) to Move To Wander_Point
              • Custom script: call RemoveLocation (udg_Wander_Point)
              • -------- --------
              • -------- Remove the picked unit from this group since it's now returning home: --------
              • Unit Group - Remove (Picked unit) from Wander_Group.
            • Else - Actions
      • -------- --------
      • -------- Turn off this trigger while the group is empty: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Wander_Group is empty) Equal to True
        • Then - Actions
          • Trigger - Turn off (This trigger)
        • Else - Actions
  • Build Farm
    • Events
      • Unit - A unit Finishes construction
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Farm
    • Actions
      • -------- Create the footman and save the X/Y coordinates of their spawn location: --------
      • Set VariableSet Spawn_Loc = (Random point in RandomReg <gen>)
      • Set VariableSet Wander_X = (X of Spawn_Loc)
      • Set VariableSet Wander_Y = (Y of Spawn_Loc)
      • Unit - Create 1 Footman for Player 11 (Dark Green) at Spawn_Loc facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Spawn_Loc)
      • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Wander_Hashtable.
      • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Wander_Hashtable.
      • Unit - Change color of (Last created unit) to Dark Green
      • -------- --------
      • Wait 2.00 seconds
      • Set VariableSet Spawn_Loc = (Random point in RandomReg <gen>)
      • Set VariableSet Wander_X = (X of Spawn_Loc)
      • Set VariableSet Wander_Y = (Y of Spawn_Loc)
      • Unit - Create 1 Footman for Player 11 (Dark Green) at Spawn_Loc facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Spawn_Loc)
      • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Wander_Hashtable.
      • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Wander_Hashtable.
      • Unit - Change color of (Last created unit) to Dark Green
      • -------- --------
      • Wait 2.00 seconds
      • Set VariableSet Spawn_Loc = (Random point in RandomReg <gen>)
      • Set VariableSet Wander_X = (X of Spawn_Loc)
      • Set VariableSet Wander_Y = (Y of Spawn_Loc)
      • Unit - Create 1 Footman for Player 11 (Dark Green) at Spawn_Loc facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Spawn_Loc)
      • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Wander_Hashtable.
      • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Wander_Hashtable.
      • Unit - Change color of (Last created unit) to Dark Green
 

Attachments

  • Wander Example.w3m
    20.5 KB · Views: 15
Level 7
Joined
May 30, 2018
Messages
290
I created what I think you wanted:
  • Create Hashtable
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Don't forget to actually create the Hashtable! --------
      • Hashtable - Create a hashtable
      • Set VariableSet Wander_Hashtable = (Last created hashtable)
      • -------- --------
      • -------- Hashtable Notes: --------
      • -------- Parent Key 0 = X coordinate of spawn position --------
      • -------- Parent Key 1 = Y coordinate of spawn position --------
      • -------- Parent Key 2 = Wander Duration --------
  • Footman Is Attacked
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Footman
    • Actions
      • -------- Add the footman to the wander group and remove it's wander ability: --------
      • Unit Group - Add (Triggering unit) to Wander_Group
      • Unit - Remove Wander (Neutral) from (Triggering unit)
      • -------- --------
      • -------- Reset the wander duration. A value of 1 is equal to 0.50 seconds, so 6 = 3.00 seconds: --------
      • Hashtable - Save 6 as (Key (Triggering unit).) of 2 in Wander_Hashtable.
      • -------- --------
      • -------- Turn on the wander periodic trigger: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Wander Periodic <gen> is on) Equal to False
        • Then - Actions
          • Trigger - Turn on Wander Periodic <gen>
        • Else - Actions
  • Wander Periodic
    • Events
      • Time - Every 0.50 seconds of game time
    • Conditions
    • Actions
      • -------- Go through all of our wandering units and count down their duration: --------
      • Unit Group - Pick every unit in Wander_Group and do (Actions)
        • Loop - Actions
          • -------- Subtract 1 from the picked footman's wander duration: --------
          • Set VariableSet Wander_Duration = (Load (Key (Picked unit).) of 2 from Wander_Hashtable.)
          • Set VariableSet Wander_Duration = (Wander_Duration - 1)
          • Hashtable - Save Wander_Duration as (Key (Picked unit).) of 2 in Wander_Hashtable.
          • -------- --------
          • -------- If it's wander duration is 0 then tell it to return home: --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Wander_Duration Equal to 0
            • Then - Actions
              • Unit - Add Wander (Neutral) to (Picked unit)
              • -------- --------
              • -------- Create a point using the loaded X/Y coordinates from the hashtable: --------
              • Set VariableSet Wander_X = (Load (Key (Picked unit).) of 0 from Wander_Hashtable.)
              • Set VariableSet Wander_Y = (Load (Key (Picked unit).) of 1 from Wander_Hashtable.)
              • Set VariableSet Wander_Point = (Point(Wander_X, Wander_Y))
              • Unit - Order (Picked unit) to Move To Wander_Point
              • Custom script: call RemoveLocation (udg_Wander_Point)
              • -------- --------
              • -------- Remove the picked unit from this group since it's now returning home: --------
              • Unit Group - Remove (Picked unit) from Wander_Group.
            • Else - Actions
      • -------- --------
      • -------- Turn off this trigger while the group is empty: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Wander_Group is empty) Equal to True
        • Then - Actions
          • Trigger - Turn off (This trigger)
        • Else - Actions
  • Build Farm
    • Events
      • Unit - A unit Finishes construction
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Farm
    • Actions
      • -------- Create the footman and save the X/Y coordinates of their spawn location: --------
      • Set VariableSet Spawn_Loc = (Random point in RandomReg <gen>)
      • Set VariableSet Wander_X = (X of Spawn_Loc)
      • Set VariableSet Wander_Y = (Y of Spawn_Loc)
      • Unit - Create 1 Footman for Player 11 (Dark Green) at Spawn_Loc facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Spawn_Loc)
      • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Wander_Hashtable.
      • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Wander_Hashtable.
      • Unit - Change color of (Last created unit) to Dark Green
      • -------- --------
      • Wait 2.00 seconds
      • Set VariableSet Spawn_Loc = (Random point in RandomReg <gen>)
      • Set VariableSet Wander_X = (X of Spawn_Loc)
      • Set VariableSet Wander_Y = (Y of Spawn_Loc)
      • Unit - Create 1 Footman for Player 11 (Dark Green) at Spawn_Loc facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Spawn_Loc)
      • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Wander_Hashtable.
      • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Wander_Hashtable.
      • Unit - Change color of (Last created unit) to Dark Green
      • -------- --------
      • Wait 2.00 seconds
      • Set VariableSet Spawn_Loc = (Random point in RandomReg <gen>)
      • Set VariableSet Wander_X = (X of Spawn_Loc)
      • Set VariableSet Wander_Y = (Y of Spawn_Loc)
      • Unit - Create 1 Footman for Player 11 (Dark Green) at Spawn_Loc facing Default building facing degrees
      • Custom script: call RemoveLocation (udg_Spawn_Loc)
      • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Wander_Hashtable.
      • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Wander_Hashtable.
      • Unit - Change color of (Last created unit) to Dark Green
Hey man, sorry for replying so late. First off I wanne thank you for your hard work concercing this trigger. It really gave me an good insight in how to apply this method. Its always like that: I have no idea how I could realise my ideas, but after somebody shows it to me step by step, I get it. :D

Just one line is not 100% clear to me: What is meant by this
-------- Reset the wander duration. A value of 1 is equal to 0.50 seconds, so 6 = 3.00 seconds: --------
Hashtable - Save 6 as (Key (Triggering unit).) of 2 in Wander_Hashtable.

And how/where does this trigger detect if an unit left battle, so it only counts down the timer for returning home as soon as the fight is over.

+ 1 question: Is there a efficient way in the spawn trigger to change ownership of the units depending on the player who built them: for example spawn building is from red --> spawned units turn to dark green, blue --> brown, teal --> peach and so on. Or should I just make a simple trigger like: "A unit enters the map" and if the owner is red, change it to dark green
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
Whenever a Footman is attacked it's "Duration" gets set to 6. The Wander Periodic trigger then subtracts 1 from this Duration every 0.50 seconds. Once the Duration reaches 0 the Footman is considered "out of combat" and will return home/regain the wander ability. Unfortunately, this design can have unwanted results since it doesn't account for everything. For example, a Priest casting heal on an ally during battle is technically in combat but the current system wouldn't account for this.

A fix to this: Make the Duration longer and/or change the design so that the periodic trigger checks the Current order of the Picked units. You could combine these two effects to almost guarantee that nothing will go wrong. So if the footman isn't attacked for X seconds then force it to return home, but also periodically check to see if it's current order is "stand down" (or whatever order is used to determine that a unit is out of combat). So if it's order is "out of combat" then return it home as well.

Something like this:
If Duration Equal to 0 OR Current order of Picked unit Equal to "stand down" -> Return footman home.


Regarding your second question:
Arrays are usually the answer to making something more efficient.
Player related arrays are especially simple since the Player Number can be used as the [index] in the array.

  • Events:
  • Unit - A unit finishes training
  • Conditions:
  • Actions:
  • Set Variable PN = Player number of (Owner of (Triggering unit))
  • Unit - Change owner of (Trained unit) to PlayerCPU[PN] and change color
PlayerCPU is a Player variable with an Array. PN is an OPTIONAL Integer variable that helps make the trigger easier to read.

Of course this Array needs to be setup first:
  • Events:
  • Map Initialization
  • Actions:
  • Set Variable PlayerCPU[1] = Player 13 (Dark Green)
  • Set Variable PlayerCPU[2] = Player 14 (Brown)
  • Set Variable PlayerCPU[3] = Player 15 (Peach)
  • etc...
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
You're referencing Triggering unit which probably does not exist in this context. Try using either GDD_DamageSource or (Damaged unit).

A Damage System is definitely a good idea though. Additionally, you could create some triggers that detect when spells are cast and Add the units in response to this.

Also, there's a trick to using variables as Keys in Hashtables.
To do this you'll need a Handle variable and then Custom script to set it to the proper thing that you want to reference:
  • Custom script: set udg_HandleVar = udg_GDD_DamageSource
  • Hashtable - Save 6 as Key(HandleVar) of 2 in Hashtable_Patrol
In that example I named my Handle variable HandleVar.
 
Level 7
Joined
May 30, 2018
Messages
290
You're referencing Triggering unit which probably does not exist in this context. Try using either GDD_DamageSource or (Damaged unit).

A Damage System is definitely a good idea though. Additionally, you could create some triggers that detect when spells are cast and Add the units in response to this.

Also, there's a trick to using variables as Keys in Hashtables.
To do this you'll need a Handle variable and then Custom script to set it to the proper thing that you want to reference:
  • Custom script: set udg_HandleVar = udg_GDD_DamageSource
  • Hashtable - Save 6 as Key(HandleVar) of 2 in Hashtable_Patrol
In that example I named my Handle variable HandleVar.
works perfectly fine now!

Another thing just came up xD. I tried to wrap my head around it myself, but cant seem to grasp how to do it.
How could I go about to respawn the units belonging to a building. Say 1 of the 3 Footmen dies and as long as the building lives, they get "restocked" after death. I would roughly guess we can work with the hastable, a unitgroup and an integer, that counts down?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
I'm not entirely sure I understand how you want it to work but it sounds like you could just save the Structure to the footman like this:
  • Events:
  • A unit finishes training a unit
  • Conditions:
  • Trained unit-type Equal to Footman
  • Actions:
  • Hashtable - Save Key(Triggering unit) as Key(Trained unit) of 3 in Hashtable_Patrol

Then when a footman dies check to see if the Structure linked to it is alive:
  • Events:
  • A unit dies
  • Conditions:
  • Unit-type Equal to Footman
  • Actions:
  • Set Variable Structure = Load Key(Triggering unit) of 3 in Hashtable_Patrol
  • If Structure is Alive Equal to True then do stuff...
 
Level 7
Joined
May 30, 2018
Messages
290
I'm not entirely sure I understand how you want it to work but it sounds like you could just save the Structure to the footman like this:
  • Events:
  • A unit finishes training a unit
  • Conditions:
  • Trained unit-type Equal to Footman
  • Actions:
  • Hashtable - Save Key(Triggering unit) as Key(Trained unit) of 3 in Hashtable_Patrol

Then when a footman dies check to see if the Structure linked to it is alive:
  • Events:
  • A unit dies
  • Conditions:
  • Unit-type Equal to Footman
  • Actions:
  • Set Variable Structure = Load Key(Triggering unit) of 3 in Hashtable_Patrol
  • If Structure is Alive Equal to True then do stuff...
Since I wanted the created units to be saved to the building I did it like this (within the spawn trigger --> as key (last created unit))

  • Then - Actions
    • Wait 2.00 seconds
    • Set VariableSet SpawnLoc = (Random point in RandomReg)
    • Set VariableSet Wander_X = (X of SpawnLoc)
    • Set VariableSet Wander_Y = (Y of SpawnLoc)
    • Unit - Create 1 Footman for Player 11 (Dark Green) at SpawnLoc facing Default building facing degrees
    • Custom script: call RemoveLocation (udg_SpawnLoc)
    • Hashtable - Save (Key (Triggering unit).) as (Key (Last created unit).) of 3 in Hashtable_Patrol.
    • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Hashtable_Patrol.
    • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Hashtable_Patrol.
    • Wait 2.00 seconds
    • Set VariableSet SpawnLoc = (Random point in RandomReg)
    • Set VariableSet Wander_X = (X of SpawnLoc)
    • Set VariableSet Wander_Y = (Y of SpawnLoc)
    • Unit - Create 1 Footman for Player 11 (Dark Green) at SpawnLoc facing Default building facing degrees
    • Custom script: call RemoveLocation (udg_SpawnLoc)
    • Hashtable - Save (Key (Triggering unit).) as (Key (Last created unit).) of 3 in Hashtable_Patrol.
    • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Hashtable_Patrol.
    • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Hashtable_Patrol.
    • Wait 2.00 seconds
    • Set VariableSet SpawnLoc = (Random point in RandomReg)
    • Set VariableSet Wander_X = (X of SpawnLoc)
    • Set VariableSet Wander_Y = (Y of SpawnLoc)
    • Unit - Create 1 Footman for Player 11 (Dark Green) at SpawnLoc facing Default building facing degrees
    • Custom script: call RemoveLocation (udg_SpawnLoc)
    • Hashtable - Save (Key (Triggering unit).) as (Key (Last created unit).) of 3 in Hashtable_Patrol.
    • Hashtable - Save Wander_X as (Key (Last created unit).) of 0 in Hashtable_Patrol.
    • Hashtable - Save Wander_Y as (Key (Last created unit).) of 1 in Hashtable_Patrol.
Then I tried to make it work with the trigger you suggested

  • CPU Respawn
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Footman
    • Actions
      • Set VariableSet Structure = (Load (Key (Triggering unit).) of 3 in Hashtable_Patrol.)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ((Load (Key (Triggering unit).) of 3 in Hashtable_Patrol.) is alive) Equal to True
        • Then - Actions
          • Unit - Create 1 Footman for Player 11 (Dark Green) at (Position of (Load (Key (Triggering unit).) of 3 in Hashtable_Patrol.)) facing Default building facing degrees
        • Else - Actions
Unfortunately this doesnt seem to work :/ I guess I can't save the units in that way.


Another thing unrelated to this problem I though about was: Would it be possible to use an array to cover all different types of buildings, that can spawn units? So I do not have to remake this spawning trigger I made for the footmen for every other building, which spawns different units? I d set up 2 Unit Type variables as arrays, one for the structures and one for the corresponding spawned units. How could I implement these into the original spawning trigger, if possible at all?
  • SetUpSpawnArray
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet SpawnArrayUnit[0] = Footman
  • SetUpSpawnArrayStructure
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet SpawnArrayStructure[0] = Farm
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
Make sure you reference the "Structure" variable instead of Loading the Structure 3 times.

But regardless I think the way you have it now should work fine. The only thing I can think of is that Triggering unit is not set to the Constructed Structure?

Also, note that you're leaking a Point when you create that Footman.
 
Level 7
Joined
May 30, 2018
Messages
290
Make sure you reference the "Structure" variable instead of Loading the Structure 3 times.

But regardless I think the way you have it now should work fine. The only thing I can think of is that Triggering unit is not set to the Constructed Structure?

Also, note that you're leaking a Point when you create that Footman.
Still doesnt work. I though triggering unit was set to the structure through this action:
set.gif
Hashtable - Save (Key (Triggering unit).) as (Key (Last created unit).) of 3 in Hashtable_Patrol
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
I got it working and incorporated that array design you wanted.

I use the "shadowing" technique in some of the triggers to make things MUI. You can read about it here: local udg_

I also added the option for you to define how many units spawn and the interval between spawning them.

The main trigger that you would configure is this one:
  • Setup Spawn Hashtable
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Hashtable Notes: --------
      • -------- Parent Key 0 = Spawn Unit --------
      • -------- Parent Key 1 = Spawn Interval --------
      • -------- Parent Key 2 = Spawn Amount --------
      • -------- Parent Key 3 = Spawn Structure (linked to Spawn Unit) --------
      • -------- --------
      • Hashtable - Create a hashtable
      • Set VariableSet SpawnHashtable = (Last created hashtable)
      • -------- --------
      • -------- Structures: --------
      • Set VariableSet SpawnStructures[1] = Farm
      • Set VariableSet SpawnStructures[2] = Scout Tower
      • -------- --------
      • -------- Units: --------
      • Set VariableSet SpawnUnits[1] = Footman
      • Set VariableSet SpawnUnits[2] = Rifleman
      • -------- --------
      • -------- Interval between spawning units: --------
      • Set VariableSet SpawnInterval[1] = 2.00
      • Set VariableSet SpawnInterval[2] = 4.00
      • -------- --------
      • -------- Amount of units spawned: --------
      • Set VariableSet SpawnAmount[1] = 3
      • Set VariableSet SpawnAmount[2] = 2
      • -------- --------
      • -------- Don't forget to increase the Loop range when you add more units ---> from 1 to X, do (Actions) --------
      • -------- So if you had 5 SpawnStructures then you would loop from 1 to 5 --------
      • For each (Integer SpawnLoop) from 1 to 2, do (Actions)
        • Loop - Actions
          • Custom script: set udg_SpawnIdU = udg_SpawnUnits[udg_SpawnLoop]
          • Custom script: set udg_SpawnIdS = udg_SpawnStructures[udg_SpawnLoop]
          • Hashtable - Save SpawnIdU as SpawnIdS of 0 in SpawnHashtable.
          • Hashtable - Save SpawnInterval[SpawnLoop] as SpawnIdS of 1 in SpawnHashtable.
          • Hashtable - Save SpawnAmount[SpawnLoop] as SpawnIdS of 2 in SpawnHashtable.

Note that I edited pretty much every trigger in the attached map. So you'll want to import everything and replace what you already have now.
 

Attachments

  • Wander Example 2.w3m
    23.2 KB · Views: 18
Last edited:
Level 7
Joined
May 30, 2018
Messages
290
I got it working and incorporated that array design you wanted.

I use the "shadowing" technique in some of the triggers to make things MUI. You read about it here: local udg_

I also added the option for you to define how many units spawn and the interval between spawning them.

The main trigger that you would configure is this one:
  • Setup Spawn Hashtable
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Hashtable Notes: --------
      • -------- Parent Key 0 = Spawn Unit --------
      • -------- Parent Key 1 = Spawn Interval --------
      • -------- Parent Key 2 = Spawn Amount --------
      • -------- Parent Key 3 = Spawn Structure (linked to Spawn Unit) --------
      • -------- --------
      • Hashtable - Create a hashtable
      • Set VariableSet SpawnHashtable = (Last created hashtable)
      • -------- --------
      • -------- Structures: --------
      • Set VariableSet SpawnStructures[1] = Farm
      • Set VariableSet SpawnStructures[2] = Scout Tower
      • -------- --------
      • -------- Units: --------
      • Set VariableSet SpawnUnits[1] = Footman
      • Set VariableSet SpawnUnits[2] = Rifleman
      • -------- --------
      • -------- Interval between spawning units: --------
      • Set VariableSet SpawnInterval[1] = 2.00
      • Set VariableSet SpawnInterval[2] = 4.00
      • -------- --------
      • -------- Amount of units spawned: --------
      • Set VariableSet SpawnAmount[1] = 3
      • Set VariableSet SpawnAmount[2] = 2
      • -------- --------
      • -------- Don't forget to increase the Loop range when you add more units ---> from 1 to X, do (Actions) --------
      • -------- So if you had 5 SpawnStructures then you would loop from 1 to 5 --------
      • For each (Integer SpawnLoop) from 1 to 2, do (Actions)
        • Loop - Actions
          • Custom script: set udg_SpawnIdU = udg_SpawnUnits[udg_SpawnLoop]
          • Custom script: set udg_SpawnIdS = udg_SpawnStructures[udg_SpawnLoop]
          • Hashtable - Save SpawnIdU as SpawnIdS of 0 in SpawnHashtable.
          • Hashtable - Save SpawnInterval[SpawnLoop] as SpawnIdS of 1 in SpawnHashtable.
          • Hashtable - Save SpawnAmount[SpawnLoop] as SpawnIdS of 2 in SpawnHashtable.

Note that I edited pretty much every trigger in the attached map. So you'll want to import everything and replace what you already have now.
This system is awesome, exactly what I needed, thanks for the hard work you put in. Iam just kinda sad, that I don't genuinely understand many of the things you used in the triggers. All the custom scripts are very outlandish to me :(. I wish I had a system I could put together myself.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
Yeah, the custom script stuff is needed because GUI (the standard trigger editor stuff that we're using) is limited in what it can do.
Even I was confused when creating this because these limitations force you to use some weird methods.

Anyway, maybe I can clear some things up by explaining the whole process:

So first I needed to save the SpawnStructures and SpawnUnits to the Hashtable so I could "bind" them together. However, if you look at the Hashtable actions, you'll notice that there is no option to save a Unit-Type. You can save a Unit handle, but that's for a specific unit that already exists on the map, not it's TYPE.

So how did I manage to save these? Well, it turns out that Unit-Types are actually Integers in "disguise". This disguise is used in GUI in order to keep things simple for the mapmaker so that they don't have to deal with long integer numbers and confusing triggers. This meant that I could save SpawnStructures and SpawnUnits if I saved them in their Integer form.

This is what you're used to:
  • Set Variable SpawnStructure[1] = Farm
Which is a lot easier to understand than this:
  • Set Variable SpawnStructure[1] = 100043843 <-- this is the id of the Farm unit-type

So in order to get the Integer Id of SpawnStructure[1] and the rest of the Unit-types I used a trick with Custom script and some Integer variables:
  • Custom script: set udg_SpawnIdU = udg_SpawnUnits[1]
  • Custom script: set udg_SpawnIdS = udg_SpawnStructures[1]

What's happening here is that during the first cycle of the Loop, I'm setting the Integer variable SpawnIdU to be equal to SpawnUnits[1] and I'm setting the Integer variable SpawnIdS to be equal to SpawnStructures[1]. This gets me the Integer form of SpawnUnits[] and SpawnStructures[], which I can then save in the Hashtable:
  • Hashtable - Save SpawnIdU as SpawnIdS of 0 in SpawnHashtable.

What I'm really saving is this, which I think will make a lot more sense to you:
  • Hashtable - Save SpawnUnits[1] as SpawnStructures[1] of 0 in SpawnHashtable.
Then during the second cycle of the Loop, I do the same thing but this time for SpawnUnits[2] and SpawnStructures[2]. This will continue on for every single Unit/Structure. I only had 2 of these in my example, but I imagine your map will have many of these Structure/Unit pairings and your Loop will probably go from 1 to 20+.

Additionally, you can see that I save the Spawn Interval and Spawn Amount in the Hashtable:
  • Hashtable - Save SpawnInterval[SpawnLoop] as SpawnIdS of 1 in SpawnHashtable.
  • Hashtable - Save SpawnAmount[SpawnLoop] as SpawnIdS of 2 in SpawnHashtable.
I think these two lines are pretty straightforward. I didn't have to use any Custom script tricks here because you can already save Integers/Reals to the Hashtable with no extra steps needed. I save this data directly to SpawnIdS, which is our SpawnStructure.

So in other words, here's what I'm saving to the Hashtable:
Save Footman as Farm of index 0
Save 2.00 as Farm of index 1
Save 3 as Farm of index 2


Then for our second pair of units:
Save Rifleman as Scout Tower of index 0
Save 4.00 as Scout Tower of index 1
Save 2 as Scout Tower of index 2


So now whenever I want to access this information all I have to do is plug in the Structure's unit-type id (SpawnIdS) into the Hashtable and reference either index 0, 1, or 2. Depending on which index I reference, I'll get either:
Index 0 = SpawnUnit
Index 1 = SpawnInterval
Index 2= SpawnAmount

You can see this being done in the Create Structure trigger:
  • Create Structure
    • Events
    • Unit - A unit Finishes construction
    • Actions
    • Custom script: set udg_SpawnIdS = GetUnitTypeId(GetTriggerUnit())
    • Custom script: set udg_SpawnIdU = LoadInteger(udg_SpawnHashtable, 0, udg_SpawnIdS)

^Here I am setting SpawnIdS to the Unit-type id of our Constructed structure. GUI doesn't have a function for getting a unit's Unit-Type id so I need to use Custom script (Jass) in order to do it. GetUnitTypeId() is a function that takes a unit and then returns you it's unit-type id. GetTriggerUnit() is the same thing as Triggering Unit, so I'm getting the Unit-Type Id of the Triggering Unit and setting it as SpawnIdS.

I then use this Unit-type id to try and Load the SpawnUnit that it is bound to in the Hashtable.

What I'm really doing is something like this:
  • Set Variable SpawnIdS = The SpawnStructure[] (Farm for example)
  • Set Variable SpawnIdU = The associated SpawnUnit[] (Footman for example)

I also use a Condition to make sure that we don't do anything if the Constructed structure isn't part of the Spawn system:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • SpawnIdU Not equal to 0
The default Integer value in a Hashtable is always 0, so if I try loading let's say a Castle from the Spawn Hashtable, it will give me a value of 0 because the Castle's integer id was never added to the Hashtable. So it loads this default value of 0 instead.

Finally, if the Constructed structure is indeed part of the Spawn System then I proceed to the Spawn Units trigger and actually create the units. That trigger is the most confusing one of them all. Basically, I use the same concept from before with SpawnIdU and SpawnIdS in order to Load the correct values from the Hashtable. I then use those values to determine which Unit should spawn, how many of them, and the interval between each spawn. I also use a trick called Shadowing which lets me turn global variables into local variables. Local variables have the perk of being MUI, which in this case means that you can safely use them after WAIT actions without worrying about their values getting changed/lost. Combining these techniques together I am able to make a Loop that runs SpawnAmount times, creating our SpawnUnit each time, and Waiting SpawnInterval seconds between each cycle of the Loop.
 
Last edited:
Level 7
Joined
May 30, 2018
Messages
290
Yeah, the custom script stuff is needed because GUI (the standard trigger editor stuff that we're using) is limited in what it can do.
Even I was confused when creating this because it's just so ugly/messy to look at.

Anyway, maybe I can clear some things up by explaining the whole process:

So first I needed to save the SpawnStructures and SpawnUnits to the Hashtable so I could "bind" them together. However, if you look at the Hashtable actions, you'll notice that there is no option to save a Unit-Type. You can save a Unit handle, but that's for a specific unit that already exists on the map, not it's TYPE.

So how did I manage to save these? Well, it turns out that Unit-Types are actually Integers in "disguise". This disguise is used in GUI in order to keep things simple for the mapmaker so that they don't have to deal with long integer numbers and confusing triggers. This meant that I could save SpawnStructures and SpawnUnits if I saved them in their Integer form.

This is what you're used to:
  • Set Variable SpawnStructure[1] = Farm
Which is a lot easier to understand than this:
  • Set Variable SpawnStructure[1] = 100043843 <-- this is the id of the Farm unit-type

So in order to get the Integer Id of SpawnStructure[1] and the rest of the Unit-types I used a trick with Custom script and some Integer variables:
  • Custom script: set udg_SpawnIdU = udg_SpawnUnits[udg_SpawnLoop]
  • Custom script: set udg_SpawnIdS = udg_SpawnStructures[udg_SpawnLoop]

What's happening here is that during the first cycle of the Loop, I'm setting the Integer variable SpawnIdU to be equal to SpawnUnits[1] and I'm setting the Integer variable SpawnIdS to be equal to SpawnStructures[1]. This gets me the Integer form of SpawnUnits[] and SpawnStructures[], which I can then save in the Hashtable:
  • Hashtable - Save SpawnIdU as SpawnIdS of 0 in SpawnHashtable.

What I'm really saving is this, which I think will make a lot more sense to you:
  • Hashtable - Save SpawnUnits[1] as SpawnStructures[1] of 0 in SpawnHashtable.
Then during the second cycle of the Loop, I do the same thing but this time for SpawnUnits[2] and SpawnStructures[2]. This will continue on for every single Unit/Structure. I only had 2 of these in my example, but I imagine your map will have many of these Structure/Unit pairings and your Loop will probably go from 1 to 20+.

Additionally, you can see that I save the Spawn Interval and Spawn Amount in the Hashtable:
  • Hashtable - Save SpawnInterval[SpawnLoop] as SpawnIdS of 1 in SpawnHashtable.
  • Hashtable - Save SpawnAmount[SpawnLoop] as SpawnIdS of 2 in SpawnHashtable.
I think these two lines are pretty straightforward. I didn't have to use any Custom script tricks here because you can already save Integers/Reals to the Hashtable with no extra steps needed. I save this data directly to SpawnIdS, which is our SpawnStructure.

So in other words, here's what I'm saving to the Hashtable:
Save Footman as Farm of index 0
Save 2.00 as Farm of index 1
Save 3 as Farm of index 2


Then for our second pair of units:
Save Rifleman as Scout Tower of index 0
Save 4.00 as Scout Tower of index 1
Save 2 as Scout Tower of index 2


So now whenever I want to access this information all I have to do is plug in the Structure's integer id into the Hashtable and reference either index 0, 1, or 2. Depending on which index I reference, I'll get either:
Index 0 = SpawnUnit
Index 1 = SpawnInterval
Index 2= SpawnAmount

You can see this being done in the Create Structure trigger:
  • Create Structure
    • Events
    • Unit - A unit Finishes construction
    • Actions
    • Custom script: set udg_SpawnIdS = GetUnitTypeId(GetTriggerUnit())
    • Custom script: set udg_SpawnIdU = LoadInteger(udg_SpawnHashtable, 0, udg_SpawnIdS)

Here I am setting SpawnIdS to the Unit-type id of our Constructed structure. I then use this Unit-type id to try and Load the SpawnUnit that it is bound to in the Hashtable.

What I'm really doing is something like this:
  • Set Variable SpawnIdS = The SpawnStructure[] (Farm for example)
  • Set Variable SpawnIdU = The associated SpawnUnit[] (Footman for example)

I also use a Condition to make sure that we don't do anything if the Constructed structure isn't part of the Spawn system:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • SpawnIdU Not equal to 0
The default Integer value in a Hashtable is always 0, so if I try loading let's say a Castle from the Spawn Hashtable, it will give me a value of 0 because the Castle was never added to the Hashtable. So it just loads this default value instead.

Finally, if the Constructed structure is indeed part of the Spawn System then I proceed to the Spawn Units trigger and actually create the units. That trigger is the most confusing one of them all. Basically, I use the same concept from before with SpawnIdU and SpawnIdS in order to Load the correct values from the Hashtable. I then use those values to determine which Unit should spawn, how many of them, and the interval between each spawn. I also use a trick called Shadowing which lets me turn global variables into local variables. Local variables have the perk of being MUI, which in this case means that you can safely use them after WAIT actions without worrying about their values getting changed/lost. Combining these techniques together I am able to make a Loop that runs SpawnAmount times, creating our SpawnUnit each time, and Waiting SpawnInterval seconds between each cycle of the Loop.
Hey man. Thank you so much for taking your precious time explaining everything to me. It sure helped me understand the system better. Naturally I still won't be able to reproduce something of that caliber myself, but at least I kinda understood what everything is supposed to do and thats also a small win in my book :)!

Thanks anyways for the outstanding support, very appreciated!
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,511
No problem, I understand if it's a lot to take in. It took me a lot of tinkering with the editor until it finally all clicked.

I'll try not to go into too great of detail but everything starts to make more sense once you understand how things are stored in the game.
Maybe this is a weird analogy, but think of everything in the Object Editor as a big grocery list that's keeping track of things:
#1 = Footman
#2 = Knight
#3 = Rifleman
#548 = Flame Strike
#1024 = Boots of Speed
etc...

EVERYTHING in the Object Editor is on this "grocery list" and is stored under a unique integer id. This id is what's used to differentiate "objects" from one another. Note that the numbers are a lot longer than what I wrote in my example and will look seemingly random.

You're actually generating a unique Integer id whenever you create a new object (Unit/Item/Ability/etc). I'm sure you've noticed when the editor asks you for that 4 character rawcode. It'll usually say something like "U000", and if the code is already taken then you have to come up with a new one.

That rawcode is actually an integer too, it's basically a shortened version of the object's id. So "Hpal", the rawcode for the Paladin, actually represents a much longer Integer id like 1000023948 which in this case is the Paladin's Unit-Type id. No other object in the game will have this id. This is what allows the game to know what's what. "That's obviously the Paladin because nothing else has that same id". Just like how no two people share the same phone number or social security number since that wouldn't work for obvious reasons. Most of the editor hides this information from you because it's much easier to work with a Graphical User Interface (GUI) that has buttons/icons like the Paladin's face instead of long confusing integers. We're not machines after all, an icon of the Paladin's face is a lot easier to work with than what would appear to be a seemingly random number like 1000023948.

So with this in mind hopefully the Hashtable stuff will start to make sense. First I'm storing SpawnStructure/SpawnUnit in Unit-Type Arrays. Then I'm using a For Loop to easily iterate over the arrays and Save all of their data into the Hashtable. But you can't save Unit-Type variables to the Hashtable, so instead I have to get and save their Unit-type ids. That's what I'm doing when I use the Custom script with SpawnIdU and SpawnIdS, I'm getting the Integer form of the Unit-Type Array.

All of this works out because I can get a unit's Unit-Type id at any time using the GetUnitTypeId() function. So as long as you have access to a unit, you also have access to it's Unit-Type id. So when the player finishes constructing a Farm, I use that Get function to get the Farm's Unit-type id (some long integer like 101231234) and then I Load the data that was saved under that id from the Hashtable. This gets me access to whatever I saved there, which differs depending on which Index (aka Child key) that I choose to Load from (0, 1, 2, etc). In this case it can Load either the Farm's SpawnUnit, SpawnInterval, or SpawnAmount.
 
Last edited:
Level 7
Joined
May 30, 2018
Messages
290
No problem, I understand if it's a lot to take in. It took me a lot of tinkering with the editor until it finally all clicked.

I'll try not to go into too great of detail but everything starts to make more sense once you understand how things are stored in the game.
Maybe this is a weird analogy, but think of everything in the Object Editor as a big grocery list that's keeping track of things:
#1 = Footman
#2 = Knight
#3 = Rifleman
#548 = Flame Strike
#1024 = Boots of Speed
etc...

EVERYTHING in the Object Editor is on this "grocery list" and is stored under a unique integer id. This id is what's used to differentiate "objects" from one another. Note that the numbers are a lot longer than what I wrote in my example and will look seemingly random.

You're actually generating a unique Integer id whenever you create a new object (Unit/Item/Ability/etc). I'm sure you've noticed when the editor asks you for that 4 character rawcode. It'll usually say something like "U000", and if the code is already taken then you have to come up with a new one.

That rawcode is actually an integer too, it's basically a shortened version of the object's id. So "Hpal", the rawcode for the Paladin, actually represents a much longer Integer id like 1000023948 which in this case is the Paladin's Unit-Type id. No other object in the game will have this id. This is what allows the game to know, okay, that's obviously the Paladin because nothing else has that id. Just like how no two people share the same phone number or social security number since that wouldn't work for obvious reasons. Most of the editor hides this information from you because it's much easier to work with a Graphical User Interface (GUI) that has buttons/icons like the Paladin's face instead of long confusing integers. We're not machines after all, an icon of the Paladin's face is a lot easier to work with than 1000023948.

So with this in mind hopefully the Hashtable stuff will start to make sense. First I'm storing SpawnStructure/SpawnUnit in Unit-Type Arrays. Then I'm using a For Loop to easily iterate over the arrays and Save all of their data into the Hashtable. But you can't save Unit-Type variables to the Hashtable, so I have to get and save their Unit-type ids instead. That's what I'm doing when I use the Custom script with SpawnIdU and SpawnIdS.

All of this works out because I can get a unit's Unit-Type id at any time using the GetUnitTypeId() function. So as long as you have access to a unit, you also have access to it's Unit-Type id. So when the player finishes constructing a Farm, I use that Get function to get the Farm's Unit-type id (some long integer like 101231234) and then I Load the data that was saved under that id from the Hashtable. This gets me access to whatever I saved there, which differs depending on which Index (aka Child key) that I choose to Load from (0, 1, 2, etc). In this case it can Load either the Farm's SpawnUnit, SpawnInterval, or SpawnAmount.
Well explained. I have to thank you!
 
Status
Not open for further replies.
Top