- Joined
- Sep 14, 2012
- Messages
- 3,413
Hi everybody !
Today, I’ll explain you how to use properly dynamic indexing with structs (so in vJASS )!
I’ll not explain how struct works (it is not the goal of this tutorial) but how to properly index them to render spells MUI.
I – Introduction
Okay I hope you know how works struct because as I just said before I’ll not explain how they work.
I’ll show two manners to do this indexing :
- The first way is C-like which means that we will use structs just as data.
- The second way is Java-like which means that we will use methods and static member to do so.
I hope you know a bit how work indexing otherwise I’ll explain this just now (and again later in the code commentaries).
Dynamic indexing’s goal is to do MUI spells!
If you ever did so in GUI you remembered that it needs one array for each thing you want to store about the spell. Now with structs we will just have to store the struct inside a struct array and there we go, we will be able to access every member!
To do dynamic indexing we also need an index that will increase when an instance will fire and that will decrease when an instance will be finished and recycled.
II – What spell will we do?
We will do a timed damage spell.
It will deal 75 damage per second over 3 seconds.
So we will have to store:
- The caster of the spell (to know which unit will deal damages) => caster
- The target of the spell (to know which unit will get damaged) => target
- The amount of damage => damage
- The time there is before the damaging => temp
- How many times will we have to hit the target before the end of the spell => steps
So we got our struct members just above!
Now, how the spell will work: the caster cast the spell, then 1s later the target got the damage. So we will need a periodic function that decrease the temp value and when the temp value is below or equal to 0 we will damage the target and recycle the index.
III – The spell
So we will start with global variables we need:
So there is our index that will start to -1 and each time an instance fire we will increase it by one so we will start to the index 0. We got our FPS of 0.0312500 which is surely one of the best period interval you can have.
Why it is a good period ? Because 1/0.0312500=32 so we have an integer number for the number of period per second
And at last we create a timer.
Now we will create our struct:
Now we need a struct array!
So we will just add it to our global block:
Okay now we will use a scope to implement our spell so the code looks like this :
Okay I implemented the init function and the condition function (with nothing inside ^-^)!
Now we will implement the beginning of the condition function!
Ok so I chose 75*level and 1s for fun you can freely change them or better store them into globals block for the second) or into constant function before the core spell but I’ll just interest myself to the indexing.
So I’ll implement the indexing :
So to rephrase this we increase our index, we store the struct instance and if the array was empty so we start our timer.
Sweet clear isn’t it ?
Now let’s do our periodic function:
With this we’ll loop through every struct instance we stored before.
Now we will do the core of the spell (nothing complicated xD) : we have to decrease the temp value of FPS each time we run the periodic function and if the temp value is expired we have to damage thetargeted unit :
Now we have to recycle our index:
To explain this piece of code we put the last stored instance to the position where an instance has been destroyed and we decrement of one both index.
Now we implement it:
Okay now there is still one problem !
The timer will run forever even if we got no instance :/
So we’ll have to check if your index is equal to -1 (meaning the array is empty):
So here is the result :
IV - Now within the struct
I'll put the code with commentary to show you how to do it only in the struct :
Conclusion
Here you made a MUI spell in vJASS.
Feel free to add more configurable things like this after the globals to make this more user-friendly :
Thanks for reading and feel free to tell me what do you think of it !
Malhorne
Today, I’ll explain you how to use properly dynamic indexing with structs (so in vJASS )!
I’ll not explain how struct works (it is not the goal of this tutorial) but how to properly index them to render spells MUI.
I – Introduction
Okay I hope you know how works struct because as I just said before I’ll not explain how they work.
I’ll show two manners to do this indexing :
- The first way is C-like which means that we will use structs just as data.
- The second way is Java-like which means that we will use methods and static member to do so.
I hope you know a bit how work indexing otherwise I’ll explain this just now (and again later in the code commentaries).
Dynamic indexing’s goal is to do MUI spells!
If you ever did so in GUI you remembered that it needs one array for each thing you want to store about the spell. Now with structs we will just have to store the struct inside a struct array and there we go, we will be able to access every member!
To do dynamic indexing we also need an index that will increase when an instance will fire and that will decrease when an instance will be finished and recycled.
II – What spell will we do?
We will do a timed damage spell.
It will deal 75 damage per second over 3 seconds.
So we will have to store:
- The caster of the spell (to know which unit will deal damages) => caster
- The target of the spell (to know which unit will get damaged) => target
- The amount of damage => damage
- The time there is before the damaging => temp
- How many times will we have to hit the target before the end of the spell => steps
So we got our struct members just above!
Now, how the spell will work: the caster cast the spell, then 1s later the target got the damage. So we will need a periodic function that decrease the temp value and when the temp value is below or equal to 0 we will damage the target and recycle the index.
III – The spell
So we will start with global variables we need:
JASS:
globals
private integer dindex = -1
private constant real FPS = 0.0312500
private timer period = CreateTimer()
endglobals
Why it is a good period ? Because 1/0.0312500=32 so we have an integer number for the number of period per second
And at last we create a timer.
Now we will create our struct:
JASS:
private struct tempDat
unit caster
unit target
real temp
real damage
integer steps
endstruct
Now we need a struct array!
So we will just add it to our global block:
JASS:
globals
private integer dindex = -1
private constant real FPS = 0.0312500
private tempDat array data
private timer period = CreateTimer()
endglobals
JASS:
scope MySuperCoolSpell initializer init
globals
private integer dindex = -1
private constant real FPS = 0.0312500
private tempDat array data
private timer period = CreateTimer()
endglobals
private struct tempDat
unit caster
unit target
real temp
real damage
integer steps
endstruct
private function cond takes nothing returns boolean
if GetSpellAbilityId() == OurSpellId then
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function cond))
set t = null
endfunction
endscope
Now we will implement the beginning of the condition function!
JASS:
private function cond takes nothing returns boolean
local tempDat this
if GetSpellAbilityId() == OurSpellId then
//We create an instance of tempDat
set this = tempDat.create()
//We store the caster of the spell
set this.caster = GetTriggerUnit()
//We store the target of the spell
set this.target = GetSpellTargetUnit()
//We store the damage of the spell
set this.damage = 75 * GetUnitAbilityLevel(this.caster, OurSpellId)
//We store in how many times the spell will deal damage.
set this.temp = 1
//We will do the damage three times.
set this.steps = 3
endif
return false
endfunction
So I’ll implement the indexing :
JASS:
private function cond takes nothing returns boolean
local tempDat this
if GetSpellAbilityId() == OurSpellId then
//We create an instance of tempDat
set this = tempDat.create()
//We store the caster of the spell
set this.caster = GetTriggerUnit()
//We store the target of the spell
set this.target = GetSpellTargetUnit()
//We store the damage of the spell
set this.damage = 75 * GetUnitAbilityLevel(this.caster, OurSpellId)
//We store in how many times the spell will deal damage.
set this.temp = 1
// We will do the damage three times.
set this.steps = 3
//We increase our index by one.
set dindex = dindex + 1
//We store our struct instance into the array.
set data[dindex] = this
//if the array was empty before
if dindex == 0 then
//We start the timer (I didn’t create the periodic function for the moment wait !)
call TimerStart(period, FPS, true, function periodic)
endif
endif
return false
endfunction
Sweet clear isn’t it ?
Now let’s do our periodic function:
JASS:
private function periodic takes nothing returns nothing
local tempDat this
local integer i = 0
loop
exitwhen i>dindex
set this = data[i]
…
set i = i + 1
endloop
endfunction
Now we will do the core of the spell (nothing complicated xD) : we have to decrease the temp value of FPS each time we run the periodic function and if the temp value is expired we have to damage thetargeted unit :
JASS:
private function periodic takes nothing returns nothing
local integer i = 0
local tempDat this
loop
exitwhen i>dindex
set this = data[i]
set this.temp = this.temp-FPS
if this.temp <= 0 then
call UnitDamageTarget(this.caster, this.target, this.damage, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC,null)
//We decrement the number of times we damaged the unit.
set this.steps = this.steps - 1
//If it the third time the unit is damaged so we will stop the spell
if this.steps == 0 then
//Wash leaks :)
set this.caster = null
//Wash leaks :)
set this.target = null
//The spell is finish we don’t need to have this struct anymore.
//We will see the recycle after
call this.destroy()
//Otherwise we continue the spell by setting the temp value to 1 again.
else
set this.temp = 1
endif
endif
set i = i + 1
endloop
endfunction
JASS:
set data[i] = data[dindex]
set i = i – 1
set dindex = dindex – 1
Now we implement it:
JASS:
private function periodic takes nothing returns nothing
local integer i = 0
local tempDat this
loop
exitwhen i>dindex
set this = data[i]
set this.temp = this.temp-FPS
if this.temp <= 0 then
call UnitDamageTarget(this.caster, this.target, this.damage, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC,null)
//We decrement the number of times we damaged the unit.
set this.steps = this.steps - 1
//If it the third time the unit is damaged so we will stop the spell
if this.steps == 0 then
//Wash leaks :)
set this.caster = null
//Wash leaks :)
set this.target = null
set data[i] = data[dindex]
set i = i – 1
set dindex = dindex – 1
//The spell is finish we don’t need to have this struct anymore.
call this.destroy()
//Otherwise we continue the spell by setting the temp value to 1 again.
else
set this.temp = 1
endif
endif
set i = i + 1
endloop
endfunction
The timer will run forever even if we got no instance :/
So we’ll have to check if your index is equal to -1 (meaning the array is empty):
JASS:
if dindex == -1 then
call PauseTimer(period)
endif
So here is the result :
JASS:
scope MySuperCoolSpell initializer init
globals
private integer dindex = -1
private constant real FPS = 0.0312500
private timer period = CreateTimer()
endglobals
private struct tempDat
unit caster
unit target
real temp
real damage
integer steps
method destroy takes nothing returns nothing
//Wash leaks
this.caster = null
this.target = null
if dindex == -1 then
call PauseTimer(period)
endif
call this.deallocate()
endmethod
endstruct
//Sometimes it bugs if you declare this before the struct so in the final result I put it after for safety.
globals
private tempDat array data
endglobals
private function periodic takes nothing returns nothing
local integer i = 0
local tempDat this
loop
exitwhen i>dindex
set this = data[i]
set this.temp = this.temp-FPS
if this.temp <= 0 then
call UnitDamageTarget(this.caster, this.target, this.damage, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC,null)
set this.steps = this.steps - 1
if this.steps == 0 then
set data[i] = data[dindex]
set i = i - 1
set dindex = dindex - 1
call this.destroy()
else
set this.temp = 1
endif
endif
set i = i + 1
endloop
endfunction
private function cond takes nothing returns boolean
local tempDat this
if GetSpellAbilityId() == 'A000' then
set this = tempDat.create()
set this.caster = GetTriggerUnit()
set this.target = GetSpellTargetUnit()
set this.temp = 1
set this.damage = 75*GetUnitAbilityLevel(this.caster, 'A000')
set this.steps = 3
set dindex = dindex + 1
set data[dindex] = this
if dindex == 0 then
call TimerStart(period, FPS, true, function periodic)
endif
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function cond))
set t = null
endfunction
endscope
IV - Now within the struct
I'll put the code with commentary to show you how to do it only in the struct :
JASS:
scope MySuperCoolSpell
globals
private constant real FPS = 0.0312500
endglobals
private struct tempDat
unit caster
unit target
real temp
real damage
integer steps
//Static members work like globals :)
static integer dindex
static timer period
static thistype array data
method destroy takes nothing returns nothing
//Wash leaks
this.caster = null
this.target = null
if dindex == -1 then
call PauseTimer(period)
endif
call this.deallocate()
endmethod
static method periodic takes nothing returns nothing
local integer i = 0
local tempDat this
loop
exitwhen i>dindex
set this = data[i]
set this.temp = this.temp-FPS
if this.temp <= 0 then
call UnitDamageTarget(this.caster, this.target, this.damage, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC,null)
set this.steps = this.steps - 1
if this.steps == 0 then
set data[i] = data[dindex]
set i = i - 1
set dindex = dindex - 1
call this.destroy()
else
set this.temp = 1
endif
endif
set i = i + 1
endloop
endmethod
static method cond takes nothing returns boolean
//Thistype is replaced by the name of the struct :)
local thistype this
if GetSpellAbilityId() == 'A000' then
set this = thistype.allocate() //Exactly the same as create
set this.caster = GetTriggerUnit()
set this.target = GetSpellTargetUnit()
set this.temp = 1
set this.damage = 75*GetUnitAbilityLevel(this.caster, 'A000')
set this.steps = 3
set dindex = dindex + 1
set data[dindex] = this
if dindex == 0 then
call TimerStart(period, FPS, true, function thistype.periodic)
endif
endif
return false
endmethod
//This method is called at the begginning of the map.
//The keyword onInit does it.
static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.cond))
//I personally prefer to instanciate static members there just a point of view
//You can do it in their declaration too it is the same.
set dindex = -1
set period = CreateTimer()
set t = null
endmethod
endstruct
endscope
Conclusion
Here you made a MUI spell in vJASS.
Feel free to add more configurable things like this after the globals to make this more user-friendly :
JASS:
private constant function Damage takes integer level returns real
return 75.*level
endfunction
Thanks for reading and feel free to tell me what do you think of it !
Malhorne
Last edited: