- Joined
- Apr 4, 2011
- Messages
- 995
A Complete Beginners Guide to Hashtables
Written because even after reading all the hashtable tutorials on this site, I still had questions.
Table of Contents
- Requirements to Fully Appreciate this Tutorial
- Hashtables and You
- Handles and Hashtables
- Useful Examples of the Hashtable in Action
- Miscellaneous Hashtable Information
- Credits
Requirements to Fully Appreciate this Tutorial
- Basic knowledge of GUI scripting
- The ability to follow directions
- Full comprehension of the English language
- A desire to learn
- Patience
Hashtables and You
What is a Hashtable?
Hashtables are data structures. A variable array works by storing multiple pieces of data, imagine it as the X axis in a graph. Now imagine a variable array that is complicated enough to store data not just along the X axis, but also the Y. This allows us to have a much more varied variable (pardon the wordplay) Hashtables work by saving the value of the variable until you need it later. It then loads the value of the variable so it can be used with the exact same data as before.Why use a Hashtable?
Simply put, hashtables are easy ways to make your spells MUI. Spells that are not MUI will immediately be rejected from the Spells section of THW. Unless you only have a single instance of a unit on your map, not being MUI will cause multiple casts of the spell to malfunction. Hashtables have allowed me to advance worlds ahead of were I was when I started triggering. They are open ended and any spell that isn't instant will need hashtables or recycling.At the same time, using hashtables has its drawbacks. It is sometimes quite annoying to have to load every value, and if you make a mistake in a spell it can be quite a long time before you realize you didn't save or load a value correctly. Hashtables are also slower than arrays.
Now let's look at how to make a hashtable.
-
Events
- Map initialization
- Conditions
-
Actions
- Hashtable - Create a hashtable
- Set EX_Hash = (Last created hashtable)
What does that Trigger Above Mean?
Broken down, the trigger means that at the start of the map, a hashtable will be created. The hashtable will be assigned the variable EX_Hash. Most hashtables are going to be created at the start of the map. You need to assign the hashtable a variable name. This is because you will probably have multiple hashtables. If you import a spell or a system, it will probably have a hashtable associated with it. If you assign your variables to (last created hashtable) instead of your hashtable variable, you may accidently load a value from a different hashtable!Handles and Hashtables
What is a Handle?
A handle is anything that is not a real, boolean, integer, or string. Handles can be saved with hashtables, allowing us to extract exact information portaining to that handle whenever we want to. Simply put, it's like saving a new variable for each handle, even if the variable is changed later, we can load the value of whatever the handle is and still have the original starting variable.Here is an example of a hashtable without handles. While it is still useful, it can be even better with them
-
Events
- Map initialization
- Conditions
-
Actions
- Hashtable - Create a hashtable
- Set EX_Hash = (Last created hashtable)
- Events
- Conditions
-
Actions
- Set EX_Real = 20.00
- Hashtable - Save EX_Real as 1 of 1 in EX_Hash
- Set EX_Real = 4566.00
- Set EX_Real = 12345.00
- Set EX_Real = 100000000.00
- Set EX_Real = (Load 1 of 1 from EX_Hash)
In this example, when the even happens, the real number 20 will be saved into value 1 of 1 in the hashtable EX_Hash. Afterwards, you can change the variable however you like, and it will not affect the value 1 of 1 in the hashtable. You can load the value after changing EX_Real, and the loaded value will be what you first saved it as, in this example the number is 20.
How can we use Handles Effectively?
Handles are used in conjunction with hashtables. A handle describes the value being saved. If you were saving EX_Real to the triggering unit of an ability, you change it to say (key (triggering unit)). Key simply is a way of identifying handles. Let's look below at a simple damage over time spell that uses handles to correctly damage every unit targetted. Please note that leaks will be addressed fully in this example.-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Damage Over Time
-
Actions
- Set EX_Target = (Target unit of ability being cast)
- Set EX_Damage = 20.00
- Set EX_Timer = 5.00
- Unit Group - Add (Target unit of ability being cast) to EX_Group
- Hashtable - Save EX_Timer as 1 of (Key (Target unit of ability being cast)) in EX_Hash
-
Events
- Time - Every 1.00 seconds of game time
- Conditions
-
Actions
-
Unit Group - Pick every unit in EX_Group and do (Actions)
-
Loop - Actions
- Set EX_Timer = (Load 1 of (Key (Picked unit)) from EX_Hash)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- EX_Timer Greater than 0.00
-
Then - Actions
- Unit - Set life of (Picked unit) to ((Life of (Picked unit)) - EX_Damage)
- Hashtable - Save (EX_Timer - 1.00) as 1 of (Key (Picked unit)) in EX_Hash
-
Else - Actions
- Unit Group - Remove (Picked unit) from EX_Group
- Hashtable - Clear all child hashtables of child (Key (Picked unit)) in EX_Hash
-
If - Conditions
-
Loop - Actions
-
Unit Group - Pick every unit in EX_Group and do (Actions)
This is a lot to look at. Spend a minute or two trying to roughly get the idea of what is happening in this spell. The targetted unit of the ability "Damage Over Time" is being added to the group "EX_Group". Every second, members of the group are checked to see how much time they have left in the spell. If they are not done with the duration of the spell, they suffer twenty damage. If they are done with the duration of the spell, they are removed from the damage group, and the child hashtables are cleared. This means that all that extra data solely belonging to the picked unit is destroyed. It is the hashtable equivelent of destroying memory leaks. Always make sure to include it at the end of spells. The absolute most important thing to pay attention to right now is how the hashtable values are saved. They correspond to how you identify the unit. EX_Timer is saved in the first part as (key (target unit of ability being cast)). At the second trigger it is being referenced to with (key (picked unit)). This is because the unit is being selected as picked unit instead of target unit of ability being cast. This difference in handles is crucial to understanding the beast known as the hashtable. If you are going to use a trigger like this, please make it more efficient by assigning unit references (picked unit, for example) to variables and then calling them.
Quick guide for recreating this trigger
Quick guide for recreating this trigger
- EX_Timer is a real
- EX_Group is a unit group
- EX_Damage is a real
Where can Handles Take You?
Handles are quite possibly the most important parts of hashtables. It allows it to be MUI, and it gives hashtables their dynamic quality. You must learn to effectively use handles if you want your spells to be MUI.Set udg_VARIABLE = GetHandleId(udg_UNIT)
A great little line, this baby is highly important to hashtables. Used with custom script, this will automatically pick the correct key corresponding to the unit. In the custom script, VARIABLE will be the name of an Integer variable, while UNIT will be the name of the unit variable. Look below for an example of how to use this.- Set Unit = (Picked unit)
- Custom script: set udg_Handle = GetHandleId(udg_Unit)
- -------- Handle is my integer variable, Unit is my unit variable --------
- Hashtable - Save Timer as 1 of Handle in Hash
Useful Examples of the Hashtable in Action
Please note that triggers are in the third section. Both spells are by Jazztastic, please give credit if used. Main purpose of these is to show examples of how hashtables and handles can be used effectively in spells
Tank Attack!, a Spell With Movement
Most spells use dummy units to deal damage and create visual effects for the spell. "Movement" is a term I use when talking about physically moving the dummy unit to help create effects for a spell.Provided for you is the trigger to "Tank Attack!", a spell that creates a horde of miniature tanks to run over your enemies. Tanks are created every 0.04 seconds. 25 tanks are created over 1 second.
Missile Launch, a Spell With Knockback
Knockback is when a spell causes a unit to slide backwards from its spot. It is effectively stunned because it can take no actions. Knockback is a desireable effect to have in a map because it looks nice, and is more interesting than just a stun.Provided for you is the trigger to "Missile Launch", a spell that shoots a missile at target location. Upon coming within range of a unit, the missile explodes, damaging all units in X range and knocking them back a random distance between X and X. It is similar to Tank Attack! in that it uses movement, but I consider this a step up from Tank Attack!. I recommend using imported explosions for the explosion effects.
-
Variables
-
Events
- Map initialization
- Conditions
-
Actions
- -------- ------------------- --------
- -------- Tank Attack --------
- -------- ------------------- --------
- Hashtable - Create a hashtable
- Set TA_Hash = (Last created hashtable)
- Set TA_Aoe = 100.00
- Set TA_Damage = 1.00
- -------- ------------------- --------
- -------- Missile Launch --------
- -------- ------------------- --------
- Hashtable - Create a hashtable
- Set ML_Hash = (Last created hashtable)
- Set ML_Aoe = 120.00
-
Events
I personally like to keep my hashtables seperate for my spells. It is not necessary, but I recommend it to help keep track of your stuff. Also, a few variables that only need to be loaded once are present in this map init trigger
-
Tank Attack Cast
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Copter Attack!
-
Actions
- Set TA_Caster = (Triggering unit)
- Sound - Play SteamTankBeep <gen>
- Set TA_Level = (Level of (Ability being cast) for TA_Caster)
- Set TA_Target = (Target point of ability being cast)
- Set TA_Time = 1.00
- Unit Group - Add TA_Caster to TA_Group1
- Hashtable - Save TA_Level as 1 of (Key (Triggering unit)) in TA_Hash
- Hashtable - Save TA_Time as 2 of (Key (Triggering unit)) in TA_Hash
- Custom script: call RemoveLocation(udg_TA_Target)
- Trigger - Turn on Tank Attack Loop <gen>
-
Events
-
Tank Attack Loop
-
Events
- Time - Every 0.04 seconds of game time
- Conditions
-
Actions
-
Unit Group - Pick every unit in TA_Group1 and do (Actions)
-
Loop - Actions
- Set TA_Level = (Load 1 of (Key (Picked unit)) from TA_Hash)
- Set TA_Time = (Load 2 of (Key (Picked unit)) from TA_Hash)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- TA_Time Greater than 0.00
-
Then - Actions
- Set TA_Caster = (Picked unit)
- Set TA_Point1 = (Position of TA_Caster)
- Unit - Create 1 Mini Tank for (Owner of TA_Caster) at TA_Point1 facing ((Facing of TA_Caster) + (Random real number between -25.00 and 25.00)) degrees
- Set TA_Tank = (Last created unit)
- Unit - Add a 2.00 second Generic expiration timer to TA_Tank
- Unit - Make TA_Tank Explode on death
- Unit Group - Add TA_Tank to TA_Group2
- Hashtable - Save TA_Level as 1 of (Key (Last created unit)) in TA_Hash
- Hashtable - Save (TA_Time - 0.04) as 2 of (Key (Picked unit)) in TA_Hash
- Custom script: call RemoveLocation(udg_TA_Point1)
- Set TA_Loops = (TA_Loops + 1)
-
Else - Actions
- Hashtable - Clear all child hashtables of child (Key (Picked unit)) in TA_Hash
- Unit Group - Remove (Picked unit) from TA_Group1
-
If - Conditions
-
Loop - Actions
-
Unit Group - Pick every unit in TA_Group2 and do (Actions)
-
Loop - Actions
- Sound - Play SteamTankMovement <gen>
- Set TA_Level = (Load 1 of (Key (Picked unit)) from TA_Hash)
- Set TA_Tank = (Picked unit)
- Set TA_Point1 = (Position of TA_Tank)
- Set TA_Point2 = (TA_Point1 offset by 25.00 towards (Facing of TA_Tank) degrees)
- Unit - Move TA_Tank instantly to TA_Point2
- Custom script: set bj_wantDestroyGroup = true
-
Unit Group - Pick every unit in (Units within TA_Aoe of TA_Point2 matching (((Matching unit) belongs to an enemy of (Owner of TA_Tank)) Equal to True)) and do (Actions)
-
Loop - Actions
- Special Effect - Create a special effect attached to the chest of (Triggering unit) using Objects\Spawnmodels\Human\HumanBlood\BloodElfSpellThiefBlood.mdl
- Special Effect - Destroy (Last created special effect)
- Unit - Cause TA_Tank to damage (Picked unit), dealing (TA_Damage x (Real(TA_Level))) damage of attack type Spells and damage type Normal
-
Loop - Actions
-
Loop - Actions
-
Unit Group - Pick every unit in TA_Group1 and do (Actions)
-
Events
-
Tank Attack End
-
Events
- Unit - A unit Dies
-
Conditions
- (Unit-type of (Triggering unit)) Equal to Mini Tank
-
Actions
- Unit Group - Remove (Triggering unit) from TA_Group2
- Hashtable - Clear all child hashtables of child (Key (Triggering unit)) in TA_Hash
- Set TA_Loops = (TA_Loops - 1)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- TA_Loops Less than or equal to 0
-
Then - Actions
- Trigger - Turn off (This trigger)
- Else - Actions
-
If - Conditions
-
Events
In the first trigger, notice the sound being played. You will have to use the sound editor to get this sound from the game. In the second trigger it is important to notice that both the movement for the units and their creation run off the same timer.
Quick guide for recreating this trigger
Quick guide for recreating this trigger
- TA_Angle is a real
- TA_Aoe is a real
- TA_Caster is a unit
- TA_Tank is a unit
- TA_Damage is a real
- TA_Group1 is a unit group
- TA_Group2 is a unit group
- TA_Hash is a hashtable
- TA_Level is an integer
- TA_Point1 is a point
- TA_Point2 is a point
- TA_Point3 is a point
- TA_Target is a point
- TA_Timer is a real
- TA_Loops is an integer
Following are the triggers for Missile Launch
-
Missile Launch Cast
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Missile Launch
-
Actions
- Set ML_Caster = (Triggering unit)
- Set ML_Point1 = (Position of ML_Caster)
- Unit - Create 1 Missile for (Triggering player) at ML_Point1 facing (Facing of ML_Caster) degrees
- Unit Group - Add (Last created unit) to ML_Group
- Set ML_Timer = 2.00
- Hashtable - Save ML_Timer as 1 of (Key (Last created unit)) in ML_Hash
- Trigger - Turn on Missile Launch Loop <gen>
- Custom script: call RemoveLocation(udg_ML_Point1)
-
Events
-
Missile Launch Loop
-
Events
- Time - Every 0.04 seconds of game time
- Conditions
-
Actions
-
Unit Group - Pick every unit in ML_Group and do (Actions)
-
Loop - Actions
- Set ML_Timer = (Load 1 of (Key (Picked unit)) from ML_Hash)
- Set ML_Missile = (Picked unit)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- ML_Timer Greater than 0.00
-
Then - Actions
- Hashtable - Save (ML_Timer - 0.04) as 1 of (Key (Picked unit)) in ML_Hash
- Set ML_Point1 = (Position of ML_Missile)
- Set ML_Point2 = (ML_Point1 offset by 30.00 towards (Facing of ML_Missile) degrees)
- Unit - Move ML_Missile instantly to ML_Point2
- Custom script: set bj_wantDestroyGroup = true
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- (Number of units in (Units within ML_Aoe of ML_Point2 matching (((Matching unit) belongs to an enemy of (Owner of ML_Missile)) Equal to True))) Equal to 0
-
Then - Actions
- Custom script: set bj_wantDestroyGroup = true
-
Unit Group - Pick every unit in (Units within 300.00 of ML_Point2 matching (((Matching unit) belongs to an enemy of (Owner of ML_Missile)) Equal to True)) and do (Actions)
-
Loop - Actions
- Unit - Cause ML_Missile to damage (Picked unit), dealing 250.00 damage of attack type Siege and damage type Demolition
- Unit Group - Add (Picked unit) to ML_Group2
- Set ML_Point3 = (Position of (Picked unit))
- Set ML_Angle = (Angle from ML_Point2 to ML_Point3)
- Set ML_Timer = 1.40
- Set ML_Distance = ((Random real number between 50.00 and 150.00) x 0.04)
- Hashtable - Save ML_Timer as 1 of (Key (Picked unit)) in ML_Hash
- Hashtable - Save ML_Angle as 2 of (Key (Picked unit)) in ML_Hash
- Hashtable - Save ML_Distance as 3 of (Key (Picked unit)) in ML_Hash
- Set ML_Loops = (ML_Loops + 1)
- Custom script: call RemoveLocation(udg_ML_Point3)
-
Loop - Actions
- Unit - Explode ML_Missile
- Special Effect - Create a special effect at ML_Point2 using war3mapImported\ExplosionBIG.mdx
- Special Effect - Destroy (Last created special effect)
- Set ML_Timer = 0.00
- Hashtable - Save ML_Timer as 1 of (Key (Picked unit)) in ML_Hash
- Else - Actions
-
If - Conditions
- Custom script: call RemoveLocation(udg_ML_Point1)
- Custom script: call RemoveLocation(udg_ML_Point2)
-
Else - Actions
- Hashtable - Clear all child hashtables of child (Key (Picked unit)) in TA_Hash
- Unit - Explode ML_Missile
- Unit Group - Remove ML_Missile from ML_Group
-
If - Conditions
-
Loop - Actions
-
Unit Group - Pick every unit in ML_Group2 and do (Actions)
-
Loop - Actions
- Set ML_KnockBackUnit = (Picked unit)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- (ML_KnockBackUnit is alive) Equal to True
-
Then - Actions
- Set ML_Timer = (Load 1 of (Key (Picked unit)) from ML_Hash)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- ML_Timer Greater than 0.00
-
Then - Actions
- Hashtable - Save (ML_Timer - 0.04) as 1 of (Key (Picked unit)) in ML_Hash
- Set ML_Angle = (Load 2 of (Key (Picked unit)) from ML_Hash)
- Set ML_Distance = (Load 3 of (Key (Picked unit)) from ML_Hash)
- Set ML_Point1 = (Position of ML_KnockBackUnit)
- Set ML_Point2 = (ML_Point1 offset by ML_Distance towards ML_Angle degrees)
- Unit - Move ML_KnockBackUnit instantly to ML_Point2
- Special Effect - Create a special effect attached to the origin of ML_KnockBackUnit using Abilities\Spells\Human\FlakCannons\FlakTarget.mdl
- Special Effect - Destroy (Last created special effect)
- Unit - Order ML_KnockBackUnit to Stop
-
Else - Actions
- Hashtable - Clear all child hashtables of child (Key (Picked unit)) in ML_Hash
- Unit Group - Remove (Picked unit) from ML_Group2
- Set ML_Loops = (ML_Loops - 1)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- ML_Loops Less than or equal to 0
-
Then - Actions
- Trigger - Turn off (This trigger)
- Else - Actions
-
If - Conditions
-
If - Conditions
-
Else - Actions
- Hashtable - Clear all child hashtables of child (Key (Picked unit)) in ML_Hash
- Unit Group - Remove ML_KnockBackUnit from ML_Group2
- Set ML_Loops = (ML_Loops - 1)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- ML_Loops Less than or equal to 0
-
Then - Actions
- Trigger - Turn off (This trigger)
- Else - Actions
-
If - Conditions
-
If - Conditions
-
Loop - Actions
-
Unit Group - Pick every unit in ML_Group and do (Actions)
-
Events
Quick guide for recreating this trigger
- ML_Angle is a real
- ML_Aoe is a real
- ML_Caster is a unit
- ML_Distance is a real
- ML_Group1 is a unit group
- ML_Group2 is a unit group
- ML_Hash is a hashtable
- ML_KnockBackUnit is a unit
- ML_Missile is a unit
- ML_Point1 is a point
- ML_Point2 is a point
- ML_Point3 is a point
- ML_Timer is a real
- ML_Loops is an integer
Miscellaneous Hashtable Information
- You are only allowed to have 256 hashtables
- Having the creation of the hashtable in the trigger will not only make it innefficient, it will also make it NOT MUI
- Multiple spells can use the same hashtable. You should have no more than 10 hashtables in your map
- Hashtables are also useful for systems, not just spells
Credits
I would like to thank Wyrmlord for his awesome tutorial on hashtables. It helped get me going, but I didn't think it was clear enough, so heres mine I would also like to thank Dr. SuperGood and Maker for some information. Finally, I would like to thank Archangel for some trigger corrections. Please comment and rate, I like feedback.
Change Log
v1.0 | Uploaded tutorial to The Hive. |
v1.1 | Fixed incorrect information, fixed example triggers, added Change Log and Future Changes to Come sections. |
v1.2 | Put [goto] and |
Last edited: