Deuterium's Essentials Tutorial
Bookmark:
I - Graphical User Interface (GUI)
1. Leaks
2. Avoiding the usage of Wait
3. Multi Unit Instanceability (MUI)
4. GUI Snippets

a. Event: Unit - A unit Begins casting an ability

b. Overlooking the double leak in Polar Projections

c. Replacing certain Event Response units with Triggering unit (also Jass related)

d. Using variables to retrieve the same information more than once

e. Avoiding the usage of Do nothing

f. Destroying leaks correctly (in case the objects are created inside a loop)

g. Avoiding the usage of Index A (or Index B)

h. Using Unit Group: Units In Range instead of Unit Group: Units In Region
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
Here,
Position of (Triggering unit) would create a handle, but this handle is never destroyed.
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)
Then, the variable is used to retrieve the handle information throughout actions. For instance:

Unit Group - Pick every unit in GroupVariable and do (Actions)
Once the user is done from the variable, he basically destroys the handle using certain Custom Script commands distinct for every object type. An example would be:

Custom script: call RemoveLocation(udg_PointVariable)
Here's a list of what the most usual commands are:
| 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)
* Variables, when inserted into Custom Scripts are preceeded by the prefix: udg_ (which means: User Defined Global)
Examples
a. Position Leak
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
How to avoid?

-------- -------------------------------------------------------------------------------------------------------------- --------

-------- 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)
b. Unit Group Leak
Leaking Example:

-------- -------------------------------------------------------------------------------------------------------------- --------

-------- This action here leaks: Units of type Footman --------

-------- -------------------------------------------------------------------------------------------------------------- --------

Unit Group - Pick every unit in (Units of type Footman) and do (Actions)
How to avoid?

-------- -------------------------------------------------------------------------------------------------------------- --------

-------- 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)
c. Player Group Leak
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)
How to avoid?

-------- -------------------------------------------------------------------------------------------------------------- --------

-------- 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)
d. Special Effect Leak
Leaking Example:

-------- -------------------------------------------------------------------------------------------------------------- --------

-------- 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
How to avoid?
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)
Or:

-------- ---------------------------------------------------------------------------------- --------

-------- 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
Using bj_wantDestroyGroup:
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
The custom script line needs to be added before every call to a unit group looping action and that it if set to true, it will cause only the next group to be used by such an action to be destroyed; it is not persistent.
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
How would we fix this? Well, pretty simple, and this applies to all cases. Definitely, some basic mathematical wit is required for each different case; I'll be giving examples of some cases.
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

Loop Trigger (Initially off)
Examples
a. Looping with Wait
Scenario:

Looping with Wait
This would basically make the actions run every 0.1 seconds for 10 times, thus basically running every 0.1 seconds for 1 second. Thus there are many ways to do this, yet I'll demonstrate the best.
Solution:

Main Trigger

Loop Trigger (Initially off)
b. Having more than one Wait
Scenarion:

Having more than one Wait
Solution:
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

Loop Trigger (Initially off)
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


Conditions


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)



-------- ------------------------------------------------------------------------------------------------------------- --------



-------- 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)

Loop Trigger (Initially off)
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
Nevertheless, the event
Unit - A unit Begins casting an ability is not always useless. It's extremely useful in some situations; i.e. if you want to stop the casting of a spell before the mana is used and the cooldown is triggered through triggers (aznricepuff,
Essential Tutorial, Post #13).
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 first leak is the central point: Position of (Triggering unit)
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
e. Avoiding the usage of Do nothing
Basically and simply said, avoid the usage of
Do nothing:

Do nothing
Do nothing does nothing but decrease the efficiency of a spell.
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

-------- -------------------------------------------------------------------------------------------------------------- --------

-------- 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)
However, the solution to this is pretty simple:

For each (Integer LoopingInteger) from 1 to 4, do (Actions)


Loop - Actions
* This applies to all leaking objects.
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.
Quote:
|
Originally Posted by PurplePoot
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.