Tom Jones
N/A
- Reaction score
- 437
Code:
What’s MUI?
Which unit references are mui?
Variables.
Local Handle Systems (jass).
Conclusion.
What’s MUI?
MUI stands for Multi Unit Instanceable, and simply put this means that two or more units are able to trigger a specific trigger at the same time, performing the triggers actions for each triggering units. To understand MUI, we must also understand how a trigger works. A trigger consist of events, conditions, and actions. When the trigger registers the event, and it’s conditions evaluates true, the triggers actions gets executed. The game engine then creates a new trigger in the old triggers place, containing the same events, conditions, and actions. This means that the “same” trigger can be triggered even if it’s still being executed.
Which unit references are MUI?
References are my word for all Blizzard defined actions that refers to something, including Event Responses, group responses, and Last Something actions. In this tutorial only unit references will be mentioned, which includes:
Event Responses:
All unit event responses are MUI and you can refer to the these responses even after long waits. To prove this I killed two pre placed units with a trigger, and made another trigger register a generic unit death. This trigger displayed the name of the triggerring unit twice, once when the trigger got triggered and once after five seconds. Here is the triggers I used:
This was the result:
I then made the trigger wait for one minute before displaying the name of the triggering unit, and changed triggering unit to dying unit. The result was the same:
After the 60 seconds wait the text was displayed:
We can conclude that Event Responses are MUI, even after long waits.
Group Responses:
Group responses includes Picked unit and Matching unit. Both responses are MUI but their functionality are more limited than event responses. Picked unit can only be used inside a group loop action, and Matching unit can only be used in group pick every units actions that has a condition. To prove that both Picked unit and Matching unit are MUI, I killed two preplaced units, one owned by player red and one owned by player blue, with a trigger. Another trigger registers the death, and picks all units owned by the owner of the triggering unit matching a condition, and displays the name and owner of the picked unit. These are the triggers I used:
And the result:
I could also have choosen to execute the triggers and used another pick every units action, instead of killing a unit and picking all units owned by the owner of the triggering unit . It would give us the same result. We can conclude that Mathcing unit and Picked unit is MUI, however we can’t use them with waits because of Blizzard limitations.
Last Something Actions:
These are actually variables created by Blizzard, and they are not MUI. To prove this I created two units with triggers, and displayed the name of the last created unit twice, once when the trigger gets triggered, and once after 5 seconds. Here’s the triggers I used:
The result:
As you can see, after 5 seconds trigger ones Last Created Unit is a knight, even though the unit created in that trigger was a footman. However it worked when the trigger got triggered, so with very very short waits it’s close to MUI.
Variables:
Variables can be declared in two ways, as locals or globals. When you create a variable using the trigger editors variable editor, you are in fact creating global variables. One of the differences between the two declarations is that globals may be used by all triggers, but locals can only be used in the trigger wherein they got declared. The most crucial difference however, is that locals works just like Event Responses. We’re going to take advantage of this later, but first I’ll prove that global variables aren’t MUI. I created two units with triggers, and assigned them to the same variable. I know that allready now it’s obvius that this isn’t MUI, but just in case here’s the trigger:
And the result:
Seems familiar? This is the exact same result as the Last Something result. Not only is Last Something actions variables, they are actually global variables. And once again we proven that global variables isn’t MUI... Or is it?
As mentioned earlier, local variables works excactly like Event Responses which, in case you forgot it, is 100% MUI. We have the option to assign local variables to our globals variables, but unfurtunately we can’t refer to local variables in the Trigger Editor. What we’re going to do instead, is localizing the global creating a, what I like to call it, global local. This is done with the custom script action, and it looks like this:
Notice the variable name: udg_Unit. When I created the variable in the variable editor, I named it Unit, but the editor automatically puts udg_ in front of all user created variables, which stands for User Defined Global. This is a safety to avoid that users creates malfunctioning variable names, and this is the real name of the variable. This means that if I had entered Unit as the variable name instead of udg_Unit, I would actually have created a local variable and not localized the global variable. It’s very important that you put udg_ in front of all user created variables in a custom script action. To prove that global locals are MUI, I used the same triggers as the last prove, except that I localized the global variable unit, which looks like this:
Here’s the result:
This proves that global locals are MUI, and by adding a global local to the two triggers we also made them MUI. You can localize the same global in several triggers, and a localized global can be set to any value, without overridding the same localized global in other triggers. The only problem with localized globals is, that because of the Trigger Editors poor conversion form GUI to JASS, global locals can’t be used in the following:
In a If/Then/Else’s condition.
In a group condition.
Inside a group loop.
In a Conditional wait.
Also remember to null the global locals at the end of the trigger to avoid leaks, and note that for some reason beyond my knowlegde, only one variable type can be localized in each trigger, etc. two localized unit variables wouldn't work.
Local Handle Vars (jass):
This is a great tool to make your code MUI.If your unfamiliar with the concept, it simply allows us to store variables and use them later, be it in a function, after waits, at the end of the map, etc.The two most used systems are KaTTaNas Local Handle Vars system, and Vexorians CS system. What I’m going to describe is similar for both systems, so if you learn one system you should also be able to learn the other. I would like to point out, that besides the normal local handle functions, Vexorians system also contains table and set functions to optimize the functionality of the system. I’m going to use KaTTaNas system, because that’s the system I work with. If you haven’t done so by now, get the system from this link. Implementation is fairly easy, all you gotta do is copy the system to the map header, and create a game cache variable. Create a game cache at map initialization, and assign the game cache variable to it. Now we have to change the return of LocalVars() to return our game cache variable:
Code:
function LocalVars takes nothing returns gamecache
return udg_gc
endfunction
Now that we imported the system, let’s understand how it works. The base of the system is the function H2I, the return bug function:
Code:
function H2I takes handle returns integer
return h
return 0
endfunction
Code:
function SetHandleHandle takes handle subject, string name, handle value returns nothing
if value==null then
call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
else
call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
endif
endfunction
function SetHandleInt takes handle subject, string name, integer value returns nothing
if value==0 then
call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
else
call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
endif
endfunction
function SetHandleBoolean takes handle subject, string name, boolean value returns nothing
if value==false then
call FlushStoredBoolean(LocalVars(),I2S(H2I(subject)),name)
else
call StoreBoolean(LocalVars(), I2S(H2I(subject)), name, value)
endif
endfunction
function SetHandleReal takes handle subject, string name, real value returns nothing
if value==0 then
call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
else
call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
endif
endfunction
function SetHandleString takes handle subject, string name, string value returns nothing
if value==null then
call FlushStoredString(LocalVars(), I2S(H2I(subject)), name)
else
call StoreString(LocalVars(), I2S(H2I(subject)), name, value)
endif
endfunction
Code:
function GetHandleHandle takes handle subject, string name returns handle
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleBoolean takes handle subject, string name returns boolean
return GetStoredBoolean(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleString takes handle subject, string name returns string
return GetStoredString(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleUnit takes handle subject, string name returns unit
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
return null
endfunction
function GetHandleTrigger takes handle subject, string name returns trigger
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
return null
endfunction
function GetHandleEffect takes handle subject, string name returns effect
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
return null
endfunction
function GetHandleGroup takes handle subject, string name returns group
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
return null
endfunction
function GetHandleLightning takes handle subject, string name returns lightning
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
return null
endfunction
function GetHandleWidget takes handle subject, string name returns widget
return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
return null
endfunction
call FlushHandleLocals(SomeHandle)
After that you can fix leaks, and null variables refering to the handle. Let’s have a look at how to use the system. We’re going to make a function that damages the target of a ability over time.
Code:
function Test_Actions takes nothing returns nothing
local timer t = CreateTimer() timer
call SetHandleHandle(t,”u”,GetTriggerUnit())
call SetHandleHandle(t,”v”,GetSpellTargetUnit())
call TimerStart(t,1,true, function Test_Timer)
set t = null
endfunction
The first thing I do is creating a timer, and assign it to a variable. I then assign the triggering unit and the targeted unit of the ability to the newly created timer. I then start the timer which will execute this function every second:
Code:
function Test_Timer takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u = GetHandleUnit(t,”u”)
local unit v = GetHandleUnit(t,”v)
local real r = GetHandleReal(t,”r”)+1
if r <= 5 then
call UnitDamageTarget(u,v,50,false,false,ATTACK_TYPE_NORMAL,etc)
call SetHandleReal(t,"r",r)
else
call PauseTimer(t)
call FlushHandleLocals(t)
call DestroyTimer(t)
endif
set v = null
set u = null
set t = null
endfunction
Code:
function Test_Timer takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u = GetHandleUnit(t,”u”)
local unit v = GetHandleUnit(t,”v)
local real r = GetHandleReal(t,”r”)+1
if r <= 5 then
call UnitDamageTarget(u,v,50,false,false,ATTACK_TYPE_NORMAL,etc)
call SetHandleReal(t,"r",r)
else
call PauseTimer(t)
call FlushHandleLocals(t)
call DestroyTimer(t)
endif
set v = null
set u = null
set t = null
endfunction
function Test_Actions takes nothing returns nothing
local timer t = CreateTimer()
call SetHandleHandle(t,”u”,GetTriggerUnit())
call SetHandleHandle(t,”v”,GetSpellTargetUnit())
call TimerStart(t,1,true, function Test_Timer)
set t = null
endfunction
function InitTrig_Test takes nothing returns nothing
blabla
endfunction
Try getting the above to work, starting from scratch. If you encounter problems, here’s some pointers:
Did you create a game cache?
Did you assign a game cache to the game cache variable?
Did you change the return value of the LocalVars() function?
Are you using the assigning and getting a value using the correct name?
Did you create the timer?
Conclusion:
I hope I might have clarified the meaning of MUI, and helped you with the issues that lead you to read this tutorial. I would also like to point out, that making efficient MUI is best done with JASS. If you wish to learn jass, I’ll suggest the tutorials by Daelin or emijl3r.