- Joined
- Dec 12, 2008
- Messages
- 7,385
MUI Triggers with Waits
Disclaimer: This is not an acceptable form for Spell/System resources.
It is only meant for map-making.
Go here instead.
Disclaimer: This is not an acceptable form for Spell/System resources.
It is only meant for map-making.
Go here instead.
Table of Contents
- Introduction
- What is 'MUI'?
- The Problem
- The Solution
- Pros and Cons
- Delayed MUI Spells Without Waits
- Wrap-Up
Introduction
Good morning reader, or if it's not morning at the moment
while you're reading this, good evening! Then again, it might
be just around noon, so in that case, Good day! What I'm
trying to say is... Bah, how about a simple "Hello"? That
applies to all readers in all timezones and places at all times.
Hello.
Since you might be new here, I'm going to explain to you exactly
why I made this tutorial. If you've never made a spell or a system
before (Yes! You can actually make custom spells with Warcraft III
and custom systems. Cool, right?), then you're probably psyched
right about now after reading the previous text in parenthesis.
Before I move on, if your understanding of the French language is
far more developed than your understanding of English, I'd recommend
this made by our very own Troll-Brain.
What is 'MUI'?
MUI is an acronym for "Melons United International". It is a secret
organization bent on the annihilation of the human race so that
melons may take over and CLAIM POWER THEN VOYAGE TO THE STA-
Oh, sorry, forget everything I said, I didn't know we were talking
about JASS. Anyway, MUI means "Multi-unit Instanceability". It's
an attribute we give to a spell or a system indicating that it can
actually work for more than one unit. In the spell case, it means
that two units can cast the spell at the same time and still have
it working. In the systems case, it means that it can handle two
units or more.
MPI means the same thing, except it refers to player-handling.
(And certainly not Mango world domination or anything like that.
What a silly thought, why are you even asking? LET'S MOVE ON
SHALL WE?)
An example of a system that needs to be MPI is a Custom Fullscreen
Inventory system. (If you're new here and you're reading this, you
probably need new pants right about now. It's okay, I'll reserve the
rest of the information for the next section. Go on, you may discard
your current pair, I'M CERTAINLY NOT WATCHING YOU.)
... Well? Move along now.
The Problem
As a 14-year old child with a dream to become the best spell-writer
ever, I came to Hive so I can submit a resource of mine. That day,
I learned of a new dark side to something I've loved for a very long
time.
I started Warcraft III modding when I was an 11-year old child. One
of my favorite GUI actions was the "Wait" action. I used it excessively,
but on that day, I learned that it's one of the worst things you could
use in a trigger.
If you're new, this is probably news for you:
Using waits can make a trigger Non-MUI in a lot of cases
Terrible, right? Crying won't help, and isn't very necessary, after all,
I didn't make this tutorial to speak of the problem, I made it to address
and fix the problem.
Yes! There's hope!
The Solution
Sorry, I didn't mean to make it sound like a Penal Enlarging Ad, this is a GUI
tutorial, so let's get back on topic, shall we?
Okay, for new users, this is going to seem difficult, but it's actually
very simple. I'm going to introduce you to the concept of the solution
first.
Do you know what a buffer is?
A buffer is basically an array of data. I can write data to a buffer,
then read it in the same order that I wrote it.
What we're going to do in order to address the Wait-MUI problem is
use a buffer for all the data. The main reason this problem arises is
because of data modifications. There are certain things in GUI that
remain unchanged after waits, and others that change depending on
what's going on in the map.
An example of something that remains constant: (Triggering unit)
An example of something that changes: (Last created unit)
The triggering unit is different for each trigger execution, so it remains
constant, but the last created unit is pretty much a variable that gets
set every time you create a unit with the BJ function (meaning the GUI
create unit action).
Using (Triggering unit) as is will work after a "Wait", but since you're
often going to store that into a unit variable, it's not going to work well.
Let's begin.
-
Bad Trigger
-
Events
- Unit - A unit starts the effects of a spell
-
Conditions
- (Ability being cast) Equal to MyDerpyAbility
-
Actions
- Set caster = (Triggering unit)
- Wait 5.00 seconds
- Unit - Remove caster from the game
-
Events
Let's investigate why, shall we?
What's going on?
Imagine we have two units on the map, just hanging out, smoking weed,
and appreciating the beauty of
The first unit decides to cast a spell (MyDerpyAbility)
Thus, currently, in the trigger, caster is equal to the first unit because
he casted the spell.
3 seconds later, the trigger is still waiting because of the 5-second wait.
Suddenly, the second unit decides to cast the spell in an attempt to troll
the first unit.
Guess what happens! The caster variable in the trigger is going to be equal
to the second unit!
After 2 more seconds, the 5-second wait for the first cast finishes, so the
trigger is going to remove the unit stored in the caster variable from the game!
This is why spells are not MUI when you use waits in them and include variables.
The second unit's trolling scheme fails as he is removed from the game.
After 3 seconds, the second 5-second wait will finish and the unit stored in the
caster variable will be removed from the game. (And it happens to be the second
unit too). So nothing actually happens because he was already removed.
See how that doesn't work?
This is pretty much what happens in almost every other case.
The solution is to make the data read on each execution a different source.
But how do we do that? The answer, my friend, is an array, which we will use as a buffer.
-
Pro Trigger
-
Events
- Unit - A unit starts the effects of an ability
-
Conditions
- (Ability being cast) Equal to MyDerpyAbility
-
Actions
- -------- WriteIndex and ReadIndex are integer variables --------
- Set WriteIndex = (WriteIndex + 1)
- Set caster[WriteIndex] = (Triggering unit)
- Wait 5.00 seconds
- Set ReadIndex = (ReadIndex + 1)
- Unit - Remove caster[ReadIndex] from the game
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- ReadIndex Equal to WriteIndex
-
Then - Actions
- Set ReadIndex = 0
- Set WriteIndex = 0
- Else - Actions
-
If - Conditions
-
Events
What's going on?
Let's imagine that the two units are in the same state.
Just hanging around, smoking weed, etc...
The first unit decides to cast the spell.
At this moment, the WriteIndex variable will be equal to 1.
Hence, caster[1] will be equal to the first unit.
After 3 seconds, the second unit decides to cast the spell.
At this moment, the WriteIndex variable will be equal to 2.
Hence, caster[2] will be equal to the second unit.
After 2 seconds, the 5-second wait for the first unit's spellcast
ends. The ReadIndex variable is increased by 1, so it becomes 1.
Thus, caster[1] will be removed from the game.
The trigger continues to check if ReadIndex is equal to WriteIndex.
Currently, WriteIndex is 2 and ReadIndex is 1. When this happens,
it means that one of the units is still waiting.
After 3 more seconds, the 5-second wait for the second unit's
spellcast ends. The ReadIndex will be increased by 1 and become 2.
Thus, caster[2] will be removed from the game.
The trigger continues to check if ReadIndex is equal to WriteIndex.
Both of them equal 2, so they are both reset to 0.
We don't really need to check if they're equal so we can set them
to 0, but we're doing this because, well, you may or may not know this,
but arrays have a limit. The highest index you could use for an array is
8191. And due to a bug with Game-saving, that limit is cranked down
to 8190. So for all of you who've been trying to use 9001 as an index,
I'M SORRY, BUT THAT'S NEVER GOING TO WORK ;_;.
How-to
- For every piece of data you want to store and retrieve after the wait, you need to use an array.
- You need two integers, one to increase and use as an index before the wait and before setting the variables,
and one to increase and use after the wait and before reading the variables. (WriteIndex and ReadIndex were used in the examples) - Don't forgot to check if these two integers are equal at the end of the trigger so you can reset them to 0.
Pros and Cons
Waits have both pros and cons:
Pros
- They are easy-to-use and clear.
Cons
- They are inaccurate (The actual wait time depends on the latency meaning that waits on Garena take much longer than waits on LAN
:3
- Wait times vary with an offset range of [+0.098 ... +0.2] seconds in the best case.
- Waits don't 'wait' when the game is paused. (Disputable)
- In JASS, when called inside Conditions, they crash the thread.
- The wait time with this method must be a constant. (Thank you IcemanBo for pointing this out)
- You either store all state and every last bit of event-response data that you want to use after the wait code, or you store absolutely nothing and depend on the current execution context of the trigger.
(This means that using something like (Triggering Unit) after the wait action will give you incorrect results if you used a buffer to store data for the instances. You either use the buffers to store absolutely everything, or you use no buffers at all.) (Thank you again, IcemanBo for helping me make this realization)
As you can see, the cons outweigh the pros.
This is why I only recommend using the method outlined in this
tutorial for map-making only. Public resources using this may be
rejected for using this method.
(Melon United International has absolutely nothing to do with this, I can assure you.
THEY DON'T EXIST I SWEAR.)
Delayed MUI Spells Without Waits
Wrap-Up
I hope this tutorial has helped you with any difficulties you're facing. If you have any
questions, feel free to ask. Also, I wouldn't recommend using this method very often in
public resources. This method is only for map-making. The cons that waits have outweigh
the pros, hence it's up to the map-maker to decide what he wants to do. Thank you for
reading this tutorial. I hope you learned a thing or two. (Actually, I hope you learned
nothing, and that's a compliment because it implies that I believe you know a lot :3).
~Magtheridon96
Last edited: