Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
Hello everyone,
Like the title said, I want to localize an array variable in order to avoid too many variables because it's getting messed up. Thank you.
Hello everyone,
Like the title said, I want to localize an array variable in order to avoid too many variables because it's getting messed up. Thank you.
Assume that I want to make a charge ability for knight. So I need a trigger for setting up variables, points, caster, and distance. and another for moving units within a time-frame.
Charge
Events
Unit - A unit Starts the effect of an ability
Conditions
(Ability being cast) Equal to Animate Dead
Actions
Set Caster = (Triggering unit)
Set TargetPoint = (Target point of ability being cast)
Unit - Pause Caster
Unit - Turn collision for Caster Off
Trigger - Turn on Charge Loop <gen>
Charge Loop
Events
Time - Every 0.05 seconds of game time
Conditions
Actions
Set CasterPoint = (Position of Caster)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Distance between CasterPoint and TargetPoint) Greater than or equal to 40.00
(Caster is alive) Equal to True
Then - Actions
Set Angle = (Angle from CasterPoint to TargetPoint)
Set MoveToPoint = (CasterPoint offset by 50.00 towards Angle degrees)
But something's wrong, it's not MUI, which may cause leaks when units are casting at the same time.
To avoid that I localize the variables, but at the same time I don't want to have too much variables either. So I make one variable being an array one to be the target point, caster point and move to point.
Edit: a bit messsy, trying to fix. Fixing done
Making the variables Local won't work for a trigger like this. Local variables will only work in the trigger that created them, but you're working with 2 triggers here.
You need to use one of these: Unit Indexing, Dynamic Indexing, Hashtables, or some other method of making it MUI.
Here's an example of an MUI Charge using a Unit Indexer (GUI Unit Indexer 1.4.0.0). I reuse some Temp variables to help keep things minimal:
Cast Charge
Events
Unit - A unit Starts the effect of an ability
Conditions
Actions
Set VariableSet TempPoint[0] = (Position of (Triggering unit))
Set VariableSet CV = (Custom value of (Triggering unit))
Set VariableSet Charge_Point[CV] = (Target point of ability being cast)
Set VariableSet Charge_Angle[CV] = (Angle from TempPoint[0] to Charge_Point[CV])
The only new variables are Charge_Point, Charge_Angle, and Charge_Group. The other variables can be used in your other triggers as well if handled appropriately.
I have these triggers which are from this thread of mine but only one was bothered to answer. Anyway, I decided to use local udg_ but like the title said, localizing array variables to save variables. After that, the only thing I struggle with is removing location because I'm using a wait function. Then I realized I don't need the unit indexer when I'm localizing a variable and destroy and null it after that. I remember that you have to destroy the object first then nullify it afterward?
I am not sure why you want to do this. Thanks to the folder structure and folder item declared variables feature you can now group global variables together where they are used. This makes managing them much easier, however using them will still be difficult due to other short comings of GUI.
In case you did not know what a local variable was I suggest reading the Wikipedia page. In case of JASS and Lua the local scope is the function it is declared in.
A single trigger can produce many functions and the scope of a local variable is the function it is declared in. For group and player loops are an example where GUI creates a separate function for the actions and so will not be able to use the local variables declared in the main action block.
In this case "handle them properly" means that actions run while the variables hold useful data does not cause other triggers to run that use those variables and so overwrite the useful data. This is why people often use separate arrays.
I would suggest separate temp variables instead of an array. Only use arrays if you stand to benefit or require from the index they offer. Might also be a good idea to write comments listing what temp variables each trigger or part of trigger use to avoid conflicts.
I am not sure why you want to do this. Thanks to the folder structure and folder item declared variables feature you can now group global variables together where they are used. This makes managing them much easier, however using them will still be difficult due to other short comings of GUI.
In case you did not know what a local variable was I suggest reading the Wikipedia page. In case of JASS and Lua the local scope is the function it is declared in.
I'm terribly sorry if I used the wrong word. I really don't know how we say these programming languages. Anyway, I want to limit the numbers variables I have now. That's why I made this thread, "Localize an array in GUI." Instead of saying localize, could I say "declare a local array variable"?
A single trigger can produce many functions and the scope of a local variable is the function it is declared in. For group and player loops are an example where GUI creates a separate function for the actions and so will not be able to use the local variables declared in the main action block.
In this case "handle them properly" means that actions run while the variables hold useful data does not cause other triggers to run that use those variables and so overwrite the useful data. This is why people often use separate arrays.
I would suggest separate temp variables instead of an array. Only use arrays if you stand to benefit or require from the index they offer. Might also be a good idea to write comments listing what temp variables each trigger or part of trigger use to avoid conflicts.
(Unit-type of (Casting unit)) Equal to Wooden Gate (Opened) (Hori) (Faces South)
Actions
Custom script: local location array udg_local_Loc_Gate
Set Loc_Gate[0] = (Position of (Casting unit))
Set Loc_Gate[1] = (Loc_Gate[CV_Array[0]] offset by (0.00, 100.00))
Set Loc_Gate[2] = (Loc_Gate[CV_Array[0]] offset by (0.00, -100.00))
Region - Center Gate Hori Det <gen> on Loc_Gate[0]
Region - Center Gate Hori T <gen> on Loc_Gate[1]
Region - Center Gate Hori B <gen> on Loc_Gate[2]
Set Loc_Gate[3] = (Loc_Gate[0] offset by (-200.00, -300.00))
Set Loc_Gate[4] = (Loc_Gate[0] offset by (200.00, 300.00))
Wait 0.02 seconds
Custom script: set bj_wantDestroyGroup = true
Unit Group - Pick every unit in (Units in Gate Hori Det <gen> matching ((((Matching unit) is A structure) Equal to False) and (((Matching unit) is A ground unit) Equal to True))) and do (Actions)
Loop - Actions
If ((Gate Hori T <gen> contains (Picked unit)) Equal to True) then do (Unit - Move (Picked unit) instantly to Loc_Gate[1]) else do (Do nothing)
If ((Gate Hori B <gen> contains (Picked unit)) Equal to True) then do (Unit - Move (Picked unit) instantly to Loc_Gate[2]) else do (Do nothing)
I'm terribly sorry if I used the wrong word. I really don't know how we say these programming languages. Anyway, I want to limit the numbers variables I have now. That's why I made this thread, "Localize an array in GUI." Instead of saying localize, could I say "declare a local array variable"?
So then I need Unit Indexer?
It's true, but when there's too many variables then optimization has to made
I'll be careful with that
So this is the gate close triggers I'm fixing.
Gate Close Hori South
Events
Unit - A unit Begins casting an ability
Conditions
(Ability being cast) Equal to Close Gate
(Unit-type of (Casting unit)) Equal to Wooden Gate (Opened) (Hori) (Faces South)
Actions
Custom script: local location array udg_local_Loc_Gate
Set Loc_Gate[0] = (Position of (Casting unit))
Set Loc_Gate[1] = (Loc_Gate[CV_Array[0]] offset by (0.00, 100.00))
Set Loc_Gate[2] = (Loc_Gate[CV_Array[0]] offset by (0.00, -100.00))
Region - Center Gate Hori Det <gen> on Loc_Gate[0]
Region - Center Gate Hori T <gen> on Loc_Gate[1]
Region - Center Gate Hori B <gen> on Loc_Gate[2]
Set Loc_Gate[3] = (Loc_Gate[0] offset by (-200.00, -300.00))
Set Loc_Gate[4] = (Loc_Gate[0] offset by (200.00, 300.00))
Wait 0.02 seconds
Custom script: set bj_wantDestroyGroup = true
Unit Group - Pick every unit in (Units in Gate Hori Det <gen> matching ((((Matching unit) is A structure) Equal to False) and (((Matching unit) is A ground unit) Equal to True))) and do (Actions)
Loop - Actions
If ((Gate Hori T <gen> contains (Picked unit)) Equal to True) then do (Unit - Move (Picked unit) instantly to Loc_Gate[1]) else do (Do nothing)
If ((Gate Hori B <gen> contains (Picked unit)) Equal to True) then do (Unit - Move (Picked unit) instantly to Loc_Gate[2]) else do (Do nothing)
So the first issue is that you're using (Casting unit) instead of (Triggering unit). You cannot and should not reference Event Responses after using a Wait. These Event Responses are basically Global variables that Blizzard assigns for you. Whenever a unit casts a spell, Casting unit is assigned to that unit. Whenever a unit dies, Dying unit is assigned to that unit. Whenever a unit enters a region, Entering unit is assigned to that unit. Etc... The problem is that these variables becomes lost the moment you use a Wait or if any time passes between the setting of the variable and the attempt to use it. I think there are some exceptions in which they don't get unassigned, but even then they can get overwritten by another trigger that uses them (or even the same trigger).
There is one exception. Triggering Unit is treated like a Local variable for whatever reason, it's the only Event Response that's like this. Meaning you can reference Triggering Unit safely throughout that Gate trigger even after the Waits. That's the ONLY thing that's safe to reference after the Waits in that trigger.
Also, Waits need to be synced when playing online, which makes them imprecise. Wait 0.02 seconds is more like Wait ~0.2 seconds. See this for more information: Wait Command, Good or Bad?
That being said, I don't think you need to use Waits in the first place.
Also, note that the Event "A unit begins casting an ability" is not safe to use in a lot of circumstances. This is because this Event runs the moment you BEGIN casting, which means that the ability hasn't even finished it's cast animation yet. So if your casting unit gets interrupted (the player can simply order the unit to "Stop") then the Event will run but the ability will never go off. If a player is aware of this, they can spam the ability/Stop command to run the trigger over and over again. HOWEVER, since your Gate is being replaced there's no room for exploitation so it's safe to use it.
With all of that being said, here's your Close Gate trigger using only "Temp" variables to handle the Points and a "Temp" Unit Group to move the nearby units:
-------- Move Nearby Units --------
Set VariableSet TempGroup[0] = (Units within 300.00 of TempPoint[0].)
Unit Group - Pick every unit in TempGroup[0] and do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
((Picked unit) is A structure) Equal to False
((Picked unit) is A ground unit) Equal to True
Then - Actions
Set VariableSet TempPoint[3] = (Position of (Picked unit))
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Distance between TempPoint[3] and TempPoint[1]) Less than or equal to (Distance between TempPoint[3] and TempPoint[2])
Then - Actions
Unit - Move (Picked unit) instantly to TempPoint[1]
Else - Actions
Unit - Move (Picked unit) instantly to TempPoint[2]
So you know how I said "it's safe to re-use these temp variables as long as you handle them properly." Well, here's a perfect example of having POTENTIAL to leak and cause issues.
So i'm moving the Picked units, there's nothing inherently wrong with this:
Unit - Move (Picked unit) instantly to TempPoint[1]
But let's say I move the Picked unit(s) to a REGION, which is used in this other trigger:
Enter Region Example
Events
Unit - A unit enters ExampleRegion <gen>
Conditions
Actions
Set VariableSet TempPoint[1] = (Position of (Triggering unit))
Special Effect - Create a special effect at TempPoint[1] using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
Special Effect - Destroy (Last created special effect)
So this is a problem! This trigger can actually run DURING the Close Gate trigger since Moving the picked unit(s) may move them into the Region, causing the trigger to run. And since both of these triggers use TempPoint[1], conflicts will occur, and the Close Gate trigger will break/have leaks.
This is what it'd look like if you moved a Picked unit into our ExampleRegion:
Close Gate
Events
Unit - A unit Begins casting an ability
Conditions
Actions
-------- Setup Variables --------
Set VariableSet TempPoint[0] = (Position of (Triggering unit))
Set VariableSet TempPoint[1] = (TempPoint[0] offset by (0.00, 150.00))
Set VariableSet TempPoint[2] = (TempPoint[0] offset by (0.00, -150.00))
-------- --------
-------- Move Nearby Units --------
Set VariableSet TempGroup[0] = (Units within 300.00 of TempPoint[0].)
Unit Group - Pick every unit in TempGroup[0] and do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
((Picked unit) is A structure) Equal to False
((Picked unit) is A ground unit) Equal to True
Then - Actions
Set VariableSet TempPoint[3] = (Position of (Picked unit))
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Distance between TempPoint[3] and TempPoint[1]) Less than or equal to (Distance between TempPoint[3] and TempPoint[2])
Then - Actions
Unit - Move (Picked unit) instantly to TempPoint[1]
-------- Let's say that the Picked unit is moved into our ExampleRegion: --------
Set VariableSet TempPoint[1] = (Position of (Picked unit))
Special Effect - Create a special effect at TempPoint[1] using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
Special Effect - Destroy (Last created special effect)
Unit - Replace (Triggering unit) with a Footman using The old unit's relative life and mana
Animation - Play (Last replaced unit)'s stand animation
Selection - Add (Last replaced unit) to selection for (Owner of (Triggering unit))
See the issue? TempPoint[1] is used in both of these triggers and it's getting overwritten and Removed. Since it's removed, our Close Gate trigger can no longer reference it which will cause problems. Also, this creates a Leak since our previous TempPoint[1] was overwritten without ever being destroyed.
This is why you need to handle Temp variables properly. A solution in this particular case is to use a separate variable for the Enters region trigger, OR, use an Array index that you know is safe/unused by anything else. Maybe you can reserve TempPoint[10] to be used for your Enter Region triggers for example.
So the first issue is that you're using (Casting unit) instead of (Triggering unit). You cannot and should not reference Event Responses after using a Wait. These Event Responses are basically Global variables that Blizzard assigns for you. Whenever a unit casts a spell, Casting unit is assigned to that unit. Whenever a unit dies, Dying unit is assigned to that unit. Whenever a unit enters a region, Entering unit is assigned to that unit. Etc... The problem is that these variables becomes lost the moment you use a Wait or if any time passes between the setting of the variable and the attempt to use it. I think there are some exceptions in which they don't get unassigned, but even then they can get overwritten by another trigger that uses them (or even the same trigger).
There is one exception. Triggering Unit is treated like a Local variable for whatever reason, it's the only Event Response that's like this. Meaning you can reference Triggering Unit safely throughout that Gate trigger even after the Waits. That's the ONLY thing that's safe to reference after the Waits in that trigger if you plan on making this MUI.
Also, Waits need to be synced when playing online, which makes them imprecise. Wait 0.02 seconds is more like Wait ~0.2 seconds. See this for more information: Wait Command, Good or Bad?
If you reduce the Wait to 0.01 seconds it will probably be closer to what you wanted. Although, I don't think you need Waits in the first place.
Also, note that the Event "A unit begins casting an ability" is not safe to use in a lot of circumstances. This is because this Event runs the moment you BEGIN casting, which means that the the ability hasn't even finished it's cast animation yet. So if your casting unit gets interrupted (the player can simply order the unit to "Stop") then the Event will run but the ability will never go off. If a player is aware of this, they can spam the ability/Stop command to run the trigger over and over again. HOWEVER, since your Gate is being replaced immediately then it's safe to do this since there's no chance for the ability to get interrupted/exploited.
With all of that being said, here's your Close Gate trigger using only "Temp" variables to handle the Points and a "Temp" Unit Group to move the nearby units:
Close Gate
Events
Unit - A unit Begins casting an ability
Conditions
Actions
-------- Setup Variables --------
Set VariableSet TempPoint[0] = (Position of (Triggering unit))
Set VariableSet TempPoint[1] = (TempPoint[0] offset by (0.00, 150.00))
Set VariableSet TempPoint[2] = (TempPoint[0] offset by (0.00, -150.00))
-------- --------
-------- Move Nearby Units --------
Set VariableSet TempGroup[0] = (Units within 300.00 of TempPoint[0].)
Unit Group - Pick every unit in TempGroup[0] and do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
((Picked unit) is A structure) Equal to False
((Picked unit) is A ground unit) Equal to True
Then - Actions
Set VariableSet TempPoint[3] = (Position of (Picked unit))
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Distance between TempPoint[3] and TempPoint[1]) Less than or equal to (Distance between TempPoint[3] and TempPoint[2])
Then - Actions
Unit - Move (Picked unit) instantly to TempPoint[1]
Else - Actions
Unit - Move (Picked unit) instantly to TempPoint[2]
Unit - Replace (Triggering unit) with a Footman using The old unit's relative life and mana
Animation - Play (Last replaced unit)'s stand animation
Selection - Add (Last replaced unit) to selection for (Owner of (Triggering unit))
So you know how I said "it's safe to re-use these temp variables as long as you handle them properly." Well, here's a perfect example of having POTENTIAL to leak and cause issues.
So i'm moving the Picked units, there's nothing inherently wrong with this:
Unit - Move (Picked unit) instantly to TempPoint[1]
But let's say I move the Picked unit(s) to a REGION, which is used in this other trigger:
Enter Region Example
Events
Unit - A unit enters ExampleRegion <gen>
Conditions
Actions
Set VariableSet TempPoint[1] = (Position of (Triggering unit))
Special Effect - Create a special effect at TempPoint[1] using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
Special Effect - Destroy (Last created special effect)
So this is a problem! This trigger can actually run DURING the Close Gate trigger since Moving the picked unit(s) may move them into the Region, causing the trigger to run. And since both of these triggers use TempPoint[1], conflicts will occur, and the Close Gate trigger will break/have leaks.
This is what it'd look like if you moved a Picked unit into our ExampleRegion:
Close Gate
Events
Unit - A unit Begins casting an ability
Conditions
Actions
-------- Setup Variables --------
Set VariableSet TempPoint[0] = (Position of (Triggering unit))
Set VariableSet TempPoint[1] = (TempPoint[0] offset by (0.00, 150.00))
Set VariableSet TempPoint[2] = (TempPoint[0] offset by (0.00, -150.00))
-------- --------
-------- Move Nearby Units --------
Set VariableSet TempGroup[0] = (Units within 300.00 of TempPoint[0].)
Unit Group - Pick every unit in TempGroup[0] and do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
((Picked unit) is A structure) Equal to False
((Picked unit) is A ground unit) Equal to True
Then - Actions
Set VariableSet TempPoint[3] = (Position of (Picked unit))
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Distance between TempPoint[3] and TempPoint[1]) Less than or equal to (Distance between TempPoint[3] and TempPoint[2])
Then - Actions
Unit - Move (Picked unit) instantly to TempPoint[1]
-------- Let's say that the Picked unit is moved into our ExampleRegion: --------
Set VariableSet TempPoint[1] = (Position of (Picked unit))
Special Effect - Create a special effect at TempPoint[1] using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
Special Effect - Destroy (Last created special effect)
Unit - Replace (Triggering unit) with a Footman using The old unit's relative life and mana
Animation - Play (Last replaced unit)'s stand animation
Selection - Add (Last replaced unit) to selection for (Owner of (Triggering unit))
See the issue? TempPoint[1] is used in both of these triggers and it's not getting overwritten (LEAK) and Removed. Since it's removed, our Close Gate trigger can no longer reference it which breaks anything referencing TempPoint[1].
This is why you need to handle Temp variables properly. A solution in this particular case is to use a separate variable for the Enters region trigger, OR, use an Array index that you know is safe/unused by anything else. Maybe you can reserve TempPoint[10] to be used for your Enter Region triggers for example.
I'm terribly sorry if I used the wrong word. I really don't know how we say these programming languages. Anyway, I want to limit the numbers variables I have now. That's why I made this thread, "Localize an array in GUI." Instead of saying localize, could I say "declare a local array variable"?
Variable count has little impact on game performance. For it to have an impact a ludicrous number must be used. For example if variables are being procedurally generated by a program resulting in hundreds of thousands or millions of variables. In such a case I recommend Lua be used instead of JASS since Lua generally scales better and is likely much faster anyway.
Your script must have an error in it. You declare a local variable called udg_local_Loc_Gate but then are using a variable called udg_Loc_Gate instead. Even if that was fixed it still would not work for reasons I stated earlier that the local variable scope is just a single function while that code compiles into multiple functions.
With arrays you must null each index used. Another reasons it is often easier to use separate variables instead of arrays with constant indices.
Variable count has little impact on game performance. For it to have an impact a ludicrous number must be used. For example if variables are being procedurally generated by a program resulting in hundreds of thousands or millions of variables. In such a case I recommend Lua be used instead of JASS since Lua generally scales better and is likely much faster anyway.
Your script must have an error in it. You declare a local variable called udg_local_Loc_Gate but then are using a variable called udg_Loc_Gate instead. Even if that was fixed it still would not work for reasons I stated earlier that the local variable scope is just a single function while that code compiles into multiple functions.
With arrays you must null each index used. Another reasons it is often easier to use separate variables instead of arrays with constant indices.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.