1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Find your way through the deepest dungeon in the 18th Mini Mapping Contest Poll.
    Dismiss Notice
  3. A brave new world lies beyond the seven seas. Join the 34th Modeling Contest today!
    Dismiss Notice
  4. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

Spell Engine v0.3.2.w3x
Variables
System
Spell Engine Doc
SpellType Doc
SpellController API
Spell API
SpellListener API
EventSpell API
SpellType API
Collection
TreeSet
Table
Spell Engine
Spell Type
Bitfield
Changelog
Demo
TimerUtils
Stuff
ARGB
Pretty Spells
Call Lightning
Death and Decay
Grant Life
Distracting Blow
Mages Bane
Daze

		
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