- Joined
- Mar 17, 2009
- Messages
- 1,350
Deuterium's Essentials Tutorial
Bookmark:
I - Graphical User Interface (GUI)
August 23, 2009
- Released
August 26, 2009
- Fixed minor grammatical and typing mistakes
- Edited: I - 1. Leaks
- Edited: I - 4 - a. Event: Unit - A unit Begins casting an ability
- Added: I - 4 - f. Destroying leaks correctly (in case the objects are created inside a loop)
- Added: I - 4 - g. Avoiding the usage of Index A (or Index B)
August 29, 2009
- Changed I - 4 - c. Unit: Casting unit to I - 4 - c. Replacing certain Event Response units with Triggering unit
- Added: I - 4 - h. Using Unit Group: Units In Range instead of Unit Group: Units In Region
August 30, 2009
- Edited: I - 3. Multi Unit Instanceability (MUI)
- Edited: I - 4 - e. Avoiding the usage of Do nothing
- Edited: i - 4 - g. Avoiding the usage of Index A (or Index B)
- Released
August 26, 2009
- Fixed minor grammatical and typing mistakes
- Edited: I - 1. Leaks
- Edited: I - 4 - a. Event: Unit - A unit Begins casting an ability
- Added: I - 4 - f. Destroying leaks correctly (in case the objects are created inside a loop)
- Added: I - 4 - g. Avoiding the usage of Index A (or Index B)
August 29, 2009
- Changed I - 4 - c. Unit: Casting unit to I - 4 - c. Replacing certain Event Response units with Triggering unit
- Added: I - 4 - h. Using Unit Group: Units In Range instead of Unit Group: Units In Region
August 30, 2009
- Edited: I - 3. Multi Unit Instanceability (MUI)
- Edited: I - 4 - e. Avoiding the usage of Do nothing
- Edited: i - 4 - g. Avoiding the usage of Index A (or Index B)
aznricepuff
- Locating some of the grammatical and typing mistakes
- Pointing out some of the missing information
Dr Super Good
- Pointing out an issue on the MUI section
Eleandor
- Pointing out some of the missing information
Hanky
- Explaining some issues about using regions
- Locating some of the grammatical and typing mistakes
- Pointing out some of the missing information
Dr Super Good
- Pointing out an issue on the MUI section
Eleandor
- Pointing out some of the missing information
Hanky
- Explaining some issues about using regions
Introduction
To start with, this tutorial contains information on issues that tend to be usually unknown to beginner spell-makers and are yet essential to the efficiency of a trigger.
* I'd advice those who intend to read this tutorial to either have some basic knowledge in spell-making or to read Basics of Trigger Enhancing Spells by Daelin.
The purpose for such issues being unknown to beginner spell-makers is that all authors tend to overlook such efficiency snippets in order to make their tutorials easier to grasp - and that makes sense.
This tutorial doesn't necessary go into the deep details of the issues mentioned. However, it provides enough information which allow the reader to make an efficient spell.
What's more, this tutorial would be updated on the run as I encounter more problems with spell-making. Thus, there is a chance this would include some Jass snippets in the future too.
I - Graphical User Interface (GUI)
I - 1. Leaks
What are leaks?
Leaks are basically information (which does take up RAM during gameplay) which is never cleaned up from the game.
There are several types of leaks, but most are common in the manner in which they are avoided.
Generally, leaks are created by objects that extend handles (positions, unit groups, player groups, special effects, etc...).
Any uneducated use of such objects would lead to memory leaking, for instance:
- Unit - Create 1 Footman for (Owner of (Triggering unit)) at (Position of (Triggering unit)) facing 0.00 degrees
How to avoid?
Generally, to avoid leaks, the object-handle to be used is set into a variable of it's type. For instance:
- Set GroupVariable = (Units of type Footman)
- Unit Group - Pick every unit in GroupVariable and do (Actions)
- Custom script: call RemoveLocation(udg_PointVariable)
Object Type | Command |
Position/Location | call RemoveLocation(<variable name>) |
Unit Group | call DestroyGroup(<variable name>) |
Player Group | call DestroyForce(<variable name>) |
* In contrast to most other leaks, Special Effects can be destroyed using GUI actions:
- Special Effect - Destroy (Last created special effect)
Leaking Example:
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- This action here leaks: Position of (Triggering unit) --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Unit - Create 1 Footman for (Owner of (Triggering unit)) at (Position of (Triggering unit)) facing 0.00 degrees
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- First of all, we set the position into a point variable --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Set PointVariable = (Position of (Triggering unit))
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Then we freely use the variable in the actions to retrive the saved handle --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Unit - Create 1 Footman for (Owner of (Triggering unit)) at PointVariable facing 0.00 degrees
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Once we're done from the position, we remove it using Custom Script --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Custom script: call RemoveLocation(udg_PointVariable)
Leaking Example:
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- This action here leaks: Units of type Footman --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Unit Group - Pick every unit in (Units of type Footman) and do (Actions)
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- First of all, we set the unit group into a unit group variable --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Set UGroupVariable = (Units of type Footman)
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Then we freely use the variable in the actions to retrive the saved handle --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Unit Group - Pick every unit in UGroupVariable and do (Actions)
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Once we're done from the unit group, we remove it using Custom Script --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Custom script: call DestroyGroup(udg_UGroupVariable)
Leaking Example:
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- This action here leaks: Player group(Player 1 (Red)) --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Player Group - Pick every player in (Player group(Player 1 (Red))) and do (Actions)
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- First of all, we set the player group into a playergroup variable --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Set PGroupVariable = (Player group(Player 1 (Red)))
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Then we freely use the variable in the actions to retrive the saved handle --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Player Group - Pick every player in PGroupVariable and do (Actions)
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Once we're done from the unit group, we remove it using Custom Script --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Custom script: call DestroyForce(udg_PGroupVariable)
Leaking Example:
Now special effects are a bit different. There are two methods:
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- This action here leaks a special effect --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Special Effect - Create a special effect attached to the origin of (Triggering unit) using Abilities\Spells\Orc\CommandAura\CommandAura.mdl
Now special effects are a bit different. There are two methods:
- -------- ---------------------------------------------------------------------------------- --------
- -------- Special Effect is created --------
- -------- ---------------------------------------------------------------------------------- --------
- Special Effect - Create a special effect attached to the origin of (Triggering unit) using Abilities\Spells\Orc\CommandAura\CommandAura.mdl
- -------- ---------------------------------------------------------------------------------- --------
- -------- If no special effect is to be created before destroying it, this could be done: --------
- -------- ---------------------------------------------------------------------------------- --------
- Special Effect - Destroy (Last created special effect)
- -------- ---------------------------------------------------------------------------------- --------
- -------- Special Effect is created --------
- -------- ---------------------------------------------------------------------------------- --------
- Special Effect - Create a special effect attached to the origin of (Triggering unit) using Abilities\Spells\Orc\CommandAura\CommandAura.mdl
- -------- ---------------------------------------------------------------------------------- --------
- -------- We store the Last created special effect in a variable --------
- -------- ---------------------------------------------------------------------------------- --------
- Set EffectVariable = (Last created special effect)
- -------- ---------------------------------------------------------------------------------- --------
- -------- Then special effect is destroyed --------
- -------- ---------------------------------------------------------------------------------- --------
- Special Effect - Destroy EffectVariable
This Custom Script command can only be used to destroy group leaks instantly after the group is created and it's actions run. This method doesn't require any array:
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- First thing we do is set the bj_wantDestroyGroup = true --------
- -------- The custom script line needs to be added before every call to a unit group looping action --------
- -------- If it is set to true, it will cause only the next group to be used by such an action to be destroyed; it is not persistent --------
- -------- (aznricepuff, Essentials Tutorial, Post #13) --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Custom script: set bj_wantDestroyGroup = true
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Now we just pick the group and run the actions; no leak to worry about --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
-
Unit Group - Pick every unit in (Units currently selected by Player 1 (Red)) and do (Actions)
-
Loop - Actions
- Unit - Kill (Picked unit)
-
Loop - Actions
I - 2. Avoiding the usage of Wait
Well, using the Wait action might not seem like a real problem, but it can be a problem in certain cases.
To start with the facts, the minimal value for Wait (or TriggerSleepAction() in Jass) would drop down to 0.27 seconds and not less whatsoever - even if the inserted number is less than 0.27 seconds.
Also, people tend to dislike the usage of Wait since they consider it not to be very precise. What's more, it would also ruin the Multi Instanceability (MUI'ness) of a spell - that would be explained later in this tutorial.
So let's start.
Suppose this simple case:
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Blink
-
Actions
- // The actions that come before the wait
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Wait 0.30 seconds
- -------- -------------------------------------------------------------------------------------------------------------- --------
- // The actions that come after the wait
Back to this case; the solution for the issue would consist of two triggers, one which is triggered when the ability is casted, and the other which runs every certain seconds:
-
Main Trigger
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Blink
-
Actions
- -------- The actions that come before the wait --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Then, we turn on the Loop Trigger --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Trigger - Turn on Loop Trigger <gen>
-
Events
-
Loop Trigger (Initially off)
-
Events
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- We set an event which runs every certain seconds --------
- -------- The periodic time can vary, it depends on what the user needs --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Time - Every 0.01 seconds of game time
- Conditions
-
Actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- We use a real variable to track the time --------
- -------- The time added to the counter equal to the periodic time in the events --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Set Counter = (Counter + 0.01)
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Now, we check if the time did exceed the time it's supposed to wait --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- Counter Greater than or equal to 0.30
-
Then - Actions
- // The actions that come after the wait
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Don't forget to turn off the trigger afte the actions are triggered --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Trigger - Turn off (This trigger)
- Else - Actions
-
If - Conditions
-
Events
Scenario:
Solution:
-
Looping with Wait
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Blink
-
Actions
- // Any actions before the loop
-
For each (Integer IntegerVar) from 1 to 10, do (Actions)
-
Loop - Actions
- // The actions through which we loop
- Wait 0.10 seconds
-
Loop - Actions
- // Any actions after the loop
-
Events
Solution:
-
Main Trigger
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Blink
-
Actions
- // The actions before the loop
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Then, we turn on the Loop Trigger --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Trigger - Turn on Loop Trigger <gen>
-
Events
-
Loop Trigger (Initially off)
-
Events
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- We set the periodic time equal to the time gap between each loop instance --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Time - Every 0.10 seconds of game time
- Conditions
-
Actions
- // The actions through which we loop
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- We use a integer variable to track how many times the loop had run --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Set InstanceCounter = (InstanceCounter + 1)
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Now we check if enough instances have run --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- InstanceCounter Greater than or equal to 10
-
Then - Actions
- // The actions that come after the wait
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Turn off the trigger afte the actions are triggered --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Trigger - Turn off (This trigger)
- Else - Actions
-
If - Conditions
-
Events
Scenarion:
First of all, to make the trigger most efficient, we find a greatest common periodic time between all Wait values. In this case, the greatest common denominator of 0.15 and 0.20 is 0.05.
-
Having more than one Wait
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Blink
-
Actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- So let's consider this complicated case --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- // First set of actions
- Wait 0.15 seconds
- // Second set of actions
- Wait 0.05 seconds
- // Final set of actions
-
Events
First of all, to make the trigger most efficient, we find a greatest common periodic time between all Wait values. In this case, the greatest common denominator of 0.15 and 0.20 is 0.05.
-
Main Trigger
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Blink
-
Actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- As usual, we start with the initial actions --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- // First set of actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Then we turn on the looping trigger --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Trigger - Turn on Loop Trigger <gen>
-
Events
-
Loop Trigger (Initially off)
-
Events
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- The periodic time picked by the user --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Time - Elapsed game time is 0.05 seconds
- Conditions
-
Actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Now we track the time with our counter --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Set WaitCounter = (WaitCounter + 0.05)
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- We check if 0.15 seconds have passed --------
- -------- Also since we want this to run only once, we flag it with a boolean --------
- -------- Basically, if boolean is true - meaning these actions have ran - then they won't run anymore --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Now here, the flag could have been not used --------
- -------- and instead we could use the condition WaitCounter equal to 0.15 --------
- -------- and it would work just fine --------
- -------- but what if we couldn't find a common denominator for the periodic time? --------
- -------- Suppose we're going at intervals of 0.04 --------
- -------- it'll skip the 0.15 - ... 0.12, 0.16... - thus the flag is usually needed --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- WaitCounter Greater than or equal to 0.15
- Flag Equal to False
-
Then - Actions
- // Second set of actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- And we set flag to true, announcing that this have ran --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Set Flag = True
- Else - Actions
-
If - Conditions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Now we want to start with the final set of actions --------
- -------- Also some basic math, we already waited 0.15 seconds --------
- -------- and we also want to wait an additional 0.05 seconds --------
- -------- that is 0.20 seconds --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Here no flag is needed since it's the last wait --------
- -------- and we're going to turn the trigger off instantly after the actions --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- WaitCounter Greater than or equal to 0.20
-
Then - Actions
- // Final set of actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- We turn off the trigger --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Trigger - Turn off (This trigger)
- Else - Actions
-
If - Conditions
-
Events
I - 3. Multi Unit Instanceability (MUI)
Simply said, a Multi Unit Instanceable (MUI) spell is a spell which would function normally if it's cast while another instance of the same spell is running.
* Instant spells (i.e. spells with no Wait at all) are already MUI due to the nature of trigger queuing.
Basically, the purpose for a spell not to be MUI is that in GUI global variables are used and more than one instance would interfere with the information the variables hold, and thus bugging the spell.
There are different approaches of making a spell MUI; however, I'm only going to explain Paladon's Indexing System, as it's my favorite, the simplest, and efficient enough. In this method, MUI'ness is achieved through giving each instance of the spell a unique array for its variables, thus preventing the interference of one instance to another. That would achieve spell's MUI'ness.
Other manners of making spells MUI are found in the following tutorials:
Dynamic Values Storage by Kingz
Hashtables and MUI by wyrmlord
[GUI] Efficient Arrays - Multiinstanceabilty for Timers, Custom Values, and more by PurplePoot
The following triggers explain how to make a spell MUI using Paladon's Indexing System:
-
Main Trigger
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Mirror Image
-
Actions
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Ok to start with, let me explain a bit --------
- -------- IndexOne doesn't do anything other than check how may instances of the spell is running --------
- -------- if IndexOne equal to 0, then no one is using the spell, if it's 1, then there's one unit --------
- -------- if it's equal to n then there are n units using the abilities --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Since all the units here are using the same Loop Trigger (the point of MUI) we make a condition --------
- -------- if IndexOne is originally zero, which means that no unit was using the ability which also means the Loop was off --------
- -------- we make a condition to check if IndexOne equal to zero --------
- -------- if it returns true then we turn on the Loop Trigger --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- DT_IndexOne Equal to 0
-
Then - Actions
- Trigger - Turn on Loop Trigger <gen>
- Else - Actions
-
If - Conditions
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Now to announce the number of units using the spell, we keep track of that by adding 1 to the value of IndexOne --------
- -------- Suppose there were no units using the ability, now there's one, so this makes the value of IndexOne to 1 --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- Set DT_IndexOne = (DT_IndexOne + 1)
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Now, to make the whole thing MUI, we are going to use a different array for each unit --------
- -------- Which is exactly like using a different variable --------
- -------- So we using another index, IndexTwo --------
- -------- This is gonna hold a different number for each instance so that each unit has his own array and thus his own variables --------
- -------- Let's say the value of IndexTwo is already 3 units using the ability, it'll become 4 for this intance --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- Set DT_IndexTwo = (DT_IndexTwo + 1)
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- This is a Boolean, which checks if the Loop Trigger should be running for that specific unit --------
- -------- since the Loop might be running for different units having the ability, we don't want it to affect the unit who has it done --------
- -------- it will be used as a condition in the Loop Trigger --------
- -------- having it True announces that the unit is using the Loop Trigger and so it will get affected by the Loop Trigger --------
- -------- As you see, it's indexed, and thus it's specific for each unit --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- Set DT_LoopIsOn[DT_IndexTwo] = True
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Now in the main trigger we use IndexTwo as an array for all variables with no exceptions --------
- -------- (unless it's a universal variable which holds the same info for all the units) --------
- -------- here go all your actions prior to the Looping --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Here, basically since the point variable would be set and destroyed instantly, we don't necessarily need to "Index" it --------
- Set DT_VariableExamplePoint = (Position of (Triggering unit))
- Unit - Create 1 Footman for (Owner of (Triggering unit)) at DT_VariableExamplePoint facing Default building facing degrees
- -------- This last created unit however would be created and kept until the end of the cast, and thus is indexed --------
- Set DT_VariableExampleUnit[DT_IndexTwo] = (Last created unit)
- Custom script: call RemoveLocation(udg_DT_VariableExamplePoint)
-
Events
-
Loop Trigger (Initially off)
-
Events
- Time - Every 0.03 seconds of game time
- Conditions
-
Actions
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Here we work slightly different --------
- -------- to have the same Loop Trigger to run for all the units seperatly, we use a Loop --------
- -------- This Loop uses IndexThree as it's integer, which is basically a different variable then IndexOne and IndexTwo --------
- -------- This Loop runs from integer 1 (which is the minimal possible array number possible for IndexTwo) --------
- -------- to IndexTwo (which is the highest number possible) --------
- -------- Thus all the units using the spell whose IndexTwo varies from 1 to n, --------
- -------- will have their actions running in variables with the same specific array integer used in the Main Trigger for each unit --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
-
For each (Integer DT_IndexThree) from 1 to DT_IndexTwo, do (Actions)
-
Loop - Actions
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Now IndexThree is used for all the arrays instead of IndexTwo --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- This condition checks whether the Boolean is true or false, if it's true, it'll run for that specific unit --------
- -------- If it's false, then it'll skip the actions for this specific array number and move on to the other array number and so on --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- DT_LoopIsOn[DT_IndexThree] Equal to True
-
Then - Actions
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- The WaitCounter which counts how long the loop has been running --------
- -------- You can see the number which I add is equal to the timer event --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- Set DT_WaitCounter[DT_IndexThree] = (DT_WaitCounter[DT_IndexThree] + 0.03)
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Condition to check your counter and all your actions --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- DT_WaitCounter[DT_IndexThree] Less than 10.00
-
Then - Actions
- -------- The actions --------
- Unit - Make DT_VariableExampleUnit[DT_IndexThree] face Default building facing over 0.00 seconds
-
Else - Actions
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Now the counter exceeded the time you specified so we reset the counter --------
- -------- and announce that we're done of this instance --------
- -------- We also destroy whatever has to be destroyed or removed --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- Unit - Add a 0.01 second Generic expiration timer to DT_VariableExampleUnit[DT_IndexThree]
- Set DT_WaitCounter[DT_IndexThree] = 0.00
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- We announce that this unit is done of it's actions by setting the Boolean to False --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- Set DT_LoopIsOn[DT_IndexThree] = False
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- We subtract the value of IndexOne by 1 since one instance is done, and as we've said, --------
- -------- IndexOne records the number of INSTANCES RUNNING only --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
- Set DT_IndexOne = (DT_IndexOne - 1)
- -------- ------------------------------------------------------------------------------------------------------------- --------
- -------- Now this condition checks if there are no more instance running --------
- -------- So, if IndexOne is zero, that implies that there are no instance running --------
- -------- in that case, we set the value of IndexTwo back to zero --------
- -------- (a sort of recycling method so that the value doesn't keep on summing up to a high value) --------
- -------- and then since there are no instance running and using the Loop (all the Booleans would be False in that case) --------
- -------- we turn the trigger off for the sake of efficiency --------
- -------- ------------------------------------------------------------------------------------------------------------- --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- DT_IndexOne Equal to 0
-
Then - Actions
- Set DT_IndexTwo = 0
- Trigger - Turn off (This trigger)
- Else - Actions
-
If - Conditions
-
If - Conditions
- Else - Actions
-
If - Conditions
-
Loop - Actions
-
Events
I - 4. GUI Snippets
These are basically some little GUI mistakes done by beginner spell-makers:
a. Event: Unit - A unit Begins casting an ability
This event is actually buggy and can cause problems in-game (unless used wisely) since this runs the trigger few moments before the cooldown runs and mana is consumed. So basically, the player (if talented enough) can keep on casting the ability without losing any mana or having a cooldown.
Instead - to solve this problem - the following event is used:
- Unit - A unit Starts the effect of an ability
b. Overlooking the double leak in Polar Projections
Polar Projections do actually leak two locations instead of one:
- Unit - Create 1 Footman for (Owner of (Triggering unit)) at ((Position of (Triggering unit)) offset by 200.00 towards 0.00 degrees) facing 0.00 degrees
The second leak is the projected point(Position of (Triggering unit)) offset by 200.00 towards 0.00 degrees)
Solution:
- Set TrigUnitLoc = (Position of (Triggering unit))
- Set PolarProjLoc = (TrigPosition offset by 200.00 towards 0.00 degrees)
- Unit - Create 1 Footman for (Owner of (Triggering unit)) at PolarProjLoc facing 0.00 degrees
- Custom script: call RemoveLocation(udg_TrigUnitLoc)
- Custom script: call RemoveLocation(udg_PolarProjLoc)
c. Replacing certain Event Response units with Triggering unit
Certain Event Response units basically call for the Triggering unit in the background of the game, thus instead we should manually and directly use Triggering unit in order to achieve better efficiency.
A list of these units would be:
GUI Form | Jass Form |
Attacked unit | GetAttackedUnitBJ() |
Cancelled structure | GetCancelledStructure() |
Constructing structure | GetConstructingStructure() |
Decaying unit | GetDecayingUnit() |
Dying unit | GetDyingUnit() |
Entering unit | GetEnteringUnit() |
Hero manipulating item | GetManipulatingUnit() |
Learning Hero | GetLearningUnit() |
Leaving unit | GetLeavingUnit() |
Leveling Hero | GetLevelingUnit() |
Loading unit | GetLoadedUnitBJ() |
Ordered unit | GetOrderedUnit() |
Ownership-changed unit | GetChangingUnit() |
Researching unit | GetResearchingUnit() |
Revivable Hero | GetRevivableUnit() |
Reviving Hero | GetRevivingUnit() |
Selling unit | GetSellingUnit() |
Summoned unit | GetSummonedUnit() |
d. Using variables to retrieve the same information more than once
Suppose that we have a trigger where we use Target unit of ability being cast more than once. Basically, every time we use it, the computer has to do some work and return the right unit.
Instead, we set Target unit of ability being cast into a variable before using it, and then we use the variable throughout the whole spell instance. Retrieving information from a variable is much faster.
* This applies to everything; this isn't specific to the case of Target unit of ability being cast.
Example:
-
Scenario
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to Mirror Image
-
Actions
- Set TargUnit = (Target unit of ability being cast)
- Unit - Hide TargUnit
- Unit - Kill TargUnit
- Unit - Remove TargUnit from the game
-
Events
e. Avoiding the usage of Do nothing
Basically and simply said, avoid the usage of Do nothing:
- Do nothing
The only reason Blizzard added this Do nothing action is due to the presence of the single lined if/then/else action and the need to fill in the "else" part with it in case there are no suitable actions. So to avoid using the Do nothing, avoid using the single lined if/then/else action.
f. Destroying leaks correctly (in case the objects are created inside a loop)
Take this faulty example:
-
For each (Integer LoopingInteger) from 1 to 4, do (Actions)
-
Loop - Actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Here, since the loop runs from 1 to 4, then 4 leaks are created --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Special Effect - Create a special effect attached to the chest of (Triggering unit) using Abilities\Spells\NightElf\shadowstrike\shadowstrike.mdl
-
Loop - Actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- However, we're detroying the leak outside of the loop, --------
- -------- destroying thus only the last created leak - leaving 3 other leaks undestroyed --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Special Effect - Destroy (Last created special effect)
-
For each (Integer LoopingInteger) from 1 to 4, do (Actions)
-
Loop - Actions
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- Here - again - since the loop runs from 1 to 4, then 4 leaks are created --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Special Effect - Create a special effect attached to the chest of (Triggering unit) using Abilities\Spells\NightElf\shadowstrike\shadowstrike.mdl
- -------- -------------------------------------------------------------------------------------------------------------- --------
- -------- In contrast to the previous example, we're detroying the leak inside of the loop, --------
- -------- destroying the last created leak directly after it get created --------
- -------- -------------------------------------------------------------------------------------------------------------- --------
- Special Effect - Destroy (Last created special effect)
-
Loop - Actions
g. Avoiding the usage of Integer A (or Integer B)
This is a rather complicated issue to explain to beginner users. Yet - simply said - Integer A (I'm going to ignore Integer B since it is the same as Integer A and both function alike) is a predefined global variable. Thus, the information it might carry while looping could be changed due to any interception by other triggers which also contain Integer A. What happens then - once the looping of the original trigger resumes - is that the value of Integer A is different leading to faulty looping - i.e. bug.
PurplePoot said:The actual reason is because (Integer A) is a global so two loops running it at once interfere with one another as they both access it and change it at the same time (more accurately, one changes it while the other is waiting for the first to finish).
So just avoid using Integer A or Integer B. You could do so by picking the following action in the trigger editor:
The integer variable used should be unique for each trigger. If in one trigger - for example - LoopIntegerOne is used, a different trigger should use a different integer variable (LoopIntegerTwo for example).
h. Using Unit Group: Units In Range instead of Unit Group: Units In Region
As a remark, if used wisely and efficiently, Units In Region is not much of a problem. However, it is prefered to use Units In Range for several reasons.
Units In Region tend to be slower then Units In Range since the Units In Region requires an additional parameter than those needed for Units In Range: a region.
What's more, if the range at which the units should be involved in a group varies, then a new region would be needed for every call in the case of Units In Regions; however, in the case of Units In Range, only a different real number defining the new range would change. In such cases, Units In Range happens to be far more efficient then Units In Region.
Also, the shape of a region happens to be rectangular, which is pretty much impractical in spell making compared to the circular shape used by Units In Range.
So as a conclusion, avoid using Units In Region in the codes of custom spells since it is easily avoidable and never essential for any spell. In my opinion, the usage of Units In Region is only fine in cases where the the region is preset in the map for general mapping purposes.
Attachments
Last edited: