- Joined
- Dec 12, 2008
- Messages
- 7,385
vJASS Spell Templates
Table of Contents
Introduction
This is more of a repository or reference rather than a tutorial.
I'm going to show you some of the cleanest and most ideal spell
models you could follow. These models have no requirements at all.
Instant Spells
Instant spells have one and only one model in my eyes.
This is the ideal model for an instant spell:
JASS:
struct Spell extends array
private static constant integer ABIL_CODE = 'A000'
private static constant integer BUFF_CODE = 'B000'
private static constant integer UNIT_CODE = 'u000'
private static method run takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
// Code.
set caster = null
set target = null
endmethod
private static method check takes nothing returns boolean
if GetSpellAbilityId() == thistype.ABIL_CODE then
call thistype.run()
endif
return false
endmethod
private 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.check))
set t = null
endmethod
endstruct
Timed Spells
No matter what kind of timed spell you need, this is the model that I see fit.
Even if you didn't need a periodic spell, this model would assure that you will
only be needing one and only one timer at all times.
If you wanted a spell that would wait for a small amount of time, execute
some code and then end, you would use an integer that is decreased after
each iteration. When the integer is 0, you would execute some code and
destroy the instance.
This is the spell model that I use:
JASS:
struct Spell extends array
private static constant integer ABIL_CODE = 'A000'
private static constant integer BUFF_CODE = 'B000'
private static constant integer UNIT_CODE = 'u000'
private static constant real TIMEOUT = 0.03125
private static integer array rn
private static integer ic = 0
private static integer array next
private static integer array prev
private static timer iterator = CreateTimer()
private static integer count = 0
private static unit array caster
private static unit array target
private static real array damage
method destroy takes nothing returns nothing
/*
* Deallocate this instance.
*/
set rn[this] = rn[0]
set rn[0] = this
/*
* We remove the instance from the linked list.
*/
set prev[next[this]] = prev[this]
set next[prev[this]] = next[this]
/*
* We decrease the count by 1.
* If the count is 0, we pause the timer.
*/
set count = count - 1
if count == 0 then
call PauseTimer(iterator)
endif
/*
* We null the data in the struct.
* This is completely optional. It doesn't really make a difference
* at all. (Unless you're casting the spell some hundreds of times.)
* If you have some real memory intense systems in your map,
* you might want to do this, especially if your struct has a lot of data.
*
* These are global variables, so they will be recycled eventually.
* It's all up to you, my friend.
*/
set caster[this] = null
set target[this] = null
// Code.
endmethod
private static method periodic takes nothing returns nothing
/*
* Starting from the first instance, we loop
* over all the instance in the list until we hit
* a dead-end.
*/
local thistype this = next[0]
loop
exitwhen this == 0
// Code.
set this = next[this]
endloop
endmethod
private static method run takes nothing returns nothing
/*
* We allocate an instance.
*/
local thistype this = rn[0]
if this == 0 then
set ic = ic + 1
set this = ic
else
set rn[0] = rn[this]
endif
/*
* We add the instance to the linked list.
*/
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
/*
* We increase the count by 1.
* If the count is 1, we start the timer to loop through
* the instances. This is because recasting the spell while
* an instance is already running shouldn't restart the timer.
*/
set count = count + 1
if count == 1 then
call TimerStart(iterator, TIMEOUT, true, function thistype.periodic)
endif
/*
* We set our struct data.
*/
set caster[this] = GetTriggerUnit()
set target[this] = GetSpellTargetUnit()
// Code.
endmethod
private static method check takes nothing returns boolean
if GetSpellAbilityId() == thistype.ABIL_CODE then
call thistype.run()
endif
return false
endmethod
private 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.check))
set t = null
endmethod
endstruct
What I'm doing inside the above model is using my own allocation/deallocation technique.
This tutorial by Nestharus shows you how to make your own custom allocator. In fact, if
you don't like writing your own and don't want to learn how to, you can use resources
like Alloc by Sevion.
You would also notice that I made the struct arrays explicit.
Most people hate this as well.
So, the below model does the exact same thing that the above one does, except it
embraces the dot operator and it uses JassHelper's generated allocator which is totally fine.
JASS:
struct Spell
private static constant integer ABIL_CODE = 'A000'
private static constant integer BUFF_CODE = 'B000'
private static constant integer UNIT_CODE = 'u000'
private static constant real TIMEOUT = 0.03125
private thistype next
private thistype prev
private static timer iterator = CreateTimer()
private static integer count = 0
private unit caster
private unit target
private real damage
private method destroy takes nothing returns nothing
/*
* Deallocate this instance.
*/
call this.deallocate()
/*
* We remove the instance from the linked list.
*/
set this.next.prev = this.prev
set this.prev.next = this.next
/*
* We decrease the count by 1.
* If the count is 0, we pause the timer.
*/
set count = count - 1
if count == 0 then
call PauseTimer(iterator)
endif
/*
* We null the data in the struct.
* This is completely optional. It doesn't really make a difference
* at all. (Unless you're casting the spell some hundreds of times.)
* If you have some real memory intense systems in your map,
* you might want to do this, especially if your struct has a lot of data.
*
* These are global variables, so they will be recycled eventually.
* It's all up to you, my friend.
*/
set this.caster = null
set this.target = null
// Code.
endmethod
private static method periodic takes nothing returns nothing
/*
* Starting from the first instance, we loop
* over all the instance in the list until we hit
* a dead-end.
*/
local thistype this = thistype(0).next
loop
exitwhen this == 0
// Code.
set this = this.next
endloop
endmethod
private static method run takes nothing returns nothing
/*
* We allocate an instance.
*/
local thistype this = thistype.allocate()
/*
* We add the instance to the linked list.
*/
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
/*
* We increase the count by 1.
* If the count is 1, we start the timer to loop through
* the instances. This is because recasting the spell while
* an instance is already running shouldn't restart the timer.
*/
set count = count + 1
if count == 1 then
call TimerStart(iterator, TIMEOUT, true, function thistype.periodic)
endif
/*
* We set our struct data.
*/
set this.caster = GetTriggerUnit()
set this.target = GetSpellTargetUnit()
// Code.
endmethod
private static method check takes nothing returns boolean
if GetSpellAbilityId() == thistype.ABIL_CODE then
call thistype.run()
endif
return false
endmethod
private 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.check))
set t = null
endmethod
endstruct
Public Resources
Please note that the above models shouldn't be used as is for public resources.
In public resources, I would recommend wrapping all the above code in
a library block so that you could make the requirements explicit and clear.
The models I depicted above should only be used for Map-making.
They were made to be easy to understand, efficient and clean.
Wrap-Up
This concludes my very short and 'straight to the point' tutorial.
Please note, I'm not saying that you should be using these spell models,
I'm merely offering you some pretty good ones in case you don't know
any. If you have any questions, feel free to ask.
Last edited by a moderator: