- Joined
- Oct 13, 2005
- Messages
- 233
Before starting this tutorial, you should be comfortable with using GUI. This tutorial was not made to target those just starting out with GUI and may be confusing for those that are.
Since you might still not understand its usefulness, here's an example of a spell that heals 10 life per second for 10 seconds:
While I'm at it, please use "Unit - A unit Starts the effect of an ability" instead of "Unit - A unit Begins casting an ability".
This might be more work than using a bunch of waits inside a single trigger, but this way of doing things is completely MUI.
Now, another popular example. We're going to make a single target knockback spell that uses Hashtables. It will work just like the HealOverTime trigger because it will also use a group to hold all the units being knocked back. The difference is that more values will be stored in the hashTable and of course the unit will be knocked back instead of healed.
Here's another thing to keep in mind. Unless you handle your values carefully, spells that use the same hashtable can easily overwrite values in other spells. So, I recommend creating a hashtable global for each spell that needs to use it unless you know what you are doing and can prevent conflicts. Anyways, here are the knockback functions:
While remembering each key in an example like this isn't tricky, sometimes with larger systems they can become mixed up. There's a handy function that will get a String ID of a string just like you can get the Handle ID of a unit. So instead of using 0 for distance, we could have gotten the String ID of "distance". Here's what the first trigger might look like if we used String IDs instead:
As a final note, these triggers are merely examples. As you may or may have not noticed, the knockback trigger would require memory leak fixing since it leaks 25 locations each second for every unit that is being knocked backward. I intentionally left the triggers how they are so they would be easier to understand. Those who wish to use these kinds of triggers in their maps should make sure to fix any memory leaks that were not cleaned up in the examples.
What is a hashtable?
For starters, it's a data structure capable of holding almost any kind of data in Wc3. It can hold integers, unit, special effects, and anything else you would ever need to store.How do we use it?
First thing is first, we need to create a hashtable in order to use it. While we're at it, we'll use a variable to keep track of it.-
Init
-
Events
- Map initialization
- Conditions
-
Actions
- Hashtable - Create a hashtable
- Set hashTable = (Last created hashtable)
-
Events
-
Example
- Events
- Conditions
-
Actions
- -------- Store the value --------
- Hashtable - Save 5.00 as 0 of 0 in hashTable
- -------- Load the value --------
- Set LoadedValue = (Load 0 of 0 from hashTable)
- -------- Will show the text: 5.00 --------
- Game - Display to (All players) the text: (String(LoadedValue))
Why are Hashtables Useful?
I know what you're thinking. "If hashtables are just giant arrays, why do we need them?" Hashtables by themselves are not terribly useful, but when we use them with Handle IDs they can actually be incredibly useful. They allow us to "attach" data to handles that can be retrieved later. We're going to cover this now.What is a Handle?
For those needing it, here's a simple overview of what a handle is. There are many types in Warcraft 3: integers, reals, strings, units, players, items, special effects, etc. A handle is any type that is not either an integer, real, boolean, or string. An example of some commonly used handles are: units, destructables, items, players, special effects, floating text, and locations (referred to as "points" in GUI). There are many, many more handles than the list I gave, but these are just some of the more commonly used ones.Handle IDs and why they're useful
This, although you might not realize it now, is incredibly useful when combined with Hashtables. With the new patch, you are now able to get the Handle ID (it's an integer) of things such as units. As far as you should be concerned, these IDs are unique to each handle. Now, as previously mentioned, hashtables require two integers when storing data inside of it. One of these integers could easily be the Handle ID of a unit that's part of a MUI spell.Since you might still not understand its usefulness, here's an example of a spell that heals 10 life per second for 10 seconds:
While I'm at it, please use "Unit - A unit Starts the effect of an ability" instead of "Unit - A unit Begins casting an ability".
-
Start
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Heal
-
Actions
- -------- Store the duration in the Hashtable under the unit's Handle ID --------
- Hashtable - Save 10.00 as 0 of (Key (Target unit of ability being cast)) in hashTable
- -------- Add the unit to the group of units that are being healed over time --------
- Unit Group - Add (Target unit of ability being cast) to HealOverTimeUnits
-
Events
-
Healing
-
Events
- Time - Every 1.00 seconds of game time
- Conditions
-
Actions
-
Unit Group - Pick every unit in HealOverTimeUnits and do (Actions)
-
Loop - Actions
- -------- Get the remaining time in seconds --------
- Set RemainingTime = (Load 0 of (Key (Picked unit)) from hashTable)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- RemainingTime Greater than 0.00
-
Then - Actions
- -------- Heal the unit and update the time remaining --------
- Unit - Set life of (Picked unit) to ((Life of (Picked unit)) + 10.00)
- Hashtable - Save (RemainingTime - 1.00) as 0 of (Key (Picked unit)) in hashTable
-
Else - Actions
- -------- The healing is done, clean up --------
- Unit Group - Remove (Picked unit) from HealOverTimeUnits
- Hashtable - Clear all child hashtables of child (Key (Picked unit)) in hashTable
-
If - Conditions
-
Loop - Actions
-
Unit Group - Pick every unit in HealOverTimeUnits and do (Actions)
-
Events
This might be more work than using a bunch of waits inside a single trigger, but this way of doing things is completely MUI.
Now, another popular example. We're going to make a single target knockback spell that uses Hashtables. It will work just like the HealOverTime trigger because it will also use a group to hold all the units being knocked back. The difference is that more values will be stored in the hashTable and of course the unit will be knocked back instead of healed.
Here's another thing to keep in mind. Unless you handle your values carefully, spells that use the same hashtable can easily overwrite values in other spells. So, I recommend creating a hashtable global for each spell that needs to use it unless you know what you are doing and can prevent conflicts. Anyways, here are the knockback functions:
-
KnockbackTarget
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Knockback
-
Actions
- -------- Just for configuration/readability --------
- Set KnockbackAngle = (Angle from (Position of (Triggering unit)) to (Position of (Target unit of ability being cast)))
- -------- Distance unit is knocked back per second --------
- -------- .04 is how often the units are moved, so we multiply the distance by .04 --------
- Set KnockbackDistance = (400.00 x 0.04)
- -------- How long the unit is knocked back for --------
- Set RemainingTime = 2.00
- -------- Store the values --------
- Hashtable - Save KnockbackDistance as 0 of (Key (Target unit of ability being cast)) in knockbackTable
- Hashtable - Save KnockbackAngle as 1 of (Key (Target unit of ability being cast)) in knockbackTable
- Hashtable - Save RemainingTime as 2 of (Key (Target unit of ability being cast)) in knockbackTable
- Unit Group - Add (Target unit of ability being cast) to KnockbackUnits
-
Events
-
KnockbackUnits
-
Events
- Time - Every 0.04 seconds of game time
- Conditions
-
Actions
-
Unit Group - Pick every unit in KnockbackUnits and do (Actions)
-
Loop - Actions
- -------- Load all necessary variables stored in the Hashtable --------
- Set KnockbackDistance = (Load 0 of (Key (Picked unit)) from knockbackTable)
- Set KnockbackAngle = (Load 1 of (Key (Picked unit)) from knockbackTable)
- Set RemainingTime = (Load 2 of (Key (Picked unit)) from knockbackTable)
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- RemainingTime Greater than 0.00
-
Then - Actions
- -------- Move the unit and update time remaining --------
- Unit - Move (Picked unit) instantly to ((Position of (Picked unit)) offset by KnockbackDistance towards KnockbackAngle degrees)
- Hashtable - Save (RemainingTime - 0.04) as 2 of (Key (Picked unit)) in knockbackTable
-
Else - Actions
- -------- Knockback is done, clean up --------
- Hashtable - Clear all child hashtables of child (Key (Picked unit)) in knockbackTable
- Unit Group - Remove (Picked unit) from KnockbackUnits
-
If - Conditions
-
Loop - Actions
-
Unit Group - Pick every unit in KnockbackUnits and do (Actions)
-
Events
While remembering each key in an example like this isn't tricky, sometimes with larger systems they can become mixed up. There's a handy function that will get a String ID of a string just like you can get the Handle ID of a unit. So instead of using 0 for distance, we could have gotten the String ID of "distance". Here's what the first trigger might look like if we used String IDs instead:
-
KnockbackTarget
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Knockback
-
Actions
- -------- Just for configuration/readability --------
- Set KnockbackAngle = (Angle from (Position of (Triggering unit)) to (Position of (Target unit of ability being cast)))
- -------- Distance unit is knocked back per second --------
- -------- .04 is how often the units are moved, so we multiply the distance by .04 --------
- Set KnockbackDistance = (400.00 x 0.04)
- -------- How long the unit is knocked back for --------
- Set RemainingTime = 2.00
- -------- Store the values --------
- Hashtable - Save KnockbackDistance as (Key distance) of (Key (Target unit of ability being cast)) in knockbackTable
- Hashtable - Save KnockbackAngle as (Key angle) of (Key (Target unit of ability being cast)) in knockbackTable
- Hashtable - Save RemainingTime as (Key time) of (Key (Target unit of ability being cast)) in knockbackTable
- Unit Group - Add (Target unit of ability being cast) to KnockbackUnits
-
Events
As a final note, these triggers are merely examples. As you may or may have not noticed, the knockback trigger would require memory leak fixing since it leaks 25 locations each second for every unit that is being knocked backward. I intentionally left the triggers how they are so they would be easier to understand. Those who wish to use these kinds of triggers in their maps should make sure to fix any memory leaks that were not cleaned up in the examples.
Attachments
Last edited: