- Joined
- Jun 5, 2008
- Messages
- 2,573
Attention!» This system is deprecated, meaning, it is outdated, no longer in use. Hanky's "Dynamic Indexing Template" is the latest standard on fulfilling the purposes of this system; using Hanky's indexing template over this one is recommendable.
Dynamic values storage
One note, this was called Paladon's integer array indexing system but after talking to PurplePoot he explained me why it cannot be named like that.
Contents:
-Introduction
What is Dynamic values storage? It's and easy to use variable system used by spell makers to make Multi Unit instancable spells.Why use it? It is easy and i found no other way than it, though other ways exist.
What do you need to use it - nothing it isn't a custom script or anything like that it is simply a variable based system.
Can you make any spell in GUI MUI with this - the answer is yes.
Okay without delay i will now introduce you how to set up before using the system.
-Variables
For using the system itself you only need 1 integer array variable, example:- index[] - this is the integer variable we are going to use for indexing
Now how does it work that is pretty easy once you trigger out the first spell with this system, we are going to use several arrays of the variable index[]
and i will explain the trigger below:
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to (==) *spell*
-
Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- index[1] Equal to (==) 0
-
Then - Actions
- Trigger - Turn on loop <gen>
- Else - Actions
-
If - Conditions
- Set index[1] = (index[1] + 1)
- Set index[2] = (index[2] + 1)
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
Now maybe you are asking yourself why are we using index[1] and index[2] for? Here is why:
-index[1] variable we are going to increase and decrease whenever we initialize or finish the effect of the desired spell so if it is 0 when we begin to cast
it we turn on the loop gen which will be our periodic trigger while when the spell effect finishes we decrease it and if it is 0 again then there are no more instances
of our spells running and we can turn off the periodic trigger and reset the index[2] variable.
-index[2] variable we are going to use for storing variables like units/damage/locations,etc. We are only going to increase it and never decrease it, why?
Imagine you have 2 spells running at the same time and you decrease the index[2] value and the second spells variables get mixed up with the first one.
So how to stop piling up of index[2] value? Easy when the spell effect finishes and we decrease index[1] variable we will if the index[1] is equal to 0(that means no spell instances are running)
and we can reset the index[2] to 0.
Now in loop triggers we are going to use index[n] variable where the n will vary from 2-y.
Example:
-
loop
-
Events
- Time - Every 1.00 seconds of game time
- Conditions
-
Actions
-
Do Multiple ActionsFor each (Integer index[3]) from 1 to index[2], do (Actions)
- Loop - Actions
-
Do Multiple ActionsFor each (Integer index[3]) from 1 to index[2], do (Actions)
-
Events
Why are we going to use index[3] variable and not for example LoopIntegerA? Cause IntegerA can cause a strange blizzard bug and it's cleaner this way.
Now what we are doing in the trigger is we are going to cycle through each value of index[2] variable using index[3] variable, in plain words:
you have 3 spell instances running and now each second you want an effect to happen so we cycle through each spell casted and do it's action while not messing up the
other 2 spell instances by using the index through we stored them.
-Making a first MUI spell with Dynamic Values Storage method
Now the first MUI spell we are going to make is a DOT(damage over time) one.We will base it on storm bolt and rename it to Heart Stab.
Then we prepare the necesary variables we have in mind:
- unit array variable - target // for storing the target into a variable
- unit array variable - damage_source // for gold/exp proper gain
- real array variable - dpi // damage per interval
- real array variable - duration // spell duration
- string variable - dpi_effect // string of our damage effect
- real array variable - damage_interval // damage interval in seconds
- real array variable - interval_check // this variable will always have a starting value of 0 cause we are going to use it for damage interval checking
Now the initial trigger setup:
-
HS init
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to (==) Heart Stab
-
Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- index[1] Equal to (==) 0
-
Then - Actions
- Trigger - Turn on loop <gen>
- Else - Actions
-
If - Conditions
- -------- We check is there an active spell instance running already by the value of index[1] --------
- -------- --- --------
- Set index[1] = (index[1] + 1)
- -------- We increase the index we are going to use for checking is there an active spell going on --------
- -------- --- --------
- Set index[2] = (index[2] + 1)
- -------- We increase the index variable we are going to use for storing variables --------
- -------- --- --------
- Set damage_source[index[2]] = (Casting unit)
- -------- we store the caster for damaging unit refrence --------
- -------- --- --------
- Set target[index[2]] = (Target unit of ability being cast)
- -------- we store the target for the periodic events --------
- -------- --- --------
- Set damage_interval[index[2]] = 1.00
- -------- we define the damage interval --------
- -------- --- --------
- Set dpi[index[2]] = 15.00
- -------- we define the damage dealt per interval to the target --------
- -------- --- --------
- Set duration[index[2]] = 10.00
- -------- we define the spell duration --------
- -------- --- --------
- Set dpi_effect = Objects\Spawnmodels\Human\HumanBlood\HumanBloodFootman.mdl
- -------- on the end we define the periodic damage effect string this shouldn't need to be a array variable if you don't plan on changing it depending on the spell level --------
- -------- --- --------
- Set interval_check = 0
- -------- variable used for damage interval checking --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
Events
That is it, we stored all the values we need into variables which we stored via integer index for future use and now we can make the loop trigger.
Now the most important thing we forgot, we didn't made it multi leveled, but with several modifications we can easily do that.
Spells without multi level support aren't good so if you wish to make MUI spells at least add multi level suport to them.
We are going to modify the values to our desire, i modifed them like this:
-
HS init
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to (==) Heart Stab
-
Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- index[1] Equal to (==) 0
-
Then - Actions
- Trigger - Turn on loop <gen>
- Else - Actions
-
If - Conditions
- -------- We check is there an active spell instance running already by the value of index[1] --------
- -------- --- --------
- Set index[1] = (index[1] + 1)
- -------- We increase the index we are going to use for checking is there an active spell going on --------
- -------- --- --------
- Set index[2] = (index[2] + 1)
- -------- We increase the index variable we are going to use for storing variables --------
- -------- --- --------
- Set damage_source[index[2]] = (Casting unit)
- -------- we store the caster for damaging unit refrence --------
- -------- --- --------
- Set target[index[2]] = (Target unit of ability being cast)
- -------- we store the target for the periodic events --------
- -------- --- --------
- Set damage_interval[index[2]] = (1.25 - (0.25 x (Real((Level of (Ability being cast) for (Casting unit))))))
- -------- we define the damage interval --------
- -------- --- --------
- Set dpi[index[2]] = (10.00 + (5.00 x (Real((Level of (Ability being cast) for (Casting unit))))))
- -------- we define the damage dealt per interval to the target --------
- -------- --- --------
- Set duration[index[2]] = (5.00 + (5.00 x (Real((Level of (Ability being cast) for (Casting unit))))))
- -------- we define the spell duration --------
- -------- --- --------
- Set dpi_effect = Objects\Spawnmodels\Human\HumanBlood\HumanBloodFootman.mdl
- -------- on the end we define the periodic damage effect string this shouldn't need to be a array variable if you don't plan on changing it depending on the spell level --------
- -------- --- --------
- Set interval_check = 0
- -------- variable used for damage interval checking --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
Events
And now we set up the settings for a multi leveled spell, the next step is making the loop trigger.
First of all our damage_interval is a multiple of 0.25 so we are going to use a trigger which runs every 0.25 seconds.
Then we are going to cycle through the index[2] values with index[3] variable.
The loop trigger should look like this:
-
loop
-
Events
- Time - Every 0.25 seconds of game time
- Conditions
-
Actions
-
Do Multiple ActionsFor each (Integer index[3]) from 1 to index[2], do (Actions)
-
Loop - Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- duration[index[3]] Greater than (>) 0.00
-
Then - Actions
- Set duration[index[3]] = (duration[index[3]] - 0.25)
- -------- we decrease the spell duration by 0.25 --------
- -------- --- --------
- Set interval_check[index[3]] = (interval_check[index[3]] + 0.25)
- -------- we increase the interval_check variable --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- interval_check[index[3]] Greater than or equal to (>=) damage_interval[index[3]]
-
Then - Actions
- Set interval_check[index[3]] = 0.00
- -------- we reset the interval_check variable --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- (target[index[3]] is alive) Equal to (==) True
-
Then - Actions
- Unit - Cause damage_source[index[3]] to damage target[index[3]], dealing dpi[index[3]] damage of attack type Spells and damage type Normal
- -------- we damage the target dealing damage per interval --------
- -------- --- --------
- Special Effect - Create a special effect attached to the chest of target[index[3]] using dpi_effect
- Special Effect - Destroy (Last created special effect)
- -------- we create and destroy the periodic damage effect on the unit, i prefer using chest attach point --------
-
Else - Actions
- Set duration[index[3]] = 0.00
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the target is alive we do actions if not we set the spell duration to 0 --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the interval_check is equal or larger than damage interval we do actions --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- duration[index[3]] Equal to (==) 0.00
-
Then - Actions
- Set index[1] = (index[1] - 1)
- -------- we reduce the index[1] variable cause one spell instance has ended --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- index[1] Equal to (==) 0
-
Then - Actions
- Set index[2] = 0
- -------- we reset the index[2] to 0 cause there aren't any active spells running --------
- -------- --- --------
- Trigger - Turn off (This trigger)
- -------- we turn off this trigger, it is pointless for it to run if there isn't any spell for it to affect --------
- -------- --- --------
- Game - Display to (All players) the text: off
- -------- we can display a "debug" message to check will the trigger turn off when there aren't any other active spell --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the index[1] is 0 there are no active spell and we do actions --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the spell duration is 0 we do actions --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
Loop - Actions
-
Do Multiple ActionsFor each (Integer index[3]) from 1 to index[2], do (Actions)
-
Events
All parts of the trigger are commented if you don't understand something feel free to pm me or post your question here.
There is a demo map attached which contains the spell we made feel free to test it's MUI-ness and does it bug.
http://www.hiveworkshop.com/forums/attachment.php?attachmentid=52316&stc=1&d=1239785960
-Using loops
Now i am not talking about loop trigger or anything periodic, i am talking about loops used in spells. An example of loop would be:-
Actions
-
Do Multiple ActionsFor each (Integer A) from 1 to 10, do (Actions)
- Loop - Actions
-
Do Multiple ActionsFor each (Integer A) from 1 to 10, do (Actions)
Now what can we do with loops? We can easily set up several functions like creating units/special effects and instead of writing a bunch fo lines to create
a circle effect we just run a loop for the desired number of effects.
In this example i will show you how to use loops to make an Incendiary Blast spell.
I suggest everyone to use channel to make custom spells cause it is intended for that.
The spell idea will be:
- deals x damage to the target
- if the target dies it will create several fire particles flying away(we will use loops for this)
- upon fall the fire particles will deal damage in 150 AOE
The dummy we are going to use will use a custom model called dummy.mdx which has no model at all but has attachment points.
Upon importing we set the dummy to use it and we set up our dummy changing:
- it's abilities(i always add invulnerable and locust ability)
- it's name
- it's classification(if it is a worker)
- change it's attacks enabled to none if attack is enables
- change it's colision to 0
- change it's movement type to fly(recommended)
- change it's food cost to 0
The dummy is prepared and now we make our trigger.
I will comment all the variables i used in the trigger to make it easier to understand.
-
IB init
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to (==) Incendiary Blast
-
Actions
- -------- First of all the first effect of the spell is instantly used so we can use array variables without indexing. --------
- -------- - --------
- Set IB_temp_real[0] = (50.00 + (100.00 x (Real((Level of (Ability being cast) for (Casting unit))))))
- -------- - --------
- Set IB_particle_count = 5
- -------- We are going to use this variable for our loop --------
- -------- - --------
- Set IB_owner = (Owner of (Casting unit))
- -------- - --------
- Set IB_leak_point[1] = (Position of (Target unit of ability being cast))
- -------- - --------
- Unit - Cause (Casting unit) to damage (Target unit of ability being cast), dealing IB_temp_real[0] damage of attack type Spells and damage type Normal
- Special Effect - Create a special effect attached to the chest of (Target unit of ability being cast) using Abilities\Spells\Other\Incinerate\FireLordDeathExplode.mdl
- Special Effect - Destroy (Last created special effect)
- -------- Causes instant damage and creates and destroys the explosion effect --------
-
-------- - --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- ((Target unit of ability being cast) is dead) Equal to (==) True
-
Then - Actions
- Set IB_temp_real[1] = (X of IB_leak_point[1])
- Set IB_temp_real[2] = (Y of IB_leak_point[1])
- Set IB_leak_point[2] = (Point(IB_temp_real[1], IB_temp_real[2]))
-
-------- We set up a point at which the particles start to move --------
-
Do Multiple ActionsFor each (Integer A) from 1 to IB_particle_count, do (Actions)
-
Loop - Actions
- -------- - --------
- -------- Now we are going to set up a dummy(missile) parameters and it's indexing --------
-
-------- - --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- IB_loop_index[1] Equal to (==) 0
-
Then - Actions
- Trigger - Turn on IB loop <gen>
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- - --------
- Set IB_loop_index[1] = (IB_loop_index[1] + 1)
- Set IB_loop_index[2] = (IB_loop_index[2] + 1)
- -------- We set up the integers used for indexing cause we will move the dummy unit every 0.03 second so we need indexing --------
- -------- - --------
- Set IB_loop_angle[IB_loop_index[2]] = (Random angle)
- -------- Defines the angle at which the particle moves --------
- -------- - --------
- Set IB_loop_max_distance[IB_loop_index[2]] = (Random real number between 150.00 and 300.00)
- -------- Defines the maximum distance which the missile travels --------
- -------- - --------
- Set IB_loop_damage_source[IB_loop_index[2]] = (Casting unit)
- -------- we store the caster for damage manipulation upon missile death --------
- -------- - --------
- Set IB_loop_damage[IB_loop_index[2]] = (25.00 + (25.00 x (Real((Level of (Ability being cast) for (Casting unit))))))
- -------- we set the damage dealt around the missile upon it's death --------
- -------- - --------
- Set IB_loop_max_height[IB_loop_index[2]] = (Random real number between 300.00 and 500.00)
- -------- - --------
- Set IB_loop_speed[IB_loop_index[2]] = 6.00
- -------- We define the missile speed, it shouldn't be too high value to ensure a nice look of the spell --------
- -------- - --------
- Unit - Create 1 Dummy for IB_owner at IB_leak_point[2] facing IB_loop_angle[IB_loop_index[2]] degrees
- Unit - Turn collision for (Last created unit) Off
- Set IB_loop_missile_unit[IB_loop_index[2]] = (Last created unit)
- -------- We create the dummy(missile) and set it to a variable for loop movement --------
- -------- - --------
- Animation - Change (Last created unit)'s size to (50.00%, 50.00%, 50.00%) of its original size
- -------- We can change the dummies size/scale to scale the model attached to it --------
- -------- - --------
- Special Effect - Create a special effect attached to the chest of (Last created unit) using Abilities\Weapons\RedDragonBreath\RedDragonMissile.mdl
- Set IB_loop_gfx[IB_loop_index[2]] = (Last created special effect)
- -------- We attach an model to the dummy and set the effect into a variable for later leak removal --------
- -------- - --------
- Set IB_loop_current_distance[IB_loop_index[2]] = 0.00
-
Loop - Actions
-
Do Multiple ActionsFor each (Integer A) from 1 to IB_particle_count, do (Actions)
- -------- We cycle through actions using integerA while the cycle number is defined by IB_particle_count variable --------
- Custom script: call RemoveLocation(udg_IB_leak_point[2])
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- Custom script: call RemoveLocation(udg_IB_leak_point[1])
- -------- If the target died we create particles via loop --------
- -------- - --------
- -------- - --------
- -------- That is all if the target is alive our spell has ended and there was no need for indexing --------
-
Events
Now we have set up our initial trigger and used the loop function for creating missile/particles instead of coping the same thing 5 times.
This is quite usefull cause it shortens the time needed to do a lot of things.
Now that we have set up our initial trigger we supose the target unit has died so we have created the dummy and now we need to move it.
Be aware that the model dummy.mdx i used doesn't support Z angle changing but there is a one that does.
I won't complicate the spell with that but i am going to use a parabola function which i will try to explaing better to you.
Here is the parabola code we are going to use:
JASS:
function ParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)
endfunction
The function takes the real h which represents the maximum height we want the missile to achieve.
The function takes the real d which represents the maximum distance we want the missile to be able to reach.
The function takes the real x which represents the current distance the missile has traveled.
Then the function calculates all the data we gave it and returns a height real.
It can also be done via GUI but it will be messy.
I will use a GUI version of parabola calculation.
Here is how the loop trigger will look like:
-
IB loop
-
Events
- Time - Every 0.03 seconds of game time
- Conditions
-
Actions
-
Do Multiple ActionsFor each (Integer IB_loop_index[3]) from 1 to IB_loop_index[2], do (Actions)
-
Loop - Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- IB_loop_missile_unit[IB_loop_index[3]] Not equal to (!=) No unit
-
Then - Actions
- Set IB_loop_leak_point[1] = (Position of IB_loop_missile_unit[IB_loop_index[3]])
- Set IB_loop_leak_point[2] = (IB_loop_leak_point[1] offset by IB_loop_speed[IB_loop_index[3]] towards IB_loop_angle[IB_loop_index[3]] degrees)
- -------- We store the points we are going to use to avoid leaks. --------
- -------- - --------
- Unit - Move IB_loop_missile_unit[IB_loop_index[3]] instantly to IB_loop_leak_point[2]
- -------- We move the dummy to the previously stored point --------
- -------- - --------
- Set IB_loop_current_distance[IB_loop_index[3]] = (IB_loop_current_distance[IB_loop_index[3]] + IB_loop_speed[IB_loop_index[3]])
- -------- We increase the current distance variable which is used for parabola and for distance checking, it's initial value is almost always 0 --------
- -------- - --------
- Set IB_loop_temp_real[1] = IB_loop_max_height[IB_loop_index[3]]
- Set IB_loop_temp_real[2] = IB_loop_max_distance[IB_loop_index[3]]
- Set IB_loop_temp_real[3] = IB_loop_current_distance[IB_loop_index[3]]
- -------- These are the variables we are going to use for parabola calculations, i used a IB_loop_temp_real variable to make it as simple as i can --------
- -------- - --------
- Custom script: set udg_IB_loop_temp_real[4] = ( 4 * udg_IB_loop_temp_real[1] / udg_IB_loop_temp_real[2]) * ( udg_IB_loop_temp_real[2] - udg_IB_loop_temp_real[3] ) * ( udg_IB_loop_temp_real[3] / udg_IB_loop_temp_real[2] )
- -------- This variable is the calculation of parabola and it's value it the height we are going to asign to the missile, i used a custom script cause it is much easier than to calculate it in GUI --------
- -------- - --------
- Animation - Change IB_loop_missile_unit[IB_loop_index[3]] flying height to IB_loop_temp_real[4] at 0.00
- -------- We aply the height change to the missile --------
-
-------- - --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- IB_loop_current_distance[IB_loop_index[3]] Greater than (>) IB_loop_max_distance[IB_loop_index[3]]
-
Then - Actions
- Custom script: if udg_IB_loop_tmp_group[udg_IB_loop_index[3]] == null then
- Custom script: set udg_IB_loop_tmp_group[udg_IB_loop_index[3]] = CreateGroup()
- Custom script: endif
- -------- These 3 lines of custom scripts are used to avoid leaks, if IB_loop_tmp_group is equal to nothing we create it, else we do nothing --------
- -------- - --------
- Set IB_loop_tmp_group[IB_loop_index[3]] = (Units within 200.00 of IB_loop_leak_point[2] matching ((((Matching unit) is A structure) Equal to (==) False) and ((((Matching unit) is alive) Equal to (==) True) and ((((Matching unit) is A flying unit) Equal to (==) False) and ((((Matching unit) is Magic I
- -------- Okay i know this looks like a really big complex thing but it's simple, we set the group to match all units within 200 AOE of position of our missile and then we add conditions --------
- -------- - --------
- Custom script: set bj_wantDestroyGroup = true
- -------- This custom script sets the next used group to be destroyed after it is used, i prefer manualy destroying groups with "call DestroyGroup(n)" than this but this is easy to remember --------
- -------- - --------
-
Unit Group - Pick every unit in IB_loop_tmp_group[IB_loop_index[3]] and do (Actions)
-
Loop - Actions
- Unit - Cause IB_loop_damage_source[IB_loop_index[3]] to damage (Picked unit), dealing IB_loop_damage[IB_loop_index[3]] damage of attack type Spells and damage type Normal
- Special Effect - Create a special effect attached to the chest of (Picked unit) using Abilities\Weapons\FireBallMissile\FireBallMissile.mdl
- Special Effect - Destroy (Last created special effect)
-
Loop - Actions
- -------- we pick every unit we put in the group previously, damage them and create + destroy a special effect --------
- -------- - --------
- Unit - Kill IB_loop_missile_unit[IB_loop_index[3]]
- -------- We kill the dummy, cause we need it no more --------
- -------- - --------
- Special Effect - Destroy IB_loop_gfx[IB_loop_index[3]]
- -------- we destroy the effect we attached to evade leaks --------
- -------- - --------
- Special Effect - Create a special effect at IB_loop_leak_point[2] using Abilities\Weapons\RedDragonBreath\RedDragonMissile.mdl
- -------- we create a effect at the impact point of the missile just for eyecandy --------
- -------- - --------
- Special Effect - Destroy (Last created special effect)
- -------- we destroy the last created effect to prevent leaks --------
- -------- - --------
- Custom script: set udg_IB_loop_missile_unit[udg_IB_loop_index[3]] = null
- -------- We null the missile_unit to make sure the actions won't repeat even if the effect has ended --------
- -------- - --------
-
Set IB_loop_index[1] = (IB_loop_index[1] - 1)
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- IB_loop_index[1] Equal to (==) 0
-
Then - Actions
- Set IB_loop_index[2] = 0
- Trigger - Turn off (This trigger)
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- i already explained what we do here before, if you don't know what i am talking about check the first part of the tutorial and the attached map. --------
- -------- - --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- - --------
- Custom script: set udg_IB_loop_temp_real[1] = 0
- Custom script: set udg_IB_loop_temp_real[2] = 0
- Custom script: set udg_IB_loop_temp_real[3] = 0
- Custom script: set udg_IB_loop_temp_real[4] = 0
- -------- setting the variables to 0 --------
- -------- - --------
- Custom script: call RemoveLocation(udg_IB_loop_leak_point[2])
- Custom script: call RemoveLocation(udg_IB_loop_leak_point[1])
- -------- removing leaks --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
Loop - Actions
-
Do Multiple ActionsFor each (Integer IB_loop_index[3]) from 1 to IB_loop_index[2], do (Actions)
-
Events
Now maybe you asked yourself why do we destroy special effects we create instantly? It prevents leaks but is still usefull cause it will make the speical effect play it's death animation.
Note: Some effects don't have death animation so they will play their whole animatiom(thunderclap for example) but this is most usefull for missile effects cause they all have death animations.
You may notice i used some custom scripts where i could have used GUI functions, why? I find it easier, it would be pain in GUI to calculate:
JASS:
( 4 * udg_IB_loop_temp_real[1] / udg_IB_loop_temp_real[2]) * ( udg_IB_loop_temp_real[2] - udg_IB_loop_temp_real[3] ) * ( udg_IB_loop_temp_real[3] / udg_IB_loop_temp_real[2] )
Also the most important thing about custom scripts is that you can't make a decent spell with locations and unit groups without them.
Why? You need them to clean leaks or it will affect the FPS.
Also you may notice i used point variables called IB_leak_point and IB_loop_leak_point and i didn't used indexes on them, only simple values.
I did that cause they are immediatly used and destroyed so there is no point in their indexing.
This also aplies to IB_temp_real and IB_loop_temp_real variables, but as reals cannot be destroyed and don't leak i just set their values to 0.
That concludes our loop part.
http://www.hiveworkshop.com/forums/attachment.php?attachmentid=52317&stc=1&d=1239785960
-Avoiding trigger overflow
What do i mean with this? I recently saw a map with 70-80 triggers and that was a system!Now what did the maker of the system do, he made a trigger like this:
-
Events
- Player - Player 1 (Red) Presses the Left Arrow key
- Conditions
- Actions
For each player and a 4 periodic triggers for each player.
Now to avoid that you can use array variables but what hapens when you use too much periodic triggers? It is uneficent.
To avoid that you can easily make a trigger that runs each 0.01 second and checks is the time to do actions now.
I wouldn't recommend doing this for all triggers but if you have a spell which uses multiple effects(example fading + location change) you can do the following:
-
Events
- Time - Every 0.01 seconds of game time
- Conditions
-
Actions
-
-------- I will comment only the first part of these variable cycles cause the second is the same --------
-
Do Multiple ActionsFor each (Integer General_index1[3]) from 1 to General_index1[2], do (Actions)
-
Loop - Actions
- Set interval_check1[General_index1[3]] = (interval_check1[General_index1[3]] + 0.01)
- -------- We increase our real array which we use for interval checking --------
-
-------- - --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- interval_check1[General_index1[3]] Greater than or equal to (>=) interval1[General_index1[3]]
-
Then - Actions
- Set interval_check1[General_index1[3]] = 0.00
- -------- Reseting the interval check variable --------
- -------- - --------
- -------- actions here --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the interval check variable value is equal to the interval variable value we reset the interval check variable value to 0 and do the actions we wanted --------
-
Loop - Actions
-
Do Multiple ActionsFor each (Integer General_index1[3]) from 1 to General_index1[2], do (Actions)
-
-------- - --------
-
Do Multiple ActionsFor each (Integer General_index2[3]) from 1 to General_index2[2], do (Actions)
-
Loop - Actions
-
Set interval_check2[General_index2[3]] = (interval_check2[General_index2[3]] + 0.01)
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- interval_check2[General_index2[3]] Greater than or equal to (>=) interval2[General_index2[3]]
-
Then - Actions
- Set interval_check2[General_index2[3]] = 0.00
- -------- actions here --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- actions here --------
-
Set interval_check2[General_index2[3]] = (interval_check2[General_index2[3]] + 0.01)
-
Loop - Actions
-
Do Multiple ActionsFor each (Integer General_index2[3]) from 1 to General_index2[2], do (Actions)
-
-------- - --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
Multiple ConditionsAnd - All (Conditions) are true
-
Conditions
- General_index1[1] Equal to (==) 0
- General_index2[1] Equal to (==) 0
-
Conditions
-
Multiple ConditionsAnd - All (Conditions) are true
-
Then - Actions
- -------- If the values of the primary indexes is 0 --------
- -------- - --------
- Set General_index1[2] = 0
- Set General_index2[2] = 0
- Trigger - Turn off (This trigger)
- -------- we reset the indexes we use for variables array and turn off the trigger --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- What we do here is we check for all indexes we used in the loop if their primary index value is 0 then if it is we set their secondary indexing value to 0 and turn off the trigger --------
-
-------- I will comment only the first part of these variable cycles cause the second is the same --------
And that is that, if all loop effects aren't active we just turn off the trigger and reset their indexes.
This concludes this part of the tutorial.
-PGS(Paladon's General System)
Not tested with latest versions of WC3, may cause issues.The map contains 3 trigger which are used as the PGS.
It contains:
- tree revival
- unit revival
- other stuff
http://www.hiveworkshop.com/forums/attachment.php?attachmentid=52320&stc=1&d=1239785960
Dynamic values storage with DIR system
First of all what is DIR system? Is an addition to the indexing system which makes the index used for actual indexing get recycled.
To impement it we are going to take example #1.
Now we are going to add several new variables:
- HS_dyn_index => Integer variable, not array
- HS_index_selected => Boolean variable, not array
- HS_index_is_empty => Boolean variable, array
First of all you should know how the the basics i wrote about in the upper section of the tutorial, if you don't know first read the upper part of the tutorial.
Now we are going to make some changes, first of all we won't be using HS_index[2] for indexing, instead we are going to use HS_dyn for indexing.
The second boolean variable is going to be used to make sure that HS_dyn_index carries an value which is not already taken.
The HS_index_is_empty[] we are going to use in the loop trigger for checking if the array is free again.
Now after reworking the initial HS init trigger it looks like this:
-
HS init
-
Events
- Unit - A unit Starts the effect of an ability
-
Conditions
- (Ability being cast) Equal to (==) Heart Stab
-
Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- HS_index[1] Equal to (==) 0
-
Then - Actions
- Trigger - Turn on loop <gen>
- Else - Actions
-
If - Conditions
- -------- We check is there an active spell instance running already by the value of index[1] --------
- -------- --- --------
- Set HS_index[1] = (HS_index[1] + 1)
- -------- We increase the index we are going to use for checking is there an active spell going on --------
- -------- --- --------
- Set HS_index_selected = False
- -------- We haven't selected the value of HS_dyn index which we are going to use for indexing so we set this to false --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- HS_dyn_index Less than (<) HS_index[2]
- HS_index_is_empty[HS_dyn_index] Equal to (==) True
- HS_dyn_index Not equal to (!=) 0
-
Then - Actions
- -------- If the conditions are true that mean we already declared a HS_dyn_index in the loop trigger and it's unused so we don't set the HS_dyn index to any other value, we just simply leave it at the current value and set HS_index_selected = true --------
- Set HS_index_selected = True
-
Else - Actions
-
Do Multiple ActionsFor each (Integer HS_index[0]) from 1 to HS_index[2], do (Actions)
-
Loop - Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- HS_index_selected Equal to (==) False
- HS_index_is_empty[HS_index[0]] Equal to (==) True
-
Then - Actions
- -------- If we have found an unused array and the HS_dyn_index isn't already selected we set the HS_dyn_index to that value and set the HS_index_selected to true to make sure the loop stops --------
- Set HS_dyn_index = HS_index[0]
- -------- We set the value of the dynamic index to first empty array value and declare that we have selected the index --------
- -------- --- --------
- Set HS_index_selected = True
- -------- We declare that the index is selected so the loop stops --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
Loop - Actions
- -------- We cycle throug HS_index[2] searching for an empty index by using HS_index_is_empy boolean, i am going to use HS_index[0] for cycling in this case --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- HS_index_selected Equal to (==) False
-
Then - Actions
- -------- If HS_index_selected is false that means we didn't found any free array value so we increase the value of HS_index[2] and set the value of HS_dyn_index to it so we avoid conflicts --------
- Set HS_index[2] = (HS_index[2] + 1)
- -------- Increasing HS_index[2] value by 1 --------
- -------- --- --------
- Set HS_dyn_index = HS_index[2]
- -------- Setting the HS_dyn_index to the value of HS_index[2] --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- If there weren't any free array value when we cycled through the HS_index[2] we increase the HS_index[2] value and set the HS_dyn_index to match the value of HS_index[2] --------
-
Do Multiple ActionsFor each (Integer HS_index[0]) from 1 to HS_index[2], do (Actions)
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- Game - Display to (All players) the text: (Debug Message: Storing Index Value: + (String(HS_dyn_index)))
- -------- Dynamic Index Recycling --------
- Set HS_index_is_empty[HS_dyn_index] = False
- -------- We now set the HS_index_is_empty to false cause we are going to use the HS_dyn_index for an array for indexing --------
- -------- --- --------
- Set damage_source[HS_dyn_index] = (Casting unit)
- -------- we store the caster for damaging unit refrence --------
- -------- --- --------
- Set target[HS_dyn_index] = (Target unit of ability being cast)
- -------- we store the target for the periodic events --------
- -------- --- --------
- Set damage_interval[HS_dyn_index] = (1.25 - (0.25 x (Real((Level of (Ability being cast) for (Casting unit))))))
- -------- we define the damage interval --------
- -------- --- --------
- Set dpi[HS_dyn_index] = (10.00 + (5.00 x (Real((Level of (Ability being cast) for (Casting unit))))))
- -------- we define the damage dealt per interval to the target --------
- -------- --- --------
- Set duration[HS_dyn_index] = (5.00 + (5.00 x (Real((Level of (Ability being cast) for (Casting unit))))))
- -------- we define the spell duration --------
- -------- --- --------
- Set dpi_effect = Objects\Spawnmodels\Human\HumanBlood\HumanBloodFootman.mdl
- -------- on the end we define the periodic damage effect string this shouldn't need to be a array variable if you don't plan on changing it depending on the spell level --------
- -------- --- --------
- Set interval_check[HS_dyn_index] = 0.00
- -------- variable used for damage interval checking --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
Events
Now we aren't finished we still need to dynamicaly free the index arrays when they aren't used we will do so in the loop trigger:
-
loop
-
Events
- Time - Every 0.25 seconds of game time
- Conditions
-
Actions
-
Do Multiple ActionsFor each (Integer HS_index[3]) from 1 to HS_index[2], do (Actions)
-
Loop - Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- duration[HS_index[3]] Greater than (>) 0.00
-
Then - Actions
- Set duration[HS_index[3]] = (duration[HS_index[3]] - 0.25)
- -------- we decrease the spell duration by 0.25 --------
- -------- --- --------
- Set interval_check[HS_index[3]] = (interval_check[HS_index[3]] + 0.25)
- -------- we increase the interval_check variable --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- interval_check[HS_index[3]] Greater than or equal to (>=) damage_interval[HS_index[3]]
-
Then - Actions
- Set interval_check[HS_index[3]] = 0.00
- -------- we reset the interval_check variable --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- (target[HS_index[3]] is alive) Equal to (==) True
-
Then - Actions
- Unit - Cause damage_source[HS_index[3]] to damage target[HS_index[3]], dealing dpi[HS_index[3]] damage of attack type Spells and damage type Normal
- -------- we damage the target dealing damage per interval --------
- -------- --- --------
- Special Effect - Create a special effect attached to the chest of target[HS_index[3]] using dpi_effect
- Special Effect - Destroy (Last created special effect)
- -------- we create and destroy the periodic damage effect on the unit, i prefer using chest attach point --------
-
Else - Actions
- Set duration[HS_index[3]] = 0.00
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the target is alive we do actions if not we set the spell duration to 0 --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the interval_check is equal or larger than damage interval we do actions --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- duration[HS_index[3]] Less than or equal to (<=) 0.00
-
Then - Actions
- Set HS_index[1] = (HS_index[1] - 1)
- -------- we reduce the index[1] variable cause one spell instance has ended --------
- -------- --- --------
- Set HS_index_is_empty[HS_index[3]] = True
- -------- This spell instance has finished it's effect and we "free" the array using this variable --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- HS_index_selected Equal to (==) False
-
Then - Actions
- Set HS_dyn_index = HS_index[3]
- -------- We set the HS_dyn_index to an value of this index so the next time when the spell is cast it has an value of an array that is not taken --------
- -------- --- --------
- Set HS_index_selected = True
- -------- We have just set an value for HS_dyn_index so we have selected the index we are going to use the next time a spell using this indexing is cast --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- HS_index[1] Not equal to (!=) 0
- HS_index[3] Equal to (==) HS_index[2]
-
Then - Actions
- Set HS_index[2] = (HS_index[2] - 1)
- Game - Display to (All players) the text: Debug Message: Loop...
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- If this is the spell instance which uses the highest value of HS_index[2] we can reduce the HS_index[2] value to shorten the loop process --------
-
-------- --- --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- HS_index[1] Equal to (==) 0
-
Then - Actions
- Set HS_index[2] = 0
- -------- we reset the index[2] to 0 cause there aren't any active spells running --------
- -------- --- --------
- Set HS_dyn_index = 0
- -------- We also reset the HS_dyn_index to 0 --------
- -------- --- --------
- Trigger - Turn off (This trigger)
- -------- we turn off this trigger, it is pointless for it to run if there isn't any spell for it to affect --------
- -------- --- --------
- Game - Display to (All players) the text: off
- -------- we can display a "debug" message to check will the trigger turn off when there aren't any other active spell --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the index[1] is 0 there are no active spell and we do actions --------
- Else - Actions
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
- -------- if the spell duration is 0 we do actions --------
-
Else - Actions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
- HS_index[1] Not equal to (!=) 0
- HS_index[3] Equal to (==) HS_index[2]
-
Then - Actions
- Set HS_index[2] = (HS_index[2] - 1)
- Game - Display to (All players) the text: Debug Message: Loop...
- Else - Actions
-
If - Conditions
- -------- If this is the spell instance which uses the highest value of HS_index[2] we can reduce the HS_index[2] value to shorten the loop process and cause it didn't mathc the condition it means it isn't doing anything so we decrease the index --------
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
Loop - Actions
-
Do Multiple ActionsFor each (Integer HS_index[3]) from 1 to HS_index[2], do (Actions)
-
Events
This way there is a possibility to exceed maximum array size and to do it you must:
- Cast the spell every 1 second
- If we take the spell lasts 15 seconds that means 15 instances per unit max
- So that means you will exceed the array limit if you have about 550 units casting the spell in the same time every single second
I think the tutorial may be aproved now that i fixed the issue and implanted Dynamic Index Recycling in the tutorial.
Demo Map:
http://www.hiveworkshop.com/forums/attachment.php?attachmentid=52319&stc=1&d=1239786126
NOTE: The name of the map is MUI example 4 cause i deleted a part of my tutorial that had the MUI example 3 map.
Credits:
Moyack for the parabola formula
Now this is where the tutorial ends for now. I may add more informations and chapters but for now this should do.
I hope this helped you^^
Attachments
Last edited by a moderator: