Name | Type | is_array | initial_value |
//TESH.scrollpos=41
//TESH.alwaysfold=0
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
========================================= Spell Engine ==========================================
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
------------------------------------------ Created by: ------------------------------------------
------------------------------------------ aznricepuff ------------------------------------------
Spell Engine is an object-oriented event-driven system for handling spell-cast detection and
spell manipulation. This system does not replace xecast or any other caster system; its primary
purpose is event-handling, not dummy casting or caster recycling.
This system is meant to provide flexibility for creating custom spells that go beyond the rather
simple dynamics offered by wc3's ability system. For example, with Spell Engine you can create
spells that have dynamic casting times, dynamic channeling times, and custom interrupts all
from dummy abilities based off Channel.
======================================== Implementation =========================================
Spell Engine requires:
-Table, by Vexorian
-TreeSet
-jasshelper (0.9.H.0+), by Vexorian
-Object Merger, by grimoire
Make sure you meet all of the requirements above before attempting to implement Spell Engine.
The demo map comes with Table and TreeSet.
Once you meet all of the requirements, you can simply copy the Spell Engine trigger into your
map. Save your map, and during the jasshelper phase, you should see a step that says,
"Executing External Commands". After saving the map once, you can comment out the two lines in
the Spell Engine trigger:
//! external ObjectMerger w3a Aasl IMBA anam "Immobilized" ansf "" Slo1 1 -10.00 aare 1 100.0 abuf 1 IMBB arac "other" atar 1 "self"
//! external ObjectMerger w3h Basl IMBB fnam "Immobilized" ftip "Immobilized" fnsf "" fube "This unit is immobilized." fart "ReplaceableTextures\CommandButtons\BTNCancel.blp" ftat ""
========================================== Limitations ==========================================
Spell Engine uses a Slow Aura ability set to -1000% movement speed penalty to immobilize units
during casting and channeling. Because of this, the system can bug if you have any ability in
your map that increases movement speed by more than 900% (which would be crazy).
============================================ A Note =============================================
The remaining sections in this documentation are meant for users with only a basic understanding
of object-oriented event-driven programming. More advanced users may wish to refer directly
to the API, which contains all the pertinent information in this documentation as well as more
technical specifications of the components of this system.
======================================== Getting Started ========================================
The part of Spell Engine with which you be interacting the most is the Spell struct. This struct
is meant to be extended by your own structs that essentially define what will happen when a
spell is cast.
The constructor of Spell takes in a single integer parameter. This parameter is simply the
ability rawcode (or ability id) of the object editor ability with which the new Spell instance
will be associated. After instantiation, you may access the Spell.abilId field to get the
ability id with with a Spell is associated.
Simply instantiating a Spell is not sufficient for it to begin working. You must register the
Spell with the system. To do this, use SpellController.registerSpell(). A Spell may only be
registered once and only one Spell may be registered for the same ability id. Once a Spell is
registered, it will be called on spell events tied to its ability id. You may also unregister
a Spell using SpellController.removeSpell().
Spell has ten stub methods that by default don't do much. These stub methods are event-response
methods. Each one is called when a certain event occurs. You must override these stub methods
in your own structs that extend Spell to define specific behavior.
The event-response methods are:
------
stub method onInitialize takes EventSpell eventSpell returns nothing
stub method onCastBegin takes EventSpell eventSpell returns nothing
stub method onCastTick takes EventSpell eventSpell, real period returns nothing
stub method onCastFinish takes EventSpell eventSpell returns nothing
stub method onEffect takes EventSpell eventSpell returns nothing
stub method onChannelBegin takes EventSpell eventSpell returns nothing
stub method onChannelTick takes EventSpell eventSpell, real period returns nothing
stub method onChannelFinish takes EventSpell eventSpell returns nothing
stub method onTerminate takes EventSpell eventSpell, unit source, integer data returns boolean
stub method onFinalTerminate takes EventSpell eventSpell, unit source, integer data returns
nothing
stub method onEnd takes EventSpell eventSpell returns nothing
------
For the specifics on under which circumstances each one is called, refer to the Spell API.
Notice that all of the event-response functions take in a parameter of type EventSpell. What is
EventSpell? It is a struct that holds data on the spell event that the methods were triggered
by. EventSpell has the following fields:
------
readonly Spell spell
integer level
readonly unit caster
boolean channeling = false
unit targetUnit
item targetItem
destructable targetDestructable
real targetX
real targetY
integer data
readonly integer targeting
readonly integer phase
real castTime = 0.0
real channelTime = 0.0
readonly real castTimeElapsed
readonly real channelTimeElapsed
------
The values shown above are the default values of their respective fields, if a default value
exists.
Most of the fields should be self-explanatory, but I want to explain what EventSpell.targeting
and EventSpell.phase are.
EventSpell.targeting is an integer that defines what type of target the spell has. You may
compare the value in this field to the following constants in Spell:
------
static constant integer TARGET_INSTANT
static constant integer TARGET_UNIT
static constant integer TARGET_ITEM
static constant integer TARGET_DESTRUCTABLE
static constant integer TARGET_POINT
------
EventSpell.phase defines in which phase of its cast cycle the event spell currently is. You may
compare the value in this field to the following constants in EventSpell:
------
static constant integer PHASE_INITIALIZE
static constant integer PHASE_PRECAST
static constant integer PHASE_CAST
static constant integer PHASE_EFFECT
static constant integer PHASE_CHANNEL
------
For more information about what these constants mean, refer to the Spell and EventSpell APIs.
For more specific information on any of the fields in EventSpell, refer to the EventSpell API.
EventSpell also has two methods:
------
method terminate takes unit source, integer data returns boolean
method forceTerminate takes unit source, integer data returns nothing
------
The first method essentially stops the casting of the event spell. For example, you may use it
to interrupt the spell. The unit parameter takes in the source of the interruption (which may be
the caster itself, or null). The method returns whether or not the spell was successfully
terminated (or interrupted). The second method does the same thing only it guarantees that the
spell will be terminated.
Finally, EventSpell delegates to SpellType, which means that you can access any instance member
contained in SpellType from instances of EventSpell. For more information on SpellType, refer to
the SpellType Documentation and API.
======================================== Spell Listeners ========================================
The Spell struct allows you to define event responses for individual spells, but there are cases
where you may want to define responses for types of spells. For this purpose, you should use the
SpellListener struct.
SpellListener is set up similarly to Spell. It contains all of the ten event-reponse methods
found in Spell. However, you do not need to specify an ability id when instantiating
SpellListener since spell listeners are not associated with any one specific ability; they
"listen" for all spell events. Just like with Spell, you need to create your own structs that
extend SpellListener to define specific event responses (the methods in the plain SpellListener
struct don't do anything).
SpellListener does have one feature not found in Spell: priority. When instantiating a
SpellListener, you must specify a priority. The priority value determines the order in which
the listeners are called on spell events. Listeners with lower priority values will be called
before listeners with higher priority values. The order in which two listeners with the same
priority values are called is undefined.
You must register SpellListeners just like with Spells. To do this, you can use
SpellController.registerListener(). A listener can only be registered once. Once a listener is
registered it will be called on all spell events. You may also unregister listeners using
SpellController.removeListener().
An important thing to consider when using SpellListeners is that for almost all of the event-
response methods, they are called AFTER the corresponding methods in Spell. The one exception
is SpellListener.onTerminate(), which is called before Spell.onTerminate().
For more information on SpellListeners, refer to the SpellListener API.
========================================= Miscellaneous =========================================
The struct SpellController has two static methods in addition to the registration methods
already mentioned above:
------
static method getSpell takes integer abilId returns Spell
static method getActiveSpell takes unit u returns EventSpell
------
SpellController.getSpell() will return the Spell instance associated with the given ability id,
if one is registered. SpellController.getActiveSpell() will return the current event spell that
the given unit is casting, if one exists.
//TESH.scrollpos=0
//TESH.alwaysfold=0
========================================== Spell Type ============================================
Spell Type is just a small add-on to Spell Engine meant to make classifying different Spells
easier. It is a pseudo-bitfield implementation (I say pseudo because JASS doesnt actually
support bitwise operations) and its so simple it consists of one struct type and three instance
methods.
The heart of this add-on is the SpellType struct, which is meant to represent (what else?) a
type of spell. To create your own spell type, simply instantiate a new SpellType. You may
then call one of three methods on your instance:
------
method hasFlag takes integer flag returns boolean
method addFlag takes integer flag returns nothing
method removeFlag takes integer flag returns nothing
------
First, lets talk about the SpellType.addFlag() method. This method is used to add a predefined
flag, meant to represent a certain classification, to your SpellType instance. The sole
parameter takes in the integer equivalent of the bitflag. If you are unfamiliar with bitflags
and bitfields, here is a short explanation of how they work:
Bitfields are just a way to store an array of boolean bitflags compactly as a series of bits
(0 or 1). Naturally, 0 means false and 1 means true. The array is nothing but multiple bits
lined up together:
110010100
means...
true, true, false, false, true, false, true, false, false
That same series of bits (110010100) can also be viewed as a numeral in binary...the equivalent
of:
1 * 2^8 +
1 * 2^7 +
0 * 2^6 +
0 * 2^5 +
1 * 2^4 +
0 * 2^3 +
1 * 2^2 +
0 * 2^1 +
0 * 2^0
Looking at it this way, we can see that each individual boolean in the array can be viewed as a
power of 2 and that the entire array can be viewed as nothing more than the sum of those powers
of 2.
Now that you know a little about bitflags, you can apply it to SpellType. I already mentioned
that SpellType.addFlag() takes in an integer that is the bitflag of a spell type flag. These
bitflags are just powers of 2 or sums of powers of 2. The easiest way to define these bitflags
is through constant global integers. For example:
globals
constant integer TYPE_FIRE = 0x00001 // 1
constant integer TYPE_EARTH = 0x00002 // 2
constant integer TYPE_WATER = 0x00004 // 4
constant integer TYPE_AIR = 0x00008 // 8
constant integer TYPE_LIGHT = 0x00010 // 16
constant integer TYPE_DARK = 0x00020 // 32
constant integer TYPE_ARCANE = 0x00040 // 64
constant integer TYPE_PHYSICAL = 0x00080 // 128
constant integer TYPE_HEALING = 0x00100 // 256
constant integer TYPE_DAMAGING = 0x00200 // 512
constant integer TYPE_BUFF = 0x00400 // 1024
constant integer TYPE_NERF = 0x00800 // 2048
endglobals
I used hexadecimal notation simply because it looks cleaner; you can use either decimal or hex
notation.
Once you have defined your spell type flags, you must update the static field
SpellType.FLAG_MAX_EXPONENT to reflect the greatest power of 2 less than or equal to the
greatest-valued flag you have defined. In the above example, FLAG_MAX_EXPONENT would have to be
set equal to 11 because the greatest-valued tag (TYPE_NERF) has a value of 2048, which is equal
to 2^11.
You may define as many flags as you want, but the highest value any flag may have is 2^31 - 1.
This is because JASS integers are 32-bit signed and will not hold higher values than 2^31 - 1.
Adding together flags basically means combining classifications. So doing TYPE_ARCANE +
TYPE_BUFF is essentially creating a new classification that is both an Arcane spell and a Buff
spell.
Because adding together flags accomplishes the same thing as adding them separating to an
instance of SpellType, the following two pieces of code will do the same thing:
------
spellType.addFlag(TYPE_DAMAGING + TYPE_FIRE)
------
spellType.addFlag(TYPE_DAMAGING)
spellType.addFlag(TYPE_FIRE)
------
In both cases, spellType will be considered to be both Damaging and Fire.
Checking to see if an instance of SpellType has certain spell classifications is done through
the SpellType.hasFlag() method. So for the SpellType instance used above as an example:
spellType.hasFlag(TYPE_DAMAGING) // returns true
spellType.hasFlag(TYPE_FIRE) // returns true
spellType.hasFlag(TYPE_NERF) // returns false
spellType.hasFlag(TYPE_DAMAGING + TYPE_FIRE) // returns true
spellType.hasFlag(TYPE_DAMAGING + TYPE_WATER) // returns false
Finally, you can remove spell classification flags by using the SpellType.removeFlag() method.
Note that if a bitflag is entered that encompasses multiple damage properties, removal will only
occur if the DamageType instance has ALL the properties. Once again using our example:
spellType.removeFlag(TYPE_DAMAGING) // removes damaging tag
spellType.removeFlag(TYPE_FIRE) // removes fire tag
spellType.removeFlag(TYPE_DAMAGING + TYPE_HEALING) // doesn't remove either tag
In case you ever need to get the actual bitfield of a SpellType, simply access the readonly
field SpellType.bitfield.
//TESH.scrollpos=0
//TESH.alwaysfold=0
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
====================================== SpellController API ======================================
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-------------
Version 0.3.2
-------------
==================================== struct SpellController =====================================
SpellController does most of the background work for Spell Engine. It contains several important
static methods.
Constructors
============
None
Constant Fields
===============
------
static constant integer IMMOBILIZE_SPELL_ABIL_ID = 'IMBA'
------
The ability id of the ability used to immobilize units when they cast and channel spells.
------
static constant integer IMMOBILIZE_SPELL_BUFF_ID = 'IMBB'
------
The ability id of the buff used to immobilize units when they cast and channel spells.
------
static constant real TIMER_PERIOD = 0.04
------
The period used to determine how often Spell.onCastTick(), Spell.onChannelTick(), and their
SpellListener counterparts should be called when a unit is casting or channeling a spell.
Instance Fields
===============
None
Static Methods
==============
------
static method getSpell takes integer abilId returns Spell
------
Returns the Spell instance associated with a certain ability id. If no such instance exists,
this method returns 0.
Parameters:
-integer abilId - the ability id to query.
Returns:
The Spell instance associated with the given ability id, or 0 if no such instance exists.
------
static method getActiveSpell takes unit u returns EventSpell
------
Returns the EventSpell currently in its cast cycle for a certain unit. In other words, returns
the event spell a certain unit is currently casting. If no such instance exists, this method
returns 0.
Parameters:
-unit u - the unit to query.
Returns:
The EventSpell instance currently in its cast cycle for the given unit, or 0 if no such instance
exists.
------
static method registerSpell takes Spell spell returns nothing
------
Registers a Spell with the system. An instance of Spell may only be registered once and only
one Spell instance may be associated with a given ability id. If another Spell instance is
already registered for the same ability id, then the old instance is automatically unregistered.
Parameters:
-Spell spell - the Spell instance to be registered.
------
static method removeSpell takes integer abilId returns nothing
------
Unregisters the Spell instance associated with a certain ability id.
Parameters:
-integer abilId - the ability id with which the Spell instance to be unregistered is associated.
------
static method registerListener takes SpellListener listener returns boolean
------
Registers a SpellListener with the system. An instance of SpellListener may only be registered
once with the system.
Parameters:
-SpellListener listener - the SpellListener to be registered.
Returns:
true if the registration succeeded; false otherwise.
------
static method removeListener takes SpellListener listener returns boolean
------
Removes a SpellListener from the system.
Parameters:
-SpellListener listener - the SpellListener to be removed.
Returns:
true if the removal succeeded; false otherwise.
Instance Methods
================
None
//TESH.scrollpos=0
//TESH.alwaysfold=0
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
=========================================== Spell API ===========================================
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-------------
Version 0.3.2
-------------
========================================= struct Spell ==========================================
Spell is the struct responsible for handling spell events tied to a specific ability id. By
default, Spell does very little. You must extend Spell with your own structs and override the
event-response stub methods to define specific event responses.
Constructors
============
------
static method create takes integer abilId returns Spell
------
Instantiates a new Spell associated with a specific ability id. After instantiation, the Spell
must be registered with the system before it can have any effect. After the new Spell is
registered with the system, whenever the system detects cast events (EVENT_UNIT_SPELL_CAST,
EVENT_UNIT_SPELL_EFFECT, and their EVENT_PLAYER counterparts) for the ability id, it will call
the appropriate event-response methods in the Spell instance.
Parameters:
-integer abilId - the ability id with which the new Spell is to be associated.
See Also:
-SpellController.registerSpell()
Constant Fields
===============
------
static constant integer TARGET_DESTRUCTABLE
------
Represents destructable targeting. A spell with destructable targeting has a destructable as
its target.
See Also:
-EventSpell.targeting
------
static constant integer TARGET_INSTANT
------
Represents instant targeting. A spell with instant targeting has no targets of any type.
See Also:
-EventSpell.targeting
------
static constant integer TARGET_ITEM
------
Represents item targeting. A spell with item targeting has an item as its target.
See Also:
-EventSpell.targeting
------
static constant integer TARGET_POINT
------
Represents point targeting. A spell with point targeting has a point (a set of x, y coordinates)
as its target.
See Also:
-EventSpell.targeting
------
static constant integer TARGET_UNIT
------
Represents unit targeting. A spell with unit targeting has a unit as its target.
See Also:
-EventSpell.targeting
Instance Fields
===============
------
readonly integer abilId
------
The ability id with which this Spell is associated.
Static Methods
==============
None
Instance Methods
================
------
stub method onCastBegin takes EventSpell eventSpell returns nothing
------
The event-response method for spell cast begin events tied to this Spell's ability id. This
method is called after Spell.onInitialize(), even if the event spell has a cast time == 0.
This method is called in conjunction with and before the SpellListener.onCastBegin() methods of
any registered listeners.
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-Spell.onCastTick()
-Spell.onCastFinish()
-EventSpell
------
stub method onCastFinish takes EventSpell eventSpell returns nothing
------
The event-reponse method for spell cast finish events tied to this Spell's ability id. This
method is called once the event spell has finished its casting time. This method is called
even if eventSpell.castTime == 0 (in which case it will be called immediately after any cast
begin event response methods).
This method is called in conjunction with and before the SpellListener.onCastFinish() methods of
any registered listeners.
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-Spell.onCastBegin()
-Spell.onCastTick()
-EventSpell
------
stub method onCastTick takes EventSpell eventSpell, real period returns nothing
------
The event-response method for spell cast periodic events tied to this Spell's ability id. This
method is called periodically during the event spell's casting time. This method will not be
called if eventSpell.castTime <= 0.
This method is called in conjunction with and before the SpellListener.onCastTick() methods of
any registered listeners.
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
-real period - the periodicity with which this method is called, in seconds.
See Also:
-Spell.onCastBegin()
-Spell.onCastFinish()
-EventSpell
------
stub method onChannelBegin takes EventSpell eventSpell returns nothing
------
The event-response method for spell channeling begin events tied to this Spell's ability id.
This method is only called if eventSpell.channeling == true and will be called after the event
spell has finished its cast time (this method is called after Spell.onCastFinish()).
This method is called in conjunction with and before the SpellListener.onChannelBegin() methods
of any registered listeners.
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-Spell.onChannelTick()
-Spell.onChannelFinish()
-EventSpell
------
stub method onChannelFinish takes EventSpell eventSpell returns nothing
------
The event-reponse method for spell channeling finish events tied to this Spell's ability id.
This method is called once the event spell has finished its channeling time. This method is
called even if eventSpell.channelTime == 0 (in which case it will be called immediately after
any channeling begin event response methods).
This method is called in conjunction with and before the SpellListener.onChannelFinish() methods
of any registered listeners.
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-Spell.onChannelBegin()
-Spell.onChannelTick()
-EventSpell
------
stub method onChannelTick takes EventSpell eventSpell, real period returns nothing
------
The event-response method for spell channeling periodic events tied to this Spell's ability id.
This method is called periodically during the event spell's channeling time. This method will
not be called if eventSpell.channelTime <= 0.
This method is called in conjunction with and before the SpellListener.onChannelTick() methods
of any registered listeners.
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
-real period - the periodicity with which this method is called, in seconds.
See Also:
-Spell.onChannelBegin()
-Spell.onChannelFinish()
-EventSpell
------
stub method onEffect takes EventSpell eventSpell returns nothing
------
The event-response method for spell effect events tied to this Spell's ability id. This method
is called once the event spell has finished its cast time and ready to begin its effect (this
method is called after Spell.onCastFinish()). This method is only called if
eventSpell.channeling == false.
This method is a logical place to execute any code to be run upon a successful casting of a
non-channeling spell.
This method is called in conjunction with and before the SpellListener.onEffect() methods of
any registered listeners.
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-Spell.onChannelBegin()
-EventSpell
------
stub method onEnd takes EventSpell eventSpell returns nothing
------
The event-response method for spell cleanup events tied to this Spell's ability id. This method
is called when the event spell ends its cast cycle for any reason (either normally or through
termination).
This method is called in conjunction with and before the SpellListener.onEnd() methods of any
registered listeners.
Parameters:
-EventSpell eventSpell - the event spell.
------
stub method onFinalTerminate takes EventSpell eventSpell, unit source, integer data returns
nothing
------
The event-response method for final spell termination events tied to this Spell's ability id.
This method is called when the event spell is finally terminated, and not when the event spell
finishes its cast cycle normally.
Unlike Spell.onTerminate(), this method is only called when an EventSpell is actually about to
be terminated. The termination cannot be stopped at the point this method is called.
This method is called in conjunction with and before the SpellListener.onFinalTerminate()
methods of any registered listeners.
Parameters:
-EventSpell eventSpell - the event spell.
-unit source - the source of the termination.
-integer data - any data that was meant to be passed to this method.
See Also:
-Spell.onTerminate()
-EventSpell
-EventSpell.terminate()
-EventSpell.forceTerminate()
------
stub method onInitialize takes EventSpell eventSpell returns nothing
------
The event-response method for spell initialization events tied to this Spell's ability id.
This method is called when a unit begins casting an ability whose ability id is equal to
Spell.abilId.
This method is a logical place to initialize many of the properties of the event spell, such as
EventSpell.channeling, EventSpell.castTime, EventSpell.channelTime, EventSpell.spellType, etc.
If eventSpell.terminate() is called, mana and cooldown penalties will not be applied to the unit
that cast the spell.
This method is called in conjunction with and before the SpellListener.onInitialize() methods
of any registered listeners.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-EventSpell
------
stub method onTerminate takes EventSpell eventSpell, unit source, integer data returns boolean
------
The event-response method for spell termination events tied to this Spell's ability id. This
method is called when the event spell is slated to be terminated (EventSpell.terminate() was
called), and not when the event spell finishes its cast cycle normally.
This method determines if the termination of the event spell should be successful or not through
its return value. If this method returns true, then the event spell is still eligible for
termination; otherwise the termination fails and the event spell continues its cast cycle.
This method is called in conjunction with and after the SpellListener.onTerminate() methods of
any registered listeners. Only one of these methods needs to return false for the termination
to fail. Therefore, once one of them returns false, the ones that have not yet been called will
simply not be called.
Note that this method is not called if eventSpell.forceTerminate() was called.
Parameters:
-EventSpell eventSpell - the event spell.
-unit source - the source of the termination.
-integer data - any data that was meant to be passed to this method.
Returns:
true if the termination can still succeed; false if the termination should fail.
See Also:
-Spell.onFinalTerminate()
-EventSpell
-EventSpell.terminate()
-EventSpell.forceTerminate()
//TESH.scrollpos=0
//TESH.alwaysfold=0
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
======================================= SpellListener API =======================================
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-------------
Version 0.3.2
-------------
===================================== struct SpellListener ======================================
SpellListener is the struct responsible for handling global spell events. By default,
SpellListener does very little. You must extend SpellListener with your own structs and override
the event-response stub methods to define specific event responses.
Constructors
============
------
static method create takes integer priority returns SpellListener
------
Instantiates a new SpellListener with a specific priority value. After instantiation, the
SpellListener must be registered with the system before it can have any effect. After the new
SpellListener is registered with the system, whenever the system detects cast events
(EVENT_UNIT_SPELL_CAST, EVENT_UNIT_SPELL_EFFECT, and their EVENT_PLAYER counterparts) it will
call the appropriate event-response methods in the SpellListener instance.
The SpellListener's priority value determines the order in which it is called once it is
registered. SpellListeners with lower priority values are called before SpellListeners with
higher priority values.
Parameters:
-integer priority - the priority value of the new SpellListener. Determines the order in which
registered SpellListeners are called.
See Also:
-SpellController.registerListener()
Constant Fields
===============
None
Instance Fields
===============
------
readonly integer priority
------
The priority value of this SpellListener. Registered SpellListeners with lower priority values
are called before registered SpellListeners with higher priority values on spell events.
Static Methods
==============
None
Instance Methods
================
------
stub method onCastBegin takes EventSpell eventSpell returns nothing
------
The event-response method for global spell cast begin events. This method is called after
SpellListener.onInitialize(), even if the event spell has a cast time == 0.
This method is called in conjunction with and after the Spell.onCastBegin() method of the Spell
to which the event spell is tied (eventSpell.spell).
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-SpellListener.onCastTick()
-SpellListener.onCastFinish()
-EventSpell
------
stub method onCastFinish takes EventSpell eventSpell returns nothing
------
The event-reponse method for global spell cast finish events. This method is called once the
event spell has finished its casting time. This method is called even if eventSpell.castTime ==
0 (in which case it will be called immediately after any cast begin event response methods).
This method is called in conjunction with and after the Spell.onCastFinish() method of the Spell
to which the event spell is tied (eventSpell.spell).
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-SpellListener.onCastBegin()
-SpellListener.onCastTick()
-EventSpell
------
stub method onCastTick takes EventSpell eventSpell, real period returns nothing
------
The event-response method for global spell cast periodic events. This method is called
periodically during the event spell's casting time. This method will not be called if
eventSpell.castTime <= 0.
This method is called in conjunction with and after the Spell.onCastTick() method of the Spell
to which the event spell is tied (eventSpell.spell).
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
-real period - the periodicity with which this method is called, in seconds.
See Also:
-SpellListener.onCastBegin()
-SpellListener.onCastFinish()
-EventSpell
------
stub method onChannelBegin takes EventSpell eventSpell returns nothing
------
The event-response method for global spell channeling begin events. This method is only called
if eventSpell.channeling == true and will be called after the event spell has finished its cast
time (this method is called after SpellListener.onCastFinish()).
This method is called in conjunction with and after the Spell.onChannelBegin() method of the
Spell to which the event spell is tied (eventSpell.spell).
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-SpellListener.onChannelTick()
-SpellListener.onChannelFinish()
-EventSpell
------
stub method onChannelFinish takes EventSpell eventSpell returns nothing
------
The event-reponse method for global spell channeling finish events. This method is called once
the event spell has finished its channeling time. This method is called even if
eventSpell.channelTime == 0 (in which case it will be called immediately after any channeling
begin event response methods).
This method is called in conjunction with and after the Spell.onChannelFinish() method of the
Spell to which the event spell is tied (eventSpell.spell).
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-SpellListener.onChannelBegin()
-SpellListener.onChannelTick()
-EventSpell
------
stub method onChannelTick takes EventSpell eventSpell, real period returns nothing
------
The event-response method for global spell channeling periodic events. This method is called
periodically during the event spell's channeling time. This method will not be called if
eventSpell.channelTime <= 0.
This method is called in conjunction with and after the Spell.onChannelTcik() method of the
Spell to which the event spell is tied (eventSpell.spell).
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
-real period - the periodicity with which this method is called, in seconds.
See Also:
-SpellListener.onChannelBegin()
-SpellListener.onChannelFinish()
-EventSpell
------
stub method onEffect takes EventSpell eventSpell returns nothing
------
The event-response method for global spell effect events. This method is called once the event
spell has finished its cast time and ready to begin its effect (this method is called after
SpellListener.onCastFinish()). This method is only called if eventSpell.channeling == false.
This method is a logical place to execute any code to be run upon a successful casting of
non-channeling spells.
This method is called in conjunction with and after the Spell.onEffect() method of the Spell
to which the event spell is tied (eventSpell.spell).
If the event spell was terminated in any manner before this method's turn to be called, then
this method will not be called.
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-SpellListener.onChannelBegin()
-EventSpell
------
stub method onEnd takes EventSpell eventSpell returns nothing
------
The event-response method for global spell cleanup events. This method is called when the event
spell ends its cast cycle for any reason (either normally or through termination).
This method is called in conjunction with and after the Spell.onEnd() method of the Spell
to which the event spell is tied (eventSpell.spell).
Parameters:
-EventSpell eventSpell - the event spell.
------
stub method onFinalTerminate takes EventSpell eventSpell, unit source, integer data returns
nothing
------
The event-response method for global final spell termination events. This method is called when
the event spell is finally terminated, and not when the event spell finishes its cast cycle
normally.
Unlike SpellListener.onTerminate(), this method is only called when an EventSpell is actually
about to be terminated. The termination cannot be stopped at the point this method is called.
This method is called in conjunction with the SpellListener.onFinalTerminate() methods of any
other registered listeners, and collectively these are called before the
Spell.onFinalTerminate() method of the Spell to which the event spell is tied
(eventSpell.spell).
Parameters:
-EventSpell eventSpell - the event spell.
-unit source - the source of the termination.
-integer data - any data that was meant to be passed to this method.
See Also:
-SpellListener.onTerminate()
-EventSpell
-EventSpell.terminate()
-EventSpell.forceTerminate()
------
stub method onInitialize takes EventSpell eventSpell returns nothing
------
The event-response method for global spell initialization events. This method is called when a
unit begins casting any ability whose ability id has been associated with a Spell instance and
registered with the system.
This method is a logical place to initialize many of the properties of the event spell, such as
EventSpell.channeling, EventSpell.castTime, EventSpell.channelTime, EventSpell.spellType, etc.
If eventSpell.terminate() is called, mana and cooldown penalties will not be applied to the unit
that cast the spell.
This method is called in conjunction with and after the Spell.onInitialize() method of the Spell
to which the event spell is tied (eventSpell.spell).
Parameters:
-EventSpell eventSpell - the event spell.
See Also:
-EventSpell
------
stub method onTerminate takes EventSpell eventSpell, unit source, integer data returns boolean
------
The event-response method for global spell termination events. This method is called when the
event spell is slated to be terminated (EventSpell.terminate() was called), and not when the
event spell finishes its cast cycle normally.
This method determines if the termination of the event spell should be successful or not through
its return value. If this method returns true, then the event spell is still eligible for
termination; otherwise the termination fails and the event spell continues its cast cycle.
This method is called in conjunction with the SpellListener.onTerminate() methods of
any other registered listeners, and collectively these are called before the Spell.onTerminate()
method of the Spell to which the event spell is tied (eventSpell.spell). Only one of these
methods needs to return false for the termination to fail. Therefore, once one of them returns
false, the ones that have not yet been called will simply not be called.
Note that this method is not called if eventSpell.forceTerminate() was called.
Parameters:
-EventSpell eventSpell - the event spell.
-unit source - the source of the termination.
-integer data - any data that was meant to be passed to this method.
Returns:
true if the termination can still succeed; false if the termination should fail.
See Also:
-SpellListener.onFinalTerminate()
-EventSpell
-EventSpell.terminate()
-EventSpell.forceTerminate()
//TESH.scrollpos=0
//TESH.alwaysfold=0
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
======================================== EventSpell API =========================================
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-------------
Version 0.3.2
-------------
======================================= struct EventSpell =======================================
EventSpell contains data on single event spell. It is used as a parameter in every event-
response method in both Spell and SpellListener. You should never attempt to instantiate
EventSpell and should never destroy EventSpell instances passed to event-response method
parameters.
Constructors
============
None
Constant Fields
===============
------
static constant integer PHASE_CAST
------
Represents the casting phase of an event spell's cast cycle. This is the third phase of the
cast cycle.
See Also:
-EventSpell.phase
------
static constant integer PHASE_CHANNEL
------
Represents the channeling phase of an event spell's cast cycle. This is one of two phases that
mark the last phase of the cast cycle.
See Also:
-EventSpell.phase
------
static constant integer PHASE_EFFECT
------
Represents the effect phase of an event spell's cast cycle. This is one of two phases that mark
the last phase of the cast cycle.
See Also:
-EventSpell.phase
------
static constant integer PHASE_INITIALIZE
------
Represents the initialization phase of an event spell's cast cycle. This is the first phase of
the cast cycle.
See Also:
-EventSpell.phase
------
static constant integer PHASE_PRECAST
------
Represents the precast phase of an event spell's cast cycle. This is the second phase of the
cast cycle.
See Also:
-EventSpell.phase
Instance Fields
===============
------
readonly unit caster
------
The unit that cast this EventSpell.
------
real castTime = 0.0
------
The total casting time of this EventSpell. Any changes made to this field after this EventSpell
moves past EventSpell.PHASE_PRECAST has no effect.
------
readonly real castTimeElapsed
------
The total casting time elapsed of this EventSpell.
------
boolean channeling
------
Whether this EventSpell is channeling. By default this field == false.
------
real channelTime = 0.0
------
The total channeling time of this EventSpell. Any changes made to this field after this
EventSpell moves past EventSpell.PHASE_CAST has no effect.
------
readonly real channelTimeElapsed
------
The total channeling time elapsed of this EventSpell.
------
integer data
------
Holds any data attached to this EventSpell. By default this field == 0.
------
integer level
------
The level at which this EventSpell is cast.
------
readonly integer phase
------
The phase of the cast cycle in which this EventSpell currently is.
See Also:
-EventSpell.PHASE_INITIALIZE
-EventSpell.PHASE_PRECAST
-EventSpell.PHASE_CAST
-EventSpell.PHASE_EFFECT
-EventSpell.PHASE_CHANNEL
------
readonly Spell spell
------
The Spell instance with which this EventSpell is associated.
------
destructable targetDestructable
------
The target destructable of this EventSpell. If no such destructable exists, then this field ==
null.
See Also:
-EventSpell.targeting
------
readonly integer targeting
------
The targeting type of this EventSpell.
See Also:
-Spell.TARGET_INSTANT
-Spell.TARGET_UNIT
-Spell.TARGET_ITEM
-Spell.TARGET_DESTRUCTABLE
-Spell.TARGET_POINT
------
item targetItem
------
The target item of this EventSpell. If no such item exists, then this field == null.
See Also:
-EventSpell.targeting
------
unit targetUnit
------
The target unit of this EventSpell. If no such unit exists, then this field == null.
See Also:
-EventSpell.targeting
------
real targetX
------
The x-coordinate of the target point of this EventSpell. If no such point exists, then this
field == 0.
See Also:
-EventSpell.targeting
------
real targetY
------
The y-coordinate of the target point of this EventSpell. If no such point exists, then this
field == 0.
See Also:
-EventSpell.targeting
Static Methods
==============
None
Instance Methods
================
------
method forceTerminate takes unit source, integer data returns nothing
------
Ends this EventSpell's cast cycle. Unlike EventSpell.terminate(), this method guarantees that
the cast cycle will be terminated.
Parameters:
-unit source - the source of the termination.
-integer data - any data to be passed to Spell.onTerminate() and SpellListener.onTerminate().
See Also:
-EventSpell.terminate()
------
method terminate takes unit source, integer data returns boolean
------
Attempts to end this EventSpell's cast cycle.
Parameters:
-unit source - the source of the termination.
-integer data - any data to be passed to Spell.onTerminate() and SpellListener.onTerminate().
Returns:
true if the cast cycle was terminated; false otherwise.
See Also:
-EventSpell.forceTerminate()
Delegates
=========
------
private delegate SpellType
------
You can access all fields and methods contained in SpellType in EventSpell.
See Also:
-SpellType
//TESH.scrollpos=0
//TESH.alwaysfold=0
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
========================================= SpellType API =========================================
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-------------
Version 0.3.2
-------------
======================================= struct SpellType ========================================
SpellType is a simple wrapper for a pseudo-bitfield meant to simulate properties of spells.
SpellType requires you to define your own bitflags to represent different spell properties.
Constructors
============
------
static method create takes nothing returns SpellType
------
Creates a new instance of SpellType with no flags added (i.e. SpellType.bitfield == 0).
Constant Fields
===============
------
static constant integer FLAG_MAX_EXPONENT
------
(Configurable)
The largest power of two that is less than or equal to any defined SpellType bitflag.
If this value is greater than 30, the system will simply regard it as 30.
Static Fields
=============
None
Instance Fields
===============
------
readonly integer bitfield
------
The bitfield of this SpellType.
Static Methods
==============
None
Instance Methods
================
------
method addFlag takes integer flag returns nothing
------
Adds a flag to this SpellType.
Parameters:
-integer flag - the bitflag to be added.
------
method hasFlag takes integer flag returns boolean
------
Checks if this SpellType contains the given flag.
Parameters:
-integer flag - the bitflag to check against.
Returns:
true if this SpellType contains the given flag.
------
method removeFlag takes integer flag returns nothing
------
Removes a flag from this SpellType. If this SpellType does not contain the flag (i.e. if
SpellType.hasFlag(flag) == false), then this method does nothing.
Parameters:
-integer flag - the bitflag to remove.
//TESH.scrollpos=186
//TESH.alwaysfold=0
/*
++++++++++++++++++++++++
+ Collection +
+ v0.6.0 +
+ Author: aznricepuff +
++++++++++++++++++++++++
*/
//! textmacro Collection takes TYPE, TYPE_NAME, TYPE_CONSTANT_NAME
library $TYPE_NAME$Collection
function interface $TYPE_NAME$_Enum takes $TYPE$ this, integer data returns nothing
function interface $TYPE_NAME$_Comparator takes $TYPE$ x, $TYPE$ y returns integer
struct $TYPE_NAME$Array
private static hashtable t
private integer n
static method create takes integer n returns thistype
local thistype this = thistype.allocate()
set this.n = IMaxBJ(0, n)
return this
endmethod
method size takes nothing returns integer
return .n
endmethod
method operator [] takes integer index returns $TYPE$
if (index >= 0 and index < .n) then
return LoadInteger(.t, this, index)
endif
return 0
endmethod
method operator []= takes integer index, $TYPE$ e returns nothing
if (index >= 0 and index < .n) then
call SaveInteger(.t, this, index, e)
endif
endmethod
method destroy takes nothing returns nothing
call FlushChildHashtable(this.t, this)
call this.deallocate()
endmethod
implement optional $TYPE_NAME$ArraySort
implement optional $TYPE_NAME$ArrayStableSort
implement optional $TYPE_NAME$ArraySelection
implement optional $TYPE_NAME$ArrayBinarySearch
private static method onInit takes nothing returns nothing
set .t = InitHashtable()
endmethod
endstruct
private interface CollectionInterface
method add takes $TYPE$ toAdd returns boolean defaults false
method addAll takes $TYPE_NAME$Collection collection returns nothing defaults nothing
method remove takes $TYPE$ toRemove returns boolean defaults false
method removeAll takes $TYPE_NAME$Collection collection returns nothing defaults nothing
method retainAll takes $TYPE_NAME$Collection collection returns nothing defaults nothing
method contains takes $TYPE$ element returns boolean defaults false
method size takes nothing returns integer defaults 0
method isEmpty takes nothing returns boolean defaults false
method clear takes nothing returns nothing defaults nothing
method toArray takes nothing returns $TYPE_NAME$Array defaults 0
method iterator takes nothing returns $TYPE_NAME$Iterator defaults 0
method enum takes $TYPE_NAME$_Enum enum, integer data returns nothing defaults nothing
endinterface
interface $TYPE_NAME$Iterator
method next takes nothing returns $TYPE$
method hasNext takes nothing returns boolean
method remove takes nothing returns nothing
endinterface
struct $TYPE_NAME$Collection extends CollectionInterface
implement optional $TYPE_NAME$CollectionSelection
stub method addAll takes $TYPE_NAME$Collection collection returns nothing
call this.addAllHelper.evaluate(collection)
endmethod
stub method removeAll takes $TYPE_NAME$Collection collection returns nothing
call this.removeAllHelper.evaluate(collection)
endmethod
stub method retainAll takes $TYPE_NAME$Collection collection returns nothing
call this.retainAllHelper.evaluate(collection)
endmethod
private method addAllHelper takes $TYPE_NAME$Collection collection returns nothing
local $TYPE_NAME$Iterator it = collection.iterator()
loop
exitwhen (it.hasNext() == false)
call this.add(it.next())
endloop
call it.destroy()
endmethod
private method removeAllHelper takes $TYPE_NAME$Collection collection returns nothing
local $TYPE_NAME$Iterator it = this.iterator()
loop
exitwhen (it.hasNext() == false)
if (collection.contains(it.next())) then
call it.remove()
endif
endloop
call it.destroy()
endmethod
private method retainAllHelper takes $TYPE_NAME$Collection collection returns nothing
local $TYPE_NAME$Iterator it = this.iterator()
loop
exitwhen (it.hasNext() == false)
if (collection.contains(it.next()) == false) then
call it.remove()
endif
endloop
call it.destroy()
endmethod
endstruct
endlibrary
library $TYPE_NAME$Set requires $TYPE_NAME$Collection
struct $TYPE_NAME$Set extends $TYPE_NAME$Collection
endstruct
endlibrary
library $TYPE_NAME$List requires $TYPE_NAME$Collection
struct $TYPE_NAME$ListTypeIterator extends $TYPE_NAME$Iterator
stub method next takes nothing returns $TYPE$
return 0
endmethod
stub method hasNext takes nothing returns boolean
return false
endmethod
stub method nextIndex takes nothing returns integer
return 0
endmethod
stub method previous takes nothing returns $TYPE$
return 0
endmethod
stub method hasPrevious takes nothing returns boolean
return false
endmethod
stub method previousIndex takes nothing returns integer
return 0
endmethod
stub method add takes $TYPE$ toAdd returns nothing
endmethod
stub method remove takes nothing returns nothing
endmethod
stub method setElement takes $TYPE$ toSet returns nothing
endmethod
endstruct
struct $TYPE_NAME$List extends $TYPE_NAME$Collection
stub method addByIndex takes integer index, $TYPE$ toAdd returns nothing
endmethod
stub method setByIndex takes integer index, $TYPE$ toSet returns $TYPE$
return 0
endmethod
stub method getByIndex takes integer index returns $TYPE$
return 0
endmethod
stub method removeByIndex takes integer index returns $TYPE$
return 0
endmethod
stub method swap takes integer index1, integer index2 returns nothing
endmethod
stub method indexOf takes $TYPE$ element returns integer
return -1
endmethod
stub method listIteratorByIndex takes integer index returns $TYPE_NAME$ListTypeIterator
return 0
endmethod
stub method listIterator takes nothing returns $TYPE_NAME$ListTypeIterator
return .listIteratorByIndex(0)
endmethod
implement optional $TYPE_NAME$ListSort
implement optional $TYPE_NAME$ListStableSort
implement optional $TYPE_NAME$ListSelection
implement optional $TYPE_NAME$ListBinarySearch
endstruct
endlibrary
//! endtextmacro
//TESH.scrollpos=488
//TESH.alwaysfold=0
/*
++++++++++++++++++++++++
+ TreeSet +
+ v0.6.0 +
+ Author: aznricepuff +
++++++++++++++++++++++++
*/
//! textmacro TreeSet takes TYPE, TYPE_NAME, TYPE_CONSTANT_NAME
library $TYPE_NAME$TreeSet requires $TYPE_NAME$Set
private keyword $TYPE_NAME$TreeSetIterator
private struct $TYPE_NAME$TreeSetNode
static constant integer BLACK = 0
static constant integer RED = 1
static thistype sentinelNode
$TYPE_NAME$TreeSet s
$TYPE$ e
thistype parent
thistype left
thistype right
integer color
static method create takes $TYPE_NAME$TreeSet s, integer color, $TYPE$ e, thistype parent, boolean sentinel returns thistype
local thistype this = thistype.allocate()
set this.s = s
set this.color = color
set this.e = e
set this.parent = parent
if (sentinel == false) then
set this.left = thistype.sentinelNode //thistype.create(s, thistype.BLACK, 0, this, true)
set this.right = thistype.sentinelNode //thistype.create(s, thistype.BLACK, 0, this, true)
endif
return this
endmethod
method operator sentinel takes nothing returns boolean
return this == thistype.sentinelNode
endmethod
method operator sibling takes nothing returns thistype
if (.parent != 0) then
if (this == .parent.left) then
return .parent.right
else
return .parent.left
endif
endif
return 0
endmethod
method operator grandparent takes nothing returns thistype
if (.parent != 0) then
return .parent.parent
endif
return 0
endmethod
method operator uncle takes nothing returns thistype
local thistype grandparent = .grandparent
if (grandparent != 0) then
if (.parent == grandparent.left) then
return grandparent.right
else
return grandparent.left
endif
endif
return 0
endmethod
method rotateLeft takes nothing returns nothing
if (.parent != 0) then
if (this == .parent.left) then
set .parent.left = .right
else
set .parent.right = .right
endif
endif
set .right.parent = .parent
set .parent = .right
set .right = .parent.left
set .parent.left = this
if (.right.sentinel == false) then
set .right.parent = this
endif
if (this == .s.root) then
set .s.root = .parent
endif
endmethod
method rotateRight takes nothing returns nothing
if (.parent != 0) then
if (this == .parent.left) then
set .parent.left = .left
else
set .parent.right = .left
endif
endif
set .left.parent = .parent
set .parent = .left
set .left = .parent.right
set .parent.right = this
if (.left.sentinel == false) then
set .left.parent = this
endif
if (this == .s.root) then
set .s.root = .parent
endif
endmethod
private method inOrderSuccessorHelper takes nothing returns $TYPE_NAME$TreeSetNode
if (this.sentinel) then
return 0
elseif (this.left.sentinel) then
return this
endif
return this.left.inOrderSuccessorHelper()
endmethod
method getInOrderSuccessor takes nothing returns $TYPE_NAME$TreeSetNode
if (this.sentinel) then
return 0
endif
return this.right.inOrderSuccessorHelper.evaluate()
endmethod
private static method onInit takes nothing returns nothing
set thistype.sentinelNode = thistype.create(0, thistype.BLACK, 0, 0, true)
endmethod
endstruct
struct $TYPE_NAME$TreeSet extends $TYPE_NAME$Set
$TYPE_NAME$TreeSetNode root = 0
integer n = 0
$TYPE_NAME$_Comparator comparator = 0
static method create takes $TYPE_NAME$_Comparator comparator returns thistype
local thistype this = thistype.allocate()
set this.comparator = comparator
return this
endmethod
method add takes $TYPE$ toAdd returns boolean
if (this.root == 0) then
set this.root = $TYPE_NAME$TreeSetNode.create(this, $TYPE_NAME$TreeSetNode.BLACK, toAdd, 0, false)
else
if (this.addNode.evaluate(toAdd, this.root) == false) then
return false
endif
endif
set this.n = this.n + 1
return true
endmethod
method remove takes $TYPE$ toRemove returns boolean
local $TYPE_NAME$TreeSetNode node = this.findNode.evaluate(toRemove, this.root)
if (node != 0) then
call this.removeNode.evaluate(node)
set this.n = this.n - 1
return true
endif
return false
endmethod
method contains takes $TYPE$ element returns boolean
return this.findNode.evaluate(element, this.root) != 0
endmethod
method size takes nothing returns integer
return this.n
endmethod
method isEmpty takes nothing returns boolean
return this.n == 0
endmethod
method clear takes nothing returns nothing
call this.clearHelper.evaluate(this.root)
set this.root = 0
set this.n = 0
endmethod
method toArray takes nothing returns $TYPE_NAME$Array
return this.toArrayHelper.evaluate()
endmethod
method iterator takes nothing returns $TYPE_NAME$Iterator
return $TYPE_NAME$TreeSetIterator.create.evaluate(this)
endmethod
method enum takes $TYPE_NAME$_Enum enum, integer data returns nothing
if (this.n == 0) then
return
endif
call this.enumHelper.evaluate(this.root, enum, data)
endmethod
method destroy takes nothing returns nothing
call this.clear()
call this.deallocate()
endmethod
private stub method addNode takes $TYPE$ element, $TYPE_NAME$TreeSetNode node returns boolean
local integer order
if (this.comparator != 0) then
set order = this.comparator.evaluate(element, node.e)
else
if (element < node.e) then
set order = -1
elseif (element > node.e) then
set order = 1
else
set order = 0
endif
endif
if (order < 0) then
if (node.left.sentinel) then
set node.left = $TYPE_NAME$TreeSetNode.create(this, $TYPE_NAME$TreeSetNode.RED, element, node, false)
return true
else
return this.addNode(element, node.left)
endif
elseif (order > 0) then
if (node.right.sentinel) then
set node.right = $TYPE_NAME$TreeSetNode.create(this, $TYPE_NAME$TreeSetNode.RED, element, node, false)
return true
else
return this.addNode(element, node.right)
endif
endif
return false
endmethod
private method findNode takes $TYPE$ element, $TYPE_NAME$TreeSetNode node returns $TYPE_NAME$TreeSetNode
local integer order
if (node == 0) then
return 0
elseif (node.sentinel) then
return 0
endif
if (this.comparator != 0) then
set order = this.comparator.evaluate(element, node.e)
else
if (element < node.e) then
set order = -1
elseif (element > node.e) then
set order = 1
else
set order = 0
endif
endif
if (order < 0) then
return this.findNode(element, node.left)
elseif (order > 0) then
return this.findNode(element, node.right)
else
return node
endif
endmethod
private stub method removeNode takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode successor
if (node.right.sentinel == false and node.left.sentinel == false) then
set successor = node.getInOrderSuccessor()
set node.e = successor.e
call this.removeNode.evaluate(successor)
elseif (node.right.sentinel and node.left.sentinel) then
if (node != this.root) then
if (node == node.parent.left) then
set node.parent.left = $TYPE_NAME$TreeSetNode.sentinelNode
else
set node.parent.right = $TYPE_NAME$TreeSetNode.sentinelNode
endif
else
set this.root = 0
endif
call node.destroy()
else
if (node.right.sentinel) then
if (node.parent.left == node) then
set node.parent.left = node.left
else
set node.parent.right = node.left
endif
set node.left.parent = node.parent
if (node == this.root) then
set this.root = node.left
endif
else
if (node.parent.left == node) then
set node.parent.left = node.right
else
set node.parent.right = node.right
endif
set node.right.parent = node.parent
if (node == this.root) then
set this.root = node.right
endif
endif
call node.destroy()
endif
endmethod
private method clearHelper takes $TYPE_NAME$TreeSetNode node returns nothing
if (node == 0) then
return
endif
if (node.left.sentinel == false) then
call this.clearHelper(node.left)
endif
if (node.right.sentinel == false) then
call this.clearHelper(node.right)
endif
call node.destroy()
endmethod
private method enumHelper takes $TYPE_NAME$TreeSetNode node, $TYPE_NAME$_Enum enum, integer data returns nothing
if (node.left.sentinel == false) then
call this.enumHelper(node.left, enum, data)
endif
call enum.evaluate(node.e, data)
if (node.right.sentinel == false) then
call this.enumHelper(node.right, enum, data)
endif
endmethod
private method toArrayHelper takes nothing returns $TYPE_NAME$Array
local $TYPE_NAME$TreeSetNode node = this.root
local $TYPE_NAME$Array ar
local integer n = 0
if (node == 0) then
return 0
endif
set ar = $TYPE_NAME$Array.create(this.size())
loop
exitwhen (node.left.sentinel)
set node = node.left
endloop
loop
set ar[n] = node.e
set n = n + 1
if (node.right.sentinel == false) then
set node = node.right
loop
exitwhen (node.left.sentinel)
set node = node.left
endloop
else
loop
exitwhen (node.parent == 0 or node != node.parent.right)
set node = node.parent
endloop
set node = node.parent
endif
exitwhen (node.sentinel or node == 0)
endloop
return ar
endmethod
endstruct
struct $TYPE_NAME$BalancedTreeSet extends $TYPE_NAME$TreeSet
private method addNode takes $TYPE$ element, $TYPE_NAME$TreeSetNode node returns boolean
local integer order
if (this.comparator != 0) then
set order = this.comparator.evaluate(element, node.e)
else
if (element < node.e) then
set order = -1
elseif (element > node.e) then
set order = 1
else
set order = 0
endif
endif
if (order < 0) then
if (node.left.sentinel) then
set node.left = $TYPE_NAME$TreeSetNode.create(this, $TYPE_NAME$TreeSetNode.RED, element, node, false)
call this.insertCaseOne.evaluate(node.left)
return true
else
return this.addNode(element, node.left)
endif
elseif (order > 0) then
if (node.right.sentinel) then
set node.right = $TYPE_NAME$TreeSetNode.create(this, $TYPE_NAME$TreeSetNode.RED, element, node, false)
call this.insertCaseOne.evaluate(node.right)
return true
else
return this.addNode(element, node.right)
endif
endif
return false
endmethod
private method removeNode takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode successor
if (node.right.sentinel == false and node.left.sentinel == false) then
set successor = node.getInOrderSuccessor()
set node.e = successor.e
call this.removeNode.evaluate(successor)
elseif (node.right.sentinel and node.left.sentinel) then
if (node != this.root) then
if (node == node.parent.left) then
set node.parent.left = $TYPE_NAME$TreeSetNode.sentinelNode
else
set node.parent.right = $TYPE_NAME$TreeSetNode.sentinelNode
endif
else
set this.root = 0
endif
call node.destroy()
else
if (node.color == $TYPE_NAME$TreeSetNode.RED) then
if (node.right.sentinel) then
if (node.parent.left == node) then
set node.parent.left = node.left
else
set node.parent.right = node.left
endif
set node.left.parent = node.parent
if (node == this.root) then
set this.root = node.left
endif
else
if (node.parent.left == node) then
set node.parent.left = node.right
else
set node.parent.right = node.right
endif
set node.right.parent = node.parent
if (node == this.root) then
set this.root = node.right
endif
endif
else
if (node.right.sentinel) then
if (node.parent.left == node) then
set node.parent.left = node.left
else
set node.parent.right = node.left
endif
set node.left.parent = node.parent
if (node == this.root) then
set this.root = node.left
endif
if (node.left.color == $TYPE_NAME$TreeSetNode.RED) then
set node.left.color = $TYPE_NAME$TreeSetNode.BLACK
else
call this.removeCaseOne.evaluate(node.left)
endif
else
if (node.parent.left == node) then
set node.parent.left = node.right
else
set node.parent.right = node.right
endif
set node.right.parent = node.parent
if (node == this.root) then
set this.root = node.right
endif
if (node.right.color == $TYPE_NAME$TreeSetNode.RED) then
set node.right.color = $TYPE_NAME$TreeSetNode.BLACK
else
call this.removeCaseOne.evaluate(node.right)
endif
endif
endif
call node.destroy()
endif
endmethod
private method insertCaseFive takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode grandparent = node.grandparent
set node.parent.color = $TYPE_NAME$TreeSetNode.BLACK
set grandparent.color = $TYPE_NAME$TreeSetNode.RED
if (node == node.parent.left and node.parent == grandparent.left) then
call grandparent.rotateRight()
else
call grandparent.rotateLeft()
endif
endmethod
private method insertCaseFour takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode grandparent = node.grandparent
if (node == node.parent.right and node.parent == grandparent.left) then
call node.parent.rotateLeft()
set node = node.left
elseif (node == node.parent.left and node.parent == grandparent.right) then
call node.parent.rotateRight()
set node = node.right
endif
call this.insertCaseFive(node)
endmethod
private method insertCaseThree takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode uncle = node.uncle
local $TYPE_NAME$TreeSetNode grandparent = node.grandparent
if (uncle != 0) then
if (uncle.color == $TYPE_NAME$TreeSetNode.RED) then
set node.parent.color = $TYPE_NAME$TreeSetNode.BLACK
set uncle.color = $TYPE_NAME$TreeSetNode.BLACK
set grandparent.color = $TYPE_NAME$TreeSetNode.RED
call this.insertCaseOne.evaluate(grandparent)
else
call this.insertCaseFour(node)
endif
else
call this.insertCaseFour(node)
endif
endmethod
private method insertCaseTwo takes $TYPE_NAME$TreeSetNode node returns nothing
if (node.parent.color != $TYPE_NAME$TreeSetNode.BLACK) then
call this.insertCaseThree(node)
endif
endmethod
private method insertCaseOne takes $TYPE_NAME$TreeSetNode node returns nothing
if (node.parent == 0) then
set node.color = $TYPE_NAME$TreeSetNode.BLACK
else
call this.insertCaseTwo(node)
endif
endmethod
private method removeCaseSix takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode sibling = node.sibling
set sibling.color = node.parent.color
set node.parent.color = $TYPE_NAME$TreeSetNode.BLACK
if (node == node.parent.left) then
set sibling.right.color = $TYPE_NAME$TreeSetNode.BLACK
call node.parent.rotateLeft()
else
set sibling.left.color = $TYPE_NAME$TreeSetNode.BLACK
call node.parent.rotateRight()
endif
endmethod
private method removeCaseFive takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode sibling = node.sibling
if (sibling.color == $TYPE_NAME$TreeSetNode.BLACK) then
if (node == node.parent.left and sibling.right.color == $TYPE_NAME$TreeSetNode.BLACK and sibling.left.color == $TYPE_NAME$TreeSetNode.RED) then
set sibling.color = $TYPE_NAME$TreeSetNode.RED
set sibling.left.color = $TYPE_NAME$TreeSetNode.BLACK
call sibling.rotateRight()
elseif (node == node.parent.right and sibling.left.color == $TYPE_NAME$TreeSetNode.BLACK and sibling.right.color == $TYPE_NAME$TreeSetNode.RED) then
set sibling.color = $TYPE_NAME$TreeSetNode.RED
set sibling.right.color = $TYPE_NAME$TreeSetNode.BLACK
call sibling.rotateLeft()
endif
endif
call this.removeCaseSix(node)
endmethod
private method removeCaseFour takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode sibling = node.sibling
if (node.parent.color == $TYPE_NAME$TreeSetNode.RED and sibling.color == $TYPE_NAME$TreeSetNode.BLACK and sibling.left.color == $TYPE_NAME$TreeSetNode.BLACK and sibling.right.color == $TYPE_NAME$TreeSetNode.BLACK) then
set sibling.color = $TYPE_NAME$TreeSetNode.RED
set node.parent.color = $TYPE_NAME$TreeSetNode.BLACK
else
call this.removeCaseFive(node)
endif
endmethod
private method removeCaseThree takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode sibling = node.sibling
if (node.parent.color == $TYPE_NAME$TreeSetNode.BLACK and sibling.color == $TYPE_NAME$TreeSetNode.BLACK and sibling.left.color == $TYPE_NAME$TreeSetNode.BLACK and sibling.right.color == $TYPE_NAME$TreeSetNode.BLACK) then
set sibling.color = $TYPE_NAME$TreeSetNode.RED
call this.removeCaseOne.evaluate(node.parent)
else
call this.removeCaseFour(node)
endif
endmethod
private method removeCaseTwo takes $TYPE_NAME$TreeSetNode node returns nothing
local $TYPE_NAME$TreeSetNode sibling = node.sibling
if (sibling.color == $TYPE_NAME$TreeSetNode.RED) then
set node.parent.color = $TYPE_NAME$TreeSetNode.RED
set sibling.color = $TYPE_NAME$TreeSetNode.BLACK
if (node == node.parent.left) then
call node.parent.rotateLeft()
else
call node.parent.rotateRight()
endif
endif
call this.removeCaseThree(node)
endmethod
private method removeCaseOne takes $TYPE_NAME$TreeSetNode node returns nothing
if (node.parent != 0) then
call this.removeCaseTwo(node)
endif
endmethod
endstruct
struct $TYPE_NAME$TreeSetIterator extends $TYPE_NAME$Iterator
private $TYPE_NAME$TreeSet s
private $TYPE_NAME$Array ar
private integer n
private integer last = -1
private integer current = 0
static method create takes $TYPE_NAME$TreeSet s returns thistype
local thistype this = thistype.allocate()
set this.s = s
set this.ar = s.toArray()
set this.n = s.n
return this
endmethod
method hasNext takes nothing returns boolean
return this.current < this.n
endmethod
method next takes nothing returns $TYPE$
if (this.hasNext()) then
set this.last = this.current
set this.current = this.current + 1
return this.ar[this.last]
endif
return 0
endmethod
method remove takes nothing returns nothing
if (this.last != -1) then
call this.s.remove(this.ar[this.last])
set this.last = -1
endif
endmethod
method destroy takes nothing returns nothing
call this.ar.destroy()
call this.deallocate()
endmethod
endstruct
endlibrary
//! endtextmacro
//TESH.scrollpos=59
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.0
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht = InitHashtable()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
++++++++++++++++++++++++
+ Spell Engine +
+ v0.3.2 +
+ Author: aznricepuff +
++++++++++++++++++++++++
*/
library SpellEngine requires Table, SpellListenerTreeSet, SpellType
//! external ObjectMerger w3a Aasl IMBA anam "Immobilized" ansf "" Slo1 1 -10.00 aare 1 100.0 abuf 1 IMBB arac "other" atar 1 "self"
//! external ObjectMerger w3h Basl IMBB fnam "Immobilized" ftip "Immobilized" fnsf "" fube "This unit is immobilized." fart "ReplaceableTextures\CommandButtons\BTNCancel.blp" ftat ""
private keyword ph
private keyword cte
private keyword chte
private keyword dead
private keyword spells
private keyword activeSpells
private keyword listeners
struct Spell
static constant integer TARGET_INSTANT = 1
static constant integer TARGET_UNIT = 2
static constant integer TARGET_ITEM = 3
static constant integer TARGET_DESTRUCTABLE = 4
static constant integer TARGET_POINT = 5
private integer id
static method create takes integer abilId returns Spell
local Spell this = Spell.allocate()
set this.id = abilId
return this
endmethod
method operator abilId takes nothing returns integer
return .id
endmethod
stub method onInitialize takes EventSpell eventSpell returns nothing
endmethod
stub method onCastBegin takes EventSpell eventSpell returns nothing
endmethod
stub method onCastTick takes EventSpell eventSpell, real period returns nothing
endmethod
stub method onCastFinish takes EventSpell eventSpell returns nothing
endmethod
stub method onEffect takes EventSpell eventSpell returns nothing
endmethod
stub method onChannelBegin takes EventSpell eventSpell returns nothing
endmethod
stub method onChannelTick takes EventSpell eventSpell, real period returns nothing
endmethod
stub method onChannelFinish takes EventSpell eventSpell returns nothing
endmethod
stub method onTerminate takes EventSpell eventSpell, unit source, integer data returns boolean
return true
endmethod
stub method onFinalTerminate takes EventSpell eventSpell, unit source, integer data returns nothing
endmethod
stub method onEnd takes EventSpell eventSpell returns nothing
endmethod
method destroy takes nothing returns nothing
if (SpellController.getSpell(this.abilId) == this) then
call SpellController.removeSpell(this.abilId)
endif
call this.deallocate()
endmethod
endstruct
struct SpellListener
private integer pr
static method create takes integer priority returns SpellListener
local SpellListener this = SpellListener.allocate()
set this.pr = priority
return this
endmethod
method operator priority takes nothing returns integer
return .pr
endmethod
method operator < takes SpellListener other returns boolean
if (.priority == other.priority) then
return this < other
endif
return .priority < other.priority
endmethod
stub method onInitialize takes EventSpell eventSpell returns nothing
endmethod
stub method onCastBegin takes EventSpell eventSpell returns nothing
endmethod
stub method onCastTick takes EventSpell eventSpell, real period returns nothing
endmethod
stub method onCastFinish takes EventSpell eventSpell returns nothing
endmethod
stub method onEffect takes EventSpell eventSpell returns nothing
endmethod
stub method onChannelBegin takes EventSpell eventSpell returns nothing
endmethod
stub method onChannelTick takes EventSpell eventSpell, real period returns nothing
endmethod
stub method onChannelFinish takes EventSpell eventSpell returns nothing
endmethod
stub method onTerminate takes EventSpell eventSpell, unit source, integer data returns boolean
return true
endmethod
stub method onFinalTerminate takes EventSpell eventSpell, unit source, integer data returns nothing
endmethod
stub method onEnd takes EventSpell eventSpell returns nothing
endmethod
endstruct
struct EventSpell
static constant integer PHASE_INITIALIZE = 1
static constant integer PHASE_PRECAST = 2
static constant integer PHASE_CAST = 3
static constant integer PHASE_EFFECT = 4
static constant integer PHASE_CHANNEL = 5
private Spell sp
private delegate SpellType st
integer level
private unit cast
boolean channeling
unit targetUnit
item targetItem
destructable targetDestructable
real targetX
real targetY
integer data
private integer tar
integer ph
real castTime = 0.0
real channelTime = 0.0
boolean dead = false
real cte = 0.0
real chte = 0.0
static method create takes Spell spell, integer level, unit caster, unit targetUnit, item targetItem, destructable targetDestructable, real targetX, real targetY, integer targeting returns EventSpell
local EventSpell this = EventSpell.allocate()
set this.sp = spell
set this.st = SpellType.create()
set this.level = level
set this.channeling = false
set this.cast = caster
set this.targetUnit = targetUnit
set this.targetItem = targetItem
set this.targetDestructable = targetDestructable
set this.targetX = targetX
set this.targetY = targetY
set this.tar = targeting
set this.ph = 0
set this.data = 0
return this
endmethod
method operator spell takes nothing returns Spell
return .sp
endmethod
method operator spellType takes nothing returns SpellType
return .st
endmethod
method operator caster takes nothing returns unit
return .cast
endmethod
method operator phase takes nothing returns integer
return this.ph
endmethod
method operator targeting takes nothing returns integer
return .tar
endmethod
method operator castTimeElapsed takes nothing returns real
return this.cte
endmethod
method operator channelTimeElapsed takes nothing returns real
return this.chte
endmethod
method terminate takes unit source, integer data returns boolean
local SpellListenerIterator it
local boolean dead = true
if (this.dead == false) then
set it = SpellController.listeners.iterator()
loop
exitwhen (it.hasNext() == false or dead == false)
set dead = it.next().onTerminate(this, source, data)
endloop
call it.destroy()
if (dead and .spell.onTerminate(this, source, data)) then
call .forceTerminate(source, data)
endif
endif
return this.dead
endmethod
method forceTerminate takes unit source, integer data returns nothing
local SpellListenerIterator it
if (this.dead == false) then
set this.dead = true
call .spell.onFinalTerminate(this, source, data)
set it = SpellController.listeners.iterator()
loop
exitwhen (it.hasNext() == false)
call it.next().onFinalTerminate(this, source, data)
endloop
call it.destroy()
endif
endmethod
method destroy takes nothing returns nothing
local SpellListenerIterator it = SpellController.listeners.iterator()
if (GetUnitAbilityLevel(this.caster, SpellController.IMMOBILIZE_SPELL_ABIL_ID) > 0) then
call UnitRemoveAbility(this.caster, SpellController.IMMOBILIZE_SPELL_ABIL_ID)
call UnitRemoveAbility(this.caster, SpellController.IMMOBILIZE_SPELL_BUFF_ID)
endif
call this.spell.onEnd(this)
loop
exitwhen (it.hasNext() == false)
call it.next().onEnd(this)
endloop
call it.destroy()
call this.st.destroy()
if (SpellController.activeSpells[this.caster] == this) then
call SpellController.activeSpells.flush(this.caster)
endif
call this.deallocate()
endmethod
endstruct
struct SpellController
static constant integer IMMOBILIZE_SPELL_ABIL_ID = 'IMBA'
static constant integer IMMOBILIZE_SPELL_BUFF_ID = 'IMBB'
static constant real TIMER_PERIOD = 0.04
static Table spells
static HandleTable activeSpells
private static timer t = CreateTimer()
private static EventSpell array timerAr
private static integer n = 0
static SpellListenerSet listeners
static method getSpell takes integer abilId returns Spell
return SpellController.spells[abilId]
endmethod
static method getActiveSpell takes unit u returns EventSpell
return SpellController.activeSpells[u]
endmethod
static method registerSpell takes Spell spell returns nothing
set SpellController.spells[spell.abilId] = spell
endmethod
static method removeSpell takes integer abilId returns nothing
call SpellController.spells.flush(abilId)
endmethod
static method registerListener takes SpellListener listener returns boolean
return SpellController.listeners.add(listener)
endmethod
static method removeListener takes SpellListener listener returns boolean
return SpellController.listeners.remove(listener)
endmethod
private static method finishChannel takes EventSpell eventSpell returns nothing
local SpellListenerIterator it = SpellController.listeners.iterator()
call eventSpell.spell.onChannelFinish(eventSpell)
loop
exitwhen (it.hasNext() == false)
call it.next().onChannelFinish(eventSpell)
endloop
call it.destroy()
call eventSpell.destroy()
endmethod
private static method startChannel takes EventSpell eventSpell returns nothing
local SpellListenerIterator it = SpellController.listeners.iterator()
call eventSpell.spell.onChannelBegin(eventSpell)
loop
exitwhen (it.hasNext() == false or eventSpell.dead)
call it.next().onChannelBegin(eventSpell)
endloop
call it.destroy()
if (eventSpell.dead) then
call eventSpell.destroy()
else
if (eventSpell.channelTime > 0.0) then
call UnitAddAbility(eventSpell.caster, .IMMOBILIZE_SPELL_ABIL_ID)
set eventSpell.ph = EventSpell.PHASE_CHANNEL
set .timerAr[.n] = eventSpell
set .n = .n + 1
if (.n == 1) then
call TimerStart(.t, .TIMER_PERIOD, true, function SpellController.tick)
endif
else
call .finishChannel(eventSpell)
endif
endif
endmethod
private static method startEffect takes EventSpell eventSpell returns nothing
local SpellListenerIterator it
if (eventSpell.channeling) then
call .startChannel(eventSpell)
else
set eventSpell.ph = EventSpell.PHASE_EFFECT
set it = SpellController.listeners.iterator()
call eventSpell.spell.onEffect(eventSpell)
loop
exitwhen (it.hasNext() == false or eventSpell.dead)
call it.next().onEffect(eventSpell)
endloop
call it.destroy()
call eventSpell.destroy()
endif
endmethod
private static method finishCast takes EventSpell eventSpell returns nothing
local SpellListenerIterator it = SpellController.listeners.iterator()
call eventSpell.spell.onCastFinish(eventSpell)
loop
exitwhen (it.hasNext() == false or eventSpell.dead)
call it.next().onCastFinish(eventSpell)
endloop
call it.destroy()
if (eventSpell.dead) then
call eventSpell.destroy()
else
call .startEffect(eventSpell)
endif
endmethod
private static method startCast takes EventSpell eventSpell returns nothing
local SpellListenerIterator it = SpellController.listeners.iterator()
call eventSpell.spell.onCastBegin(eventSpell)
loop
exitwhen (it.hasNext() == false or eventSpell.dead)
call it.next().onCastBegin(eventSpell)
endloop
call it.destroy()
if (eventSpell.dead) then
call eventSpell.destroy()
else
call UnitAddAbility(eventSpell.caster, .IMMOBILIZE_SPELL_ABIL_ID)
set eventSpell.ph = EventSpell.PHASE_CAST
if (eventSpell.castTime > 0.0) then
set .timerAr[.n] = eventSpell
set .n = .n + 1
if (.n == 1) then
call TimerStart(.t, .TIMER_PERIOD, true, function SpellController.tick)
endif
else
call .finishCast(eventSpell)
endif
endif
endmethod
private static method startInitialize takes EventSpell eventSpell returns nothing
local SpellListenerIterator it = SpellController.listeners.iterator()
set eventSpell.ph = EventSpell.PHASE_INITIALIZE
call eventSpell.spell.onInitialize(eventSpell)
loop
exitwhen (it.hasNext() == false or eventSpell.dead)
call it.next().onInitialize(eventSpell)
endloop
call it.destroy()
if (eventSpell.dead) then
call eventSpell.destroy()
call IssueImmediateOrder(eventSpell.caster, "stop")
else
set eventSpell.ph = EventSpell.PHASE_PRECAST
endif
endmethod
private static method onSpellCast takes nothing returns nothing
local integer abilId = GetSpellAbilityId()
local Spell spell = .getSpell(abilId)
local EventSpell eventSpell
local unit caster = GetTriggerUnit()
local unit targetUnit = GetSpellTargetUnit()
local item targetItem = GetSpellTargetItem()
local destructable targetDestructable = GetSpellTargetDestructable()
local location targetLoc = GetSpellTargetLoc()
local integer level = GetUnitAbilityLevel(caster, abilId)
local integer targeting
if (spell != 0) then
if (targetUnit != null) then
set targeting = Spell.TARGET_UNIT
elseif (targetItem != null) then
set targeting = Spell.TARGET_ITEM
elseif (targetDestructable != null) then
set targeting = Spell.TARGET_DESTRUCTABLE
elseif (targetLoc != null) then
set targeting = Spell.TARGET_POINT
else
set targeting = Spell.TARGET_INSTANT
endif
set eventSpell = EventSpell.create(spell, level, caster, targetUnit, targetItem, targetDestructable, GetLocationX(targetLoc), GetLocationY(targetLoc), targeting)
set SpellController.activeSpells[caster] = eventSpell
call .startInitialize(eventSpell)
endif
call RemoveLocation(targetLoc)
set caster = null
set targetUnit = null
set targetItem = null
set targetDestructable = null
set targetLoc = null
endmethod
private static method onSpellEffect takes nothing returns nothing
local Spell spell = .getSpell(GetSpellAbilityId())
local EventSpell eventSpell = SpellController.activeSpells[GetTriggerUnit()]
if (spell != 0) then
call .startCast(eventSpell)
endif
endmethod
private static method onInterrupt takes nothing returns nothing
local unit u = GetTriggerUnit()
local EventSpell eventSpell = SpellController.activeSpells[u]
if (eventSpell != 0) then
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_DEATH) then
call eventSpell.forceTerminate(GetKillingUnit(), 0)
elseif (eventSpell.phase == EventSpell.PHASE_PRECAST or eventSpell.phase == EventSpell.PHASE_CAST or eventSpell.phase == EventSpell.PHASE_CHANNEL) then
call eventSpell.forceTerminate(u, 0)
endif
endif
set u = null
endmethod
private static method onRemoveUnit takes unit u returns nothing
local EventSpell eventSpell = SpellController.activeSpells[u]
if (eventSpell != 0) then
call eventSpell.forceTerminate(null, 0)
endif
endmethod
private static method tick takes nothing returns nothing
local EventSpell eventSpell
local integer i = 0
local SpellListenerIterator it
loop
exitwhen (i >= .n)
set eventSpell = .timerAr[i]
set eventSpell.dead = eventSpell.dead or GetWidgetLife(eventSpell.caster) < 0.405 or GetUnitTypeId(eventSpell.caster) == 0
if (eventSpell.dead) then
set .timerAr[i] = .timerAr[.n - 1]
set .n = .n - 1
set i = i - 1
call eventSpell.destroy()
else
if (eventSpell.phase == EventSpell.PHASE_CAST) then
if (eventSpell.castTimeElapsed >= eventSpell.castTime) then
set eventSpell.cte = eventSpell.castTime
set .timerAr[i] = .timerAr[.n - 1]
set .n = .n - 1
set i = i - 1
call UnitRemoveAbility(eventSpell.caster, .IMMOBILIZE_SPELL_ABIL_ID)
call UnitRemoveAbility(eventSpell.caster, .IMMOBILIZE_SPELL_BUFF_ID)
call .finishCast(eventSpell)
else
set it = SpellController.listeners.iterator()
set eventSpell.cte = eventSpell.cte + .TIMER_PERIOD
call eventSpell.spell.onCastTick(eventSpell, .TIMER_PERIOD)
loop
exitwhen (it.hasNext() == false)
call it.next().onCastTick(eventSpell, .TIMER_PERIOD)
endloop
call it.destroy()
endif
else
if (eventSpell.channelTimeElapsed >= eventSpell.channelTime) then
set eventSpell.chte = eventSpell.channelTime
set .timerAr[i] = .timerAr[.n - 1]
set .n = .n - 1
set i = i - 1
call UnitRemoveAbility(eventSpell.caster, .IMMOBILIZE_SPELL_ABIL_ID)
call UnitRemoveAbility(eventSpell.caster, .IMMOBILIZE_SPELL_BUFF_ID)
call .finishChannel(eventSpell)
else
set it = SpellController.listeners.iterator()
set eventSpell.chte = eventSpell.chte + .TIMER_PERIOD
call eventSpell.spell.onChannelTick(eventSpell, .TIMER_PERIOD)
loop
exitwhen (it.hasNext() == false)
call it.next().onChannelTick(eventSpell, .TIMER_PERIOD)
endloop
call it.destroy()
endif
endif
endif
set i = i + 1
endloop
if (.n == 0) then
call PauseTimer(.t)
endif
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
set SpellController.spells = Table.create()
set SpellController.activeSpells = HandleTable.create()
set SpellController.listeners = SpellListenerBalancedTreeSet.create(0)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddAction(t, function SpellController.onSpellCast)
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t, function SpellController.onSpellEffect)
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction(t, function SpellController.onInterrupt)
endmethod
endstruct
hook RemoveUnit SpellController.onRemoveUnit
endlibrary
//! runtextmacro Collection("SpellListener", "SpellListener", "SPELL_LISTENER")
//! runtextmacro TreeSet("SpellListener", "SpellListener", "SPELL_LISTENER")
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
++++++++++++++++++++++++
+ Spell Type +
+ v0.3.2 +
+ Author: aznricepuff +
++++++++++++++++++++++++
*/
library SpellType requires Bitfield
//**** Begin example code...
globals
constant integer TYPE_PHYSICAL = 0x00001
constant integer TYPE_ARCANE = 0x00002
constant integer TYPE_ENCHANTMENT = 0x00004
constant integer TYPE_DAMAGING = 0x00008
constant integer TYPE_HEALING = 0x00010
constant integer TYPE_FIRE = 0x00020
constant integer TYPE_WATER = 0x00040
constant integer TYPE_AIR = 0x00080
constant integer TYPE_EARTH = 0x00100
endglobals
//**** End example code...
struct SpellType
// ========================= Configuration =========================
static constant integer FLAG_MAX_EXPONENT = 8
// =================================================================
private delegate Bitfield bf
static method create takes nothing returns SpellType
local SpellType this = SpellType.allocate()
set this.bf = Bitfield.create(.FLAG_MAX_EXPONENT)
return this
endmethod
method destroy takes nothing returns nothing
call this.bf.destroy()
call this.deallocate()
endmethod
endstruct
endlibrary
//TESH.scrollpos=24
//TESH.alwaysfold=0
library Bitfield
struct Bitfield
private integer bf = 0
private integer maxExponent
static method create takes integer maxExponent returns Bitfield
local Bitfield this = Bitfield.allocate()
set this.maxExponent = maxExponent
return this
endmethod
method operator bitfield takes nothing returns integer
return .bf
endmethod
method hasFlag takes integer flag returns boolean
local integer bf = .bf
local integer exp
local integer i = IMinBJ(.maxExponent, 30)
loop
exitwhen (i < 0)
set exp = R2I(Pow(2, i))
if (exp <= bf) then
set bf = bf - exp
if (flag - exp >= 0) then
set flag = flag - exp
endif
endif
if (flag == 0) then
return true
elseif (bf == 0) then
return false
endif
set i = i - 1
endloop
return false
endmethod
method addFlag takes integer flag returns nothing
local integer bf = .bf
local integer exp
local integer i = IMinBJ(.maxExponent, 30)
loop
exitwhen (i < 0 or flag == 0)
set exp = R2I(Pow(2, i))
if (exp > bf) then
if (flag - exp >= 0) then
set flag = flag - exp
set .bf = .bf + exp
endif
else
set bf = bf - exp
endif
set i = i - 1
endloop
endmethod
method removeFlag takes integer flag returns nothing
if (.hasFlag(flag)) then
set .bf = .bf - flag
endif
endmethod
method clear takes nothing returns nothing
set .bf = 0
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
v0.3.2
-Now requires jasshelper 0.9.Z.0+.
-Now uses TreeSet v0.6.0.
-Optimized internal coding to use fewer calls to TriggerEvaluate.
v0.3.1
-EventSpell now delegates to SpellType instead of referencing an instance to clean up the API a little.
-Event spells now correctly (force) terminate when a unit dies while casting (the source for the termination is the killing unit).
-Hooked RemoveUnit() so that event spells (force) terminate when a unit is removed (the source for the termination is null).
v0.3.0
-Added Spell.onFinalTerminate() and SpellListener.onFinalTerminate(), and changed behavior of Spell.onTerminate() and SpellListener.onTerminate() so that they are not called if eventSpell.forceTerminate() is called.
-Added SpellType and EventSpell.spellType.
v0.2.0
-First release.
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once TimerUtils initializer init
//*********************************************************************
//* TimerUtils (Blue flavor for 1.23b or later)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//* limit, which means that if more than 408000 handle ids
//* are used in your map, TimerUtils might fail, this
//* value is quite big and it is much bigger than the
//* timer limit in Red flavor.
//*
//********************************************************************
//==================================================================================================
globals
private hashtable hasht //I <3 blizz
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
call SaveInteger(hasht,0, GetHandleId(t), value)
endfunction
function GetTimerData takes timer t returns integer
return LoadInteger(hasht, 0, GetHandleId(t))
endfunction
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
set tT[0]=CreateTimer()
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==8191) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
set hasht = InitHashtable()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Stuff
struct EffectData
effect fx
static method create takes effect fx returns EffectData
local EffectData this = EffectData.allocate()
set this.fx = fx
return this
endmethod
endstruct
struct UnitData
unit u
static method create takes unit u returns UnitData
local UnitData this = UnitData.allocate()
set this.u = u
return this
endmethod
endstruct
function AddLife takes unit u, real amount returns real
local real life = GetWidgetLife(u)
local real maxLife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
if (life + amount > maxLife) then
set amount = maxLife - life
endif
call SetWidgetLife(u, life + amount)
return amount
endfunction
function AddMana takes unit u, real amount returns real
local real mana = GetUnitState(u, UNIT_STATE_MANA)
local real maxMana = GetUnitState(u, UNIT_STATE_MAX_MANA)
if (mana + amount > maxMana) then
set amount = maxMana - mana
endif
call SetUnitState(u, UNIT_STATE_MANA, mana + amount)
return amount
endfunction
function SubtractLife takes unit u, real amount returns real
local real life = GetWidgetLife(u)
if (life - amount < 0) then
set amount = life
endif
call SetWidgetLife(u, life - amount)
return amount
endfunction
function SubtractMana takes unit u, real amount returns real
local real mana = GetUnitState(u, UNIT_STATE_MANA)
if (mana - amount < 0) then
set amount = mana
endif
call SetUnitState(u, UNIT_STATE_MANA, mana - amount)
return amount
endfunction
endlibrary
//TESH.scrollpos=28
//TESH.alwaysfold=0
library ARGB initializer init
//******************************************************************************
//*
//* ARGB 1.1
//* ====
//* For your color needs.
//*
//* An ARGB object is a by-value struct, this means that assigning copies the
//* contents of the struct and that you don't have to use .destroy(), the
//* downside is that you cannot assign its members (can't do set c.r= 123 )
//*
//* This library should have plenty of uses, for example, if your spell involves
//* some unit recoloring you can allow users to input the color in the config
//* section as 0xAARRGGBB and you can then use this to decode that stuff.
//*
//* You can also easily merge two colors and make fading effects using ARGB.mix
//*
//* There's ARGB.fromPlayer which gets an ARGB object containing the player's
//* color. Then you can use the previous utilities on it.
//*
//* The .str() instance method can recolor a string, and the recolorUnit method
//* will apply the ARGB on a unit
//*
//* For other uses, you can use the .red, .green, .blue and .alpha members to get
//* an ARGB object's color value (from 0 to 255).
//*
//* structs that have a recolor method that takes red,green,blue and alpha as 0.255
//* integers can implement the ARGBrecolor module to gain an ability to quickly
//* recolor using an ARGB object.
//*
//********************************************************************************
//=================================================================================
globals
private string array i2cc
endglobals
//this double naming stuff is beginning to make me insane, if only TriggerEvaluate() wasn't so slow...
struct ARGB extends array
static method create takes integer a, integer r, integer g, integer b returns ARGB
return ARGB(b + g*0x100 + r*0x10000 + a*0x1000000)
endmethod
static method fromPlayer takes player p returns ARGB
local playercolor pc=GetPlayerColor(p)
if(pc==PLAYER_COLOR_RED) then
return 0xFFFF0303
elseif(pc==PLAYER_COLOR_BLUE) then
return 0xFF0042FF
elseif(pc==PLAYER_COLOR_CYAN) then
return 0xFF1CB619
elseif(pc==PLAYER_COLOR_PURPLE) then
return 0xFF540081
elseif(pc==PLAYER_COLOR_YELLOW) then
return 0xFFFFFF01
elseif(pc==PLAYER_COLOR_ORANGE) then
return 0xFFFE8A0E
elseif(pc==PLAYER_COLOR_GREEN) then
return 0xFF20C000
elseif(pc==PLAYER_COLOR_PINK) then
return 0xFFE55BB0
elseif(pc==PLAYER_COLOR_LIGHT_GRAY) then
return 0xFF959697
elseif(pc==PLAYER_COLOR_LIGHT_BLUE) then
return 0xFF7EBFF1
elseif(pc==PLAYER_COLOR_AQUA) then
return 0xFF106246
elseif(pc==PLAYER_COLOR_BROWN) then
return 0xFF4E2A04
endif
return 0xFF111111
endmethod
method operator alpha takes nothing returns integer
if( integer(this) <0) then
return 0x80+(-(-integer(this)+0x80000000))/0x1000000
else
return (integer(this))/0x1000000
endif
endmethod
method operator alpha= takes integer na returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + g*0x100 + r*0x10000 + na*0x1000000)
endmethod
method operator red takes nothing returns integer
local integer c=integer(this)*0x100
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator red= takes integer nr returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + g*0x100 + nr*0x10000 + a*0x1000000)
endmethod
method operator green takes nothing returns integer
local integer c=integer(this)*0x10000
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator green= takes integer ng returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + ng*0x100 + r*0x10000 + a*0x1000000)
endmethod
//=======================================================
//
//
method operator blue takes nothing returns integer
local integer c=integer(this)*0x1000000
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator blue= takes integer nb returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(nb + g*0x100 + r*0x10000 + a*0x1000000)
endmethod
//====================================================================
// Mixes two colors, s would be a number 0<=s<=1 that determines
// the weight given to color c2.
//
// mix(c1,c2,0) = c1
// mix(c1,c2,1) = c2
// mix(c1,c2,0.5) = Mixing the colors c1 and c2 in equal proportions.
//
static method mix takes ARGB c1, ARGB c2, real s returns ARGB
//widest function ever
return ARGB( R2I(c2.blue*s+c1.blue*(1-s)+0.5) + R2I(c2.green*s+c1.green*(1-s)+0.5)*0x100 + R2I(c2.red*s+c1.red*(1-s)+0.5)*0x10000 + R2I(c2.alpha*s+c1.alpha*(1-s)+0.5)*0x1000000)
endmethod
method str takes string s returns string
return "|c"+i2cc[.alpha]+i2cc[.red]+i2cc[.green]+i2cc[.blue]+s+"|r"
endmethod
method recolorUnit takes unit u returns nothing
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
call SetUnitVertexColor(u,r,g,b,a)
endmethod
endstruct
module ARGBrecolor
method ARGBrecolor takes ARGB color returns nothing
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
call this.recolor(r, g , b, a)
endmethod
endmodule
private function init takes nothing returns nothing
local integer i=0
// Don't run textmacros you don't own!
//! textmacro ARGB_CHAR takes int, chr
set i=0
loop
exitwhen i==16
set i2cc[$int$*16+i]="$chr$"+i2cc[$int$*16+i]
set i2cc[i*16+$int$]=i2cc[i*16+$int$]+"$chr$"
set i=i+1
endloop
//! endtextmacro
//! runtextmacro ARGB_CHAR( "0","0")
//! runtextmacro ARGB_CHAR( "1","1")
//! runtextmacro ARGB_CHAR( "2","2")
//! runtextmacro ARGB_CHAR( "3","3")
//! runtextmacro ARGB_CHAR( "4","4")
//! runtextmacro ARGB_CHAR( "5","5")
//! runtextmacro ARGB_CHAR( "6","6")
//! runtextmacro ARGB_CHAR( "7","7")
//! runtextmacro ARGB_CHAR( "8","8")
//! runtextmacro ARGB_CHAR( "9","9")
//! runtextmacro ARGB_CHAR("10","A")
//! runtextmacro ARGB_CHAR("11","B")
//! runtextmacro ARGB_CHAR("12","C")
//! runtextmacro ARGB_CHAR("13","D")
//! runtextmacro ARGB_CHAR("14","E")
//! runtextmacro ARGB_CHAR("15","F")
endfunction
endlibrary
//TESH.scrollpos=45
//TESH.alwaysfold=0
library PrettySpells initializer init requires SpellEngine, ARGB
private keyword initialize
struct PrettySpell extends Spell
private static Table fx
ARGB castTitleRgb = 0xffffcc00
ARGB castBackgroundRgb = 0xff646464
ARGB castForegroundRgb = 0xffffcc00
ARGB castFinishForegroundRgb = 0x0000ff00
ARGB channelTitleRgb = 0xff87ceeb
ARGB channelBackgroundRgb = 0xff646464
ARGB channelForegroundRgb = 0xff87ceeb
ARGB interruptRgb = 0xff400080
stub method onInitialize takes EventSpell eventSpell returns nothing
set .fx[eventSpell] = SpellEffect.create(eventSpell)
endmethod
stub method onCastBegin takes EventSpell eventSpell returns nothing
local SpellEffect spellEffect = .fx[eventSpell]
call spellEffect.onCastBegin()
endmethod
stub method onCastTick takes EventSpell eventSpell, real period returns nothing
local SpellEffect spellEffect = .fx[eventSpell]
call spellEffect.onCastTick()
endmethod
stub method onCastFinish takes EventSpell eventSpell returns nothing
local SpellEffect spellEffect = .fx[eventSpell]
call spellEffect.onCastFinish()
endmethod
stub method onEffect takes EventSpell eventSpell returns nothing
endmethod
stub method onChannelBegin takes EventSpell eventSpell returns nothing
local SpellEffect spellEffect = .fx[eventSpell]
call spellEffect.onChannelBegin()
endmethod
stub method onChannelTick takes EventSpell eventSpell, real period returns nothing
local SpellEffect spellEffect = .fx[eventSpell]
call spellEffect.onChannelTick()
endmethod
stub method onChannelFinish takes EventSpell eventSpell returns nothing
local SpellEffect spellEffect = .fx[eventSpell]
call spellEffect.onChannelFinish()
endmethod
stub method onFinalTerminate takes EventSpell eventSpell, unit source, integer data returns nothing
local SpellEffect spellEffect = .fx[eventSpell]
call spellEffect.onFinalTerminate()
endmethod
stub method onEnd takes EventSpell eventSpell returns nothing
local SpellEffect spellEffect = .fx[eventSpell]
call spellEffect.onEnd()
endmethod
static method initialize takes nothing returns nothing
set .fx = Table.create()
endmethod
endstruct
struct SpellEffect
static constant real ANIMATION_PERIOD = 0.035
private EventSpell eventSpell
private ProgressBar bar = 0
private boolean toDestroy = false
private ARGB castTitleRgb = 0xffffcc00
private ARGB castBackgroundRgb = 0xff646464
private ARGB castForegroundRgb = 0xffffcc00
private ARGB castFinishForegroundRgb = 0x0000ff00
private ARGB channelTitleRgb = 0xff87ceeb
private ARGB channelBackgroundRgb = 0xff646464
private ARGB channelForegroundRgb = 0xff87ceeb
private ARGB interruptRgb = 0xff400080
static method create takes EventSpell eventSpell returns SpellEffect
local SpellEffect this = SpellEffect.allocate()
local PrettySpell spell = eventSpell.spell
set this.eventSpell = eventSpell
set this.castTitleRgb = spell.castTitleRgb
set this.castBackgroundRgb = spell.castBackgroundRgb
set this.castForegroundRgb = spell.castForegroundRgb
set this.castFinishForegroundRgb = spell.castFinishForegroundRgb
set this.channelTitleRgb = spell.channelTitleRgb
set this.channelBackgroundRgb = spell.channelBackgroundRgb
set this.channelForegroundRgb = spell.channelForegroundRgb
set this.interruptRgb = spell.interruptRgb
return this
endmethod
method onCastBegin takes nothing returns nothing
if (.eventSpell.castTime > 0) then
set .bar = ProgressBar.create(GetObjectName(.eventSpell.spell.abilId), 12.0 * 0.023 / 10, GetUnitX(.eventSpell.caster) - 50.0, GetUnitY(.eventSpell.caster) + 50.0, 150.0, 12.5 * 0.023 / 10, 20, 0)
call .bar.setTitleColor(.castTitleRgb)
call .bar.setBackgroundColor(.castBackgroundRgb)
call .bar.setForegroundColor(.castForegroundRgb)
call .bar.setTarget(.eventSpell.caster)
call .bar.setTargetXOffset(-50.0)
call .bar.setFadeDuration(1.00)
endif
endmethod
method onCastTick takes nothing returns nothing
call .bar.setPercent(.eventSpell.castTimeElapsed / .eventSpell.castTime)
endmethod
method onCastFinish takes nothing returns nothing
call .bar.setTitleColor(.castFinishForegroundRgb)
call .bar.setForegroundColor(.castFinishForegroundRgb)
call .bar.setPercent(1)
call .bar.terminate()
set .bar = 0
endmethod
method onChannelBegin takes nothing returns nothing
if (.eventSpell.channelTime > 0) then
set .bar = ProgressBar.create(GetObjectName(.eventSpell.spell.abilId), 12.0 * 0.023 / 10, GetUnitX(.eventSpell.caster) - 50.0, GetUnitY(.eventSpell.caster) + 50.0, 150.0, 12.5 * 0.023 / 10, 20, 0)
call .bar.setTitleColor(.channelTitleRgb)
call .bar.setBackgroundColor(.channelBackgroundRgb)
call .bar.setForegroundColor(.channelForegroundRgb)
call .bar.setTarget(.eventSpell.caster)
call .bar.setTargetXOffset(-50.0)
call .bar.setFadeDuration(1.00)
endif
endmethod
method onChannelTick takes nothing returns nothing
call .bar.setPercent(.eventSpell.channelTimeElapsed / .eventSpell.channelTime)
endmethod
method onChannelFinish takes nothing returns nothing
call .bar.setPercent(1)
call .bar.terminate()
set .bar = 0
endmethod
method onFinalTerminate takes nothing returns nothing
if (.bar != 0) then
call .bar.setTitleColor(.interruptRgb)
call .bar.setForegroundColor(.interruptRgb)
endif
endmethod
method onEnd takes nothing returns nothing
call .destroy()
endmethod
private method onDestroy takes nothing returns nothing
if (.bar != 0) then
call .bar.terminate()
endif
endmethod
endstruct
struct ProgressBar
private static constant real TIMER_PERIOD = 0.035
private static constant string UNIT = "'"
private static ARGB DEFAULT_TITLE_COLOR = 0xffccff00
private static ARGB DEFAULT_BACKGROUND_COLOR = 0xff646464
private static ARGB DEFAULT_FOREGROUND_COLOR = 0xffccff00
private static timer t = CreateTimer()
private static ProgressBar array ar
private static integer n = 0
private texttag title
private texttag back
private texttag fore
private string titleString
private real x
private real y
private real z
private unit target = null
private real targetXOffset = 0
private real targetYOffset = 0
private real titleHeight
private real barHeight
private real titleZOffset = 150.0
private real barZOffset = 100.0
private integer length
private real percent
private real fadeDuration = 0.0
private boolean locked = false
private boolean toDestroy = false
static method create takes string title, real titleHeight, real x, real y, real z, real barHeight, integer length, real initialPercent returns ProgressBar
local ProgressBar this = ProgressBar.allocate()
local string s = ""
local integer foreLength = .percentToLength(initialPercent, length)
local integer i = 0
set this.titleString = title
set this.x = x
set this.y = y
set this.z = z
set this.titleHeight = titleHeight
set this.barHeight = barHeight
set this.length = length
set this.percent = initialPercent
set this.title = CreateTextTag()
set this.back = CreateTextTag()
set this.fore = CreateTextTag()
call SetTextTagText(this.title, .titleString, titleHeight)
call SetTextTagColor(this.title, .DEFAULT_TITLE_COLOR.red, .DEFAULT_TITLE_COLOR.green, .DEFAULT_TITLE_COLOR.blue, .DEFAULT_TITLE_COLOR.alpha)
call SetTextTagPos(this.title, x, y, z + .titleZOffset)
call SetTextTagPermanent(this.title, true)
call SetTextTagVisibility(this.title, true)
loop
exitwhen (i >= length)
set s = s + .UNIT
set i = i + 1
endloop
call SetTextTagText(this.back, s, barHeight)
call SetTextTagColor(this.back, .DEFAULT_BACKGROUND_COLOR.red, .DEFAULT_BACKGROUND_COLOR.green, .DEFAULT_BACKGROUND_COLOR.blue, .DEFAULT_BACKGROUND_COLOR.alpha)
call SetTextTagPos(this.back, x, y, z + .barZOffset)
call SetTextTagPermanent(this.back, true)
call SetTextTagVisibility(this.back, true)
set i = 0
set s = ""
loop
exitwhen (i >= foreLength)
set s = s + .UNIT
set i = i + 1
endloop
call SetTextTagText(this.fore, s, barHeight)
call SetTextTagColor(this.fore, .DEFAULT_BACKGROUND_COLOR.red, .DEFAULT_FOREGROUND_COLOR.green, .DEFAULT_FOREGROUND_COLOR.blue, .DEFAULT_FOREGROUND_COLOR.alpha)
call SetTextTagPos(this.fore, x, y, z + .barZOffset)
call SetTextTagPermanent(this.fore, true)
call SetTextTagVisibility(this.fore, true)
return this
endmethod
method getTitle takes nothing returns string
return .titleString
endmethod
method getPositionX takes nothing returns real
return .x
endmethod
method getPositionY takes nothing returns real
return .y
endmethod
method getPositionZ takes nothing returns real
return .z
endmethod
method getTarget takes nothing returns unit
return .target
endmethod
method getTargetXOffset takes nothing returns real
return .targetXOffset
endmethod
method getTargetYOffset takes nothing returns real
return .targetYOffset
endmethod
method getPercent takes nothing returns real
return .percent
endmethod
method getLength takes nothing returns integer
return .length
endmethod
method getFadeDuration takes nothing returns real
return .fadeDuration
endmethod
method setTitle takes string s returns nothing
set .titleString = s
call SetTextTagText(.title, .titleString, .titleHeight)
endmethod
method setPosition takes real x, real y, real z returns nothing
set .x = x
set .y = y
set .z = z
call SetTextTagPos(.title, x, y, z + .titleZOffset)
call SetTextTagPos(.back, x, y, z + .barZOffset)
call SetTextTagPos(.fore, x, y, z + .barZOffset)
endmethod
method setTarget takes unit target returns nothing
if (.target == null and target != null) then
set .locked = true
set .ar[.n] = this
set .n = .n + 1
if (.n == 1) then
call TimerStart(.t, .TIMER_PERIOD, true, function ProgressBar.tick)
endif
endif
set .target = target
endmethod
method setTargetXOffset takes real x returns nothing
set .targetXOffset = x
endmethod
method setTargetYOffset takes real y returns nothing
set .targetYOffset = y
endmethod
method setVisibility takes boolean b returns nothing
call SetTextTagVisibility(this.title, b)
call SetTextTagVisibility(this.back, b)
call SetTextTagVisibility(this.fore, b)
endmethod
method setTitleColor takes ARGB rgb returns nothing
call SetTextTagColor(.title, rgb.red, rgb.green, rgb.blue, rgb.alpha)
endmethod
method setBackgroundColor takes ARGB rgb returns nothing
call SetTextTagColor(.back, rgb.red, rgb.green, rgb.blue, rgb.alpha)
endmethod
method setForegroundColor takes ARGB rgb returns nothing
call SetTextTagColor(.fore, rgb.red, rgb.green, rgb.blue, rgb.alpha)
endmethod
method setPercent takes real percent returns nothing
local integer foreLength
local string s = ""
local integer i = 0
if (percent < 0) then
set percent = 0
elseif (percent > 1) then
set percent = 1
endif
set .percent = percent
set foreLength = .percentToLength(.percent, .length)
loop
exitwhen (i >= foreLength)
set s = s + .UNIT
set i = i + 1
endloop
call SetTextTagText(.fore, s, .barHeight)
endmethod
method setFadeDuration takes real duration returns nothing
set .fadeDuration = duration
endmethod
method terminate takes nothing returns nothing
if (.locked) then
set .toDestroy = true
else
call .destroy()
endif
endmethod
private method onDestroy takes nothing returns nothing
call SetTextTagPermanent(.title, false)
call SetTextTagPermanent(.back, false)
call SetTextTagPermanent(.fore, false)
call SetTextTagAge(.title, 0.0)
call SetTextTagAge(.back, 0.0)
call SetTextTagAge(.fore, 0.0)
call SetTextTagLifespan(.title, .fadeDuration)
call SetTextTagLifespan(.back, .fadeDuration)
call SetTextTagLifespan(.fore, .fadeDuration)
call SetTextTagFadepoint(.title, 0.0)
call SetTextTagFadepoint(.back, 0.0)
call SetTextTagFadepoint(.fore, 0.0)
endmethod
private static method percentToLength takes real percent, integer length returns integer
return R2I(percent * length)
endmethod
private static method tick takes nothing returns nothing
local ProgressBar this
local integer i = 0
loop
exitwhen (i >= .n)
set this = .ar[i]
if (this.toDestroy or this.target == null) then
set .ar[i] = .ar[.n - 1]
set .n = .n - 1
set i = i - 1
if (.n == 0) then
call PauseTimer(.t)
endif
if (this.toDestroy) then
call this.destroy()
endif
else
call this.setPosition(GetUnitX(this.target) + this.targetXOffset, GetUnitY(this.target) + this.targetYOffset, this.z)
endif
set i = i + 1
endloop
endmethod
endstruct
private function init takes nothing returns nothing
call PrettySpell.initialize()
endfunction
endlibrary
//TESH.scrollpos=8
//TESH.alwaysfold=0
scope CallLightning
struct CallLightning extends PrettySpell
static constant integer SPELL_ABIL_ID = 'A001'
static constant integer SPELL_TYPE = TYPE_ARCANE + TYPE_DAMAGING + TYPE_AIR
static constant string EFFECT = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl"
static method create takes nothing returns CallLightning
return CallLightning.allocate(.SPELL_ABIL_ID)
endmethod
method onInitialize takes EventSpell eventSpell returns nothing
call eventSpell.addFlag(.SPELL_TYPE)
set eventSpell.castTime = 3.00
call super.onInitialize(eventSpell)
endmethod
method onEffect takes EventSpell eventSpell returns nothing
call DestroyEffect(AddSpecialEffect(.EFFECT, GetUnitX(eventSpell.targetUnit), GetUnitY(eventSpell.targetUnit)))
call UnitDamageTarget(eventSpell.caster, eventSpell.targetUnit, 250, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
call super.onEffect(eventSpell)
endmethod
private static method onInit takes nothing returns nothing
call SpellController.registerSpell(CallLightning.create())
endmethod
endstruct
endscope
//TESH.scrollpos=6
//TESH.alwaysfold=0
scope DeathAndDecay
struct DeathAndDecay extends PrettySpell
static constant integer SPELL_ABIL_ID = 'A003'
static constant integer SPELL_TYPE = TYPE_ARCANE + TYPE_DAMAGING
static constant string EFFECT = "Abilities\\Spells\\Human\\Banish\\BanishTarget.mdl"
static constant string EFFECT_ATTACH = "origin"
static method create takes nothing returns DeathAndDecay
return DeathAndDecay.allocate(.SPELL_ABIL_ID)
endmethod
method onInitialize takes EventSpell eventSpell returns nothing
call eventSpell.addFlag(.SPELL_TYPE)
set eventSpell.channeling = true
set eventSpell.channelTime = 5.00
call super.onInitialize(eventSpell)
endmethod
method onChannelBegin takes EventSpell eventSpell returns nothing
set eventSpell.data = EffectData.create(AddSpecialEffectTarget(.EFFECT, eventSpell.targetUnit, .EFFECT_ATTACH))
call super.onChannelBegin(eventSpell)
endmethod
method onChannelTick takes EventSpell eventSpell, real period returns nothing
call UnitDamageTarget(eventSpell.caster, eventSpell.targetUnit, GetUnitState(eventSpell.targetUnit, UNIT_STATE_MAX_LIFE) * 0.08 * period, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
call super.onChannelTick(eventSpell, period)
endmethod
method onEnd takes EventSpell eventSpell returns nothing
local EffectData data = eventSpell.data
if (data != 0) then
call DestroyEffect(data.fx)
call data.destroy()
endif
call super.onEnd(eventSpell)
endmethod
private static method onInit takes nothing returns nothing
call SpellController.registerSpell(DeathAndDecay.create())
endmethod
endstruct
endscope
//TESH.scrollpos=6
//TESH.alwaysfold=0
scope GrantLife
struct GrantLife extends PrettySpell
static constant integer SPELL_ABIL_ID = 'A004'
static constant integer SPELL_TYPE = TYPE_ARCANE + TYPE_HEALING
static constant string EFFECT = "Abilities\\Spells\\NightElf\\Rejuvenation\\RejuvenationTarget.mdl"
static constant string EFFECT_ATTACH = "chest"
static method create takes nothing returns GrantLife
return GrantLife.allocate(.SPELL_ABIL_ID)
endmethod
method onInitialize takes EventSpell eventSpell returns nothing
call eventSpell.addFlag(.SPELL_TYPE)
set eventSpell.channeling = true
set eventSpell.channelTime = 10.00
call super.onInitialize(eventSpell)
endmethod
method onChannelBegin takes EventSpell eventSpell returns nothing
set eventSpell.data = EffectData.create(AddSpecialEffectTarget(.EFFECT, eventSpell.targetUnit, .EFFECT_ATTACH))
call super.onChannelBegin(eventSpell)
endmethod
method onChannelTick takes EventSpell eventSpell, real period returns nothing
local real mana = RMinBJ(GetUnitState(eventSpell.caster, UNIT_STATE_MANA), 10)
call SubtractMana(eventSpell.caster, AddLife(eventSpell.targetUnit, mana * 2 * period) / 2)
call super.onChannelTick(eventSpell, period)
endmethod
method onEnd takes EventSpell eventSpell returns nothing
local EffectData data = eventSpell.data
call super.onEnd(eventSpell)
if (data != 0) then
call DestroyEffect(data.fx)
call data.destroy()
endif
endmethod
private static method onInit takes nothing returns nothing
call SpellController.registerSpell(GrantLife.create())
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope DistractingBlow
struct DistractingBlow extends PrettySpell
static constant integer SPELL_ABIL_ID = 'A000'
static constant integer SPELL_TYPE = TYPE_PHYSICAL
static method create takes nothing returns DistractingBlow
return DistractingBlow.allocate(.SPELL_ABIL_ID)
endmethod
method onInitialize takes EventSpell eventSpell returns nothing
call eventSpell.addFlag(.SPELL_TYPE)
set eventSpell.castTime = 0.50
call super.onInitialize(eventSpell)
endmethod
method onEffect takes EventSpell eventSpell returns nothing
local EventSpell other = SpellController.getActiveSpell(eventSpell.targetUnit)
call SetUnitAnimation(eventSpell.caster, "attack")
if (other != 0) then
call other.terminate(eventSpell.caster, 0)
endif
call super.onEffect(eventSpell)
endmethod
private static method onInit takes nothing returns nothing
call SpellController.registerSpell(DistractingBlow.create())
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope MagesBane
struct MagesBane extends PrettySpell
static constant integer SPELL_ABIL_ID = 'A002'
static constant integer SPELL_TYPE = TYPE_PHYSICAL
static method create takes nothing returns MagesBane
return MagesBane.allocate(.SPELL_ABIL_ID)
endmethod
method onInitialize takes EventSpell eventSpell returns nothing
call eventSpell.addFlag(.SPELL_TYPE)
set eventSpell.castTime = 0.25
call super.onInitialize(eventSpell)
endmethod
method onEffect takes EventSpell eventSpell returns nothing
local EventSpell other = SpellController.getActiveSpell(eventSpell.targetUnit)
call SetUnitAnimation(eventSpell.caster, "attack")
call UnitDamageTarget(eventSpell.caster, eventSpell.targetUnit, 50, true, false, ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_METAL_HEAVY_BASH)
if (other != 0) then
call UnitDamageTarget(eventSpell.caster, eventSpell.targetUnit, 100, true, false, ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_METAL_HEAVY_BASH)
endif
call super.onEffect(eventSpell)
endmethod
private static method onInit takes nothing returns nothing
call SpellController.registerSpell(MagesBane.create())
endmethod
endstruct
endscope
//TESH.scrollpos=24
//TESH.alwaysfold=0
scope Daze
globals
private HandleTable table
endglobals
struct DazeListener extends SpellListener
static method create takes nothing returns DazeListener
return DazeListener.allocate(10)
endmethod
method onInitialize takes EventSpell eventSpell returns nothing
if (GetUnitAbilityLevel(eventSpell.caster, Daze.AURA_ABIL_ID) > 0) then
set eventSpell.castTime = eventSpell.castTime * 2
endif
endmethod
private static method onInit takes nothing returns nothing
call SpellController.registerListener(DazeListener.create())
endmethod
endstruct
struct Daze extends PrettySpell
static constant integer SPELL_ABIL_ID = 'A005'
static constant integer AURA_ABIL_ID = 'A006'
static constant integer SPELL_TYPE = TYPE_PHYSICAL
static method create takes nothing returns Daze
return Daze.allocate(.SPELL_ABIL_ID)
endmethod
method onInitialize takes EventSpell eventSpell returns nothing
call eventSpell.addFlag(.SPELL_TYPE)
set eventSpell.castTime = 0.75
call super.onInitialize(eventSpell)
endmethod
method onEffect takes EventSpell eventSpell returns nothing
local timer t = NewTimer()
call SetUnitAnimation(eventSpell.caster, "attack")
call UnitDamageTarget(eventSpell.caster, eventSpell.targetUnit, 50, true, false, ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_METAL_HEAVY_BASH)
call UnitAddAbility(eventSpell.targetUnit, .AURA_ABIL_ID)
call SetTimerData(t, UnitData.create(eventSpell.targetUnit))
call TimerStart(t, 5.00, false, function Daze.callback)
set table[eventSpell.targetUnit] = GetTimerData(t)
call super.onEffect(eventSpell)
set t = null
endmethod
private static method callback takes nothing returns nothing
local UnitData data = GetTimerData(GetExpiredTimer())
if (data == table[data.u]) then
call UnitRemoveAbility(data.u, .AURA_ABIL_ID)
call table.flush(data.u)
endif
call data.destroy()
endmethod
private static method onInit takes nothing returns nothing
call SpellController.registerSpell(Daze.create())
set table = HandleTable.create()
endmethod
endstruct
endscope