1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  4. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  5. The results are out! Check them out.
    Dismiss Notice
  6. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  7. The raddest synthwave tracks were chosen - Check out our Music Contest #12 - Results and congratulate the winners!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Essentials Tutorial

Discussion in 'Trigger (GUI) Editor Tutorials' started by Deuterium, Aug 22, 2009.

  1. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2

    Deuterium's Essentials Tutorial



    Bookmark:

    I - Graphical User Interface (GUI)

    [​IMG]1. Leaks
    [​IMG]2. Avoiding the usage of Wait
    [​IMG]3. Multi Unit Instanceability (MUI)
    [​IMG]4. GUI Snippets
    [​IMG][​IMG]a. Event: Unit - A unit Begins casting an ability
    [​IMG][​IMG]b. Overlooking the double leak in Polar Projections
    [​IMG][​IMG]c. Replacing certain Event Response units with Triggering unit (also Jass related)
    [​IMG][​IMG]d. Using variables to retrieve the same information more than once
    [​IMG][​IMG]e. Avoiding the usage of Do nothing
    [​IMG][​IMG]f. Destroying leaks correctly (in case the objects are created inside a loop)
    [​IMG][​IMG]g. Avoiding the usage of Index A (or Index B)
    [​IMG][​IMG]h. Using Unit Group: Units In Range instead of Unit Group: Units In Region

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

    Credits
    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

    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
        • Unit - Kill (Picked unit)


    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
      • // The actions that come before the wait
      • -------- -------------------------------------------------------------------------------------------------------------- --------
      • Wait 0.30 seconds
      • -------- -------------------------------------------------------------------------------------------------------------- --------
      • // The actions that come after the wait


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


    • 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


    Examples
    a. Looping with Wait
    Scenario:
    • 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
        • // Any actions after the loop

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


    • 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

    b. Having more than one Wait
    Scenarion:
    • 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


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


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



    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
        • -------- ------------------------------------------------------------------------------------------------------------- --------
        • -------- 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)
      • 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
              • Else - Actions





    I - 4. GUI Snippets



    These are basically some little GUI mistakes done by beginner spell-makers:
    [point]Event Casting[/point]
    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).

    [point]Polar Leak[/point]
    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)


    [point]Casting Unit[/point]
    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()


    [point]Use variable[/point]
    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


    [point]Do Nothing[/point]
    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.

    [point]Loop Leak[/point]
    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
    • -------- -------------------------------------------------------------------------------------------------------------- --------
    • -------- 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
        • -------- -------------------------------------------------------------------------------------------------------------- --------
        • -------- 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)


    * 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.

    So just avoid using Integer A or Integer B. You could do so by picking the following action in the trigger editor:

    [​IMG]

    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.
     

    Attached Files:

    Last edited: Aug 30, 2009
  2. -Berz-

    -Berz-

    Joined:
    Mar 5, 2008
    Messages:
    3,203
    Resources:
    123
    Icons:
    111
    Packs:
    1
    Skins:
    1
    Spells:
    10
    Resources:
    123
    Nicely done ;)

    Simple explanation and nice and easy examples, I would recommend every new "GUI-er" to read these little facts, I would also make this sticky in spell section, because it's easy and if you don't get this, then quit right away.

    Understanding this little facts, is a path to become a good GUI spell maker (enough to freely submit your resources without getting comments e.g. Fail, leaks, waits, non MUI...)

    Deu, thanks for this writing this up!

    There is one more thing that you could do Deu, use hidden tags for longer triggers ;)

    ~Berz
     
  3. Paladon

    Paladon

    Joined:
    Dec 6, 2007
    Messages:
    2,083
    Resources:
    42
    Icons:
    28
    Packs:
    2
    Spells:
    12
    Resources:
    42
    Your example of MUIness is just one possible way.
    I know you know that, but you should mention that.
     
  4. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
  5. Paladon

    Paladon

    Joined:
    Dec 6, 2007
    Messages:
    2,083
    Resources:
    42
    Icons:
    28
    Packs:
    2
    Spells:
    12
    Resources:
    42
    The line below :p

    There.
    Otherwise, it looks as if every link explains my old way which does in fact do just one of them.
     
    Last edited: Aug 22, 2009
  6. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
  7. Dark_Dragon

    Dark_Dragon

    Joined:
    Jul 19, 2007
    Messages:
    596
    Resources:
    8
    Tools:
    1
    Spells:
    7
    Resources:
    8
    impressive gj Deut! its well explained and k like BZK said xD it might be nice idea to make hidden tages on long triggers but its not like it cant live without it :)

    ill suggest any new GUI-er to read this :)
     
  8. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    Maybe I'll hide them once I update it... let me find more gramatical mistakes before I do so. But I mean, hiding them would be like hiding the main explanation :s and that sort of doesn't make sense to me...
    But I'll think about that :p

    Thanks for checking!
     
  9. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    not bad not bad, rechecking again :)

    EDIT:

    You should mention in the MUI wayness that a boolean isnt needed if you do it right, like me :)

    Mention about array stored points, needed in most cases ;)

    Variablize target event isnt always needed, ofc it's faster but you wont recognize it anyway

    If you want to mention about varablize something that is an event stuff that is used more often is level of ability being cast :p

    Hidden the triggers for perfection!

    I see it very well made but you should have a spell inside that got loads of indexed stuffs so ppl get it better :)

    Very good, sure about that one!
     
  10. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    Well yes true, you can use the unit comparison... but I mentioned that it could be done in different ways. I demonstrated the one that I prefer.

    I don't quite understand what you mean by this... :s

    Umm, yeah Level of ability being cast sounds better :) maybe I'll fix that if I'm not too lazy :p

    Well I guess I'll hide all triggers... good? :) (but I'll wait a bit until I've got more to update)

    Well, I sort of hate coding in GUI nowadays... and what's more, I've been taking a break from coding lately :p yet maybe some day I'll improve one of my GUI spells... make it as efficient as it could get, and then attach it here :)

    Thanks!
     
  11. aznricepuff

    aznricepuff

    Joined:
    Feb 22, 2006
    Messages:
    749
    Resources:
    4
    Maps:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    4
    Ok, there's some small grammar/typo issues:

    In contrast to...

    Definitely, some basic...each different case; I'll be giving...

    ...the ability which means the Loop was...

    I have to go now, but I'll review this more in-depth later.
     
  12. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    Oh thanks :) I'll fix them all at once once you finish the whole review. I didn't use Microsoft Office to write this so well... couldn't locate all the typos.

    Also, I want to ask you, do you agree with the guys that I should hide the triggers which aren't hidden? Because the purpose I didn't hide them is that they're a major part of the explanation... and thus hiding them didn't feel right to me.


    EDIT:
    Ok I did skim through the tutorial again to find some more grammatical mistakes... I fixed those that I found and the ones you mentioned.

    Thanks :)

    I'll wait for your opinion on the hidden thing...
     
  13. aznricepuff

    aznricepuff

    Joined:
    Feb 22, 2006
    Messages:
    749
    Resources:
    4
    Maps:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    4
    I don't really have an opinion either way on hiding those triggers. If you think hiding them makes your tutorial more effective, then do it; otherwise, don't worry about it...I'm not gonna not approve it for something like that.

    I found some more errors:

    ...in the manner in which they are avoided.

    ...i.e. spells with no Wait at all....

    I assume you meant to type "event" there.

    "...nothing except decrease the efficiency of a spell."

    Ok, and now onto content-related stuff.

    • You might want to mention when discussing bj_wantDestroyGroup that 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.
    • Event - Unit Starts 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.
    • Maybe if you could provide a list of all the useless event response actions and not just casting unit.
     
  14. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    Rightt... thanks for pointing that out! :) Otherwise wouldn't have ever thought of mentioning this...

    Kept on telling myself not to forget mentioning this, yet I did... :s

    Sure... I'll go through all and check which are BJ's and which are natives... :)


    I'll update this the following few days, going to be busy for a day or two.

    Thanks for pointing my mistakes out :)
     
  15. MortAr

    MortAr

    Joined:
    Feb 25, 2009
    Messages:
    1,768
    Resources:
    7
    Icons:
    6
    Packs:
    1
    Resources:
    7
    Woah! I make a fast preview of the tutorial and I was surprised!
    Im definetily gonna read the whole of it when I have some time.
    Great work, Deuterium.
     
  16. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    Glad you like it :) Well, I would be updating it these two days, adding one more snippet issue and improve what aznricepuff pointed out...
     
  17. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    Updated... added 2 more snippets and edited some little issues and fixed some mistakes.

    Well, I might do so in the future, but testing for them would be sort of exhausting as I'd need to trigger every unit event in a map (which is difficult as I need to set the map to run the specific event) and retrieve the Id of the unit if I use: GetTriggerUnit() and see if I get an Id or not... and then compare it to the Id of the corresponding Event Response.

    I might test the units which are mostly used instead of all - unless you know an easier way for testing for that.

    Hope it's good enough to be approved now :)
     
  18. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    Very good adding the Integer A/B problems and yeah I agree, hard to explain but I saw in one post from Purple that had a pretty good explenation, I will look for it if I got time. Very good.

    Found it but not a big explenation but you still get it.

    Look at the thread for more info and yes seed you even commented there! :)
     
  19. Deuterium

    Deuterium

    Joined:
    Mar 17, 2009
    Messages:
    1,301
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    Yes Purple had explained that... and I know where it is :p but if I quote that, I would have to go deep in the explanation of the problem, complicating the whole thing for beginners...

    Keeping it as it is... well, gives the reader a better chance of getting a brief idea of why not to use it.

     
  20. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    you really dont need to explain it more or? I get it, you get it, berzerk get it, my mom get it so we all get it (partly all)