• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • ✅ The POLL for Hive's Texturing Contest #33 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!

Triggers

Jaina Hero Concept.w3x
Variables
Libraries
Utilities
Alloc
Table
Utilities
Indexer
TimerUtils
MouseUtils
WorldBounds
LineSegment
TimedHandles
SpellEffectPlugin
SpellEffectEvent
ArcingFloatingText
RegisterPlayerUnitEvent
Missiles
MissileEffect
MissileUtils
Missiles
Tenacity
Tenacity
TenacityUtils
Interface
GetMainSelectedUnit
Interface
NewBonus
NewBonus
NewBonusUtils
Crowd Control
CrowdControl
Damage Interface
DamageInterface
Evasion
CriticalStrike
SpellPower
LifeSteal
SpellVamp
DamageInterfaceUtils
Cooldown Reduction
CooldownReduction
CooldownReductionUtils
Spells
Water Elemental
WaterElemental
Crushing Wave
CrushingWave
Water Shield
WaterShield
Brilliance Aura
BrillianceAura
Water Orb
WaterOrb
Living Tide
LivingTide
Bad Weather
BadWeather
This Map Only
Initialization
Revive
Refresh
Spawn
Camera
OnDamage
Name Type is_array initial_value
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Alloc ~~ By Sevion ~~ Version 1.09 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is Alloc?
//         - Alloc implements an intuitive allocation method for array structs
//
//    =Pros=
//         - Efficient.
//         - Simple.
//         - Less overhead than regular structs.
//
//    =Cons=
//         - Must use array structs (hardly a con).
//         - Must manually call OnDestroy.
//         - Must use Delegates for inheritance.
//         - No default values for variables (use onInit instead).
//         - No array members (use another Alloc struct as a linked list or type declaration).
//
//    Methods:
//         - struct.allocate()
//         - struct.deallocate()
//
//           These methods are used just as they should be used in regular structs.
//
//    Modules:
//         - Alloc
//           Implements the most basic form of Alloc. Includes only create and destroy
//           methods.
//
//  Details:
//         - Less overhead than regular structs
//
//         - Use array structs when using Alloc. Put the implement at the top of the struct.
//
//         - Alloc operates almost exactly the same as default structs in debug mode with the exception of onDestroy.
//
//  How to import:
//         - Create a trigger named Alloc.
//         - Convert it to custom text and replace the whole trigger text with this.
//
//  Thanks:
//         - Nestharus for the method of allocation and suggestions on further merging.
//         - Bribe for suggestions like the static if and method names.
//         - PurgeandFire111 for some suggestions like the merging of Alloc and AllocX as well as OnDestroy stuff.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Alloc    
    module Alloc
        private static integer instanceCount = 0
        private thistype recycle
   
        static method allocate takes nothing returns thistype
            local thistype this
   
            if (thistype(0).recycle == 0) then
                debug if (instanceCount == JASS_MAX_ARRAY_SIZE) then
                    debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to allocate too many instances!")
                    debug return 0
                debug endif
                set instanceCount = instanceCount + 1
                set this = instanceCount
            else
                set this = thistype(0).recycle
                set thistype(0).recycle = thistype(0).recycle.recycle
            endif

            debug set this.recycle = -1
   
            return this
        endmethod
   
        method deallocate takes nothing returns nothing
            debug if (this.recycle != -1) then
                debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to deallocate an invalid instance at [" + I2S(this) + "]!")
                debug return
            debug endif

            set this.recycle = thistype(0).recycle
            set thistype(0).recycle = this
        endmethod
    endmodule
endlibrary
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
    
    One map, one hashtable. Welcome to NewTable 4.1.0.1

    This newest iteration of Table introduces the new HashTable struct.
    You can now instantiate HashTables which enables the use of large
    parent and large child keys, just like a standard hashtable. Previously,
    the user would have to instantiate a Table to do this on their own which -
    while doable - is something the user should not have to do if I can add it
    to this resource myself (especially if they are inexperienced).

    This library was originally called NewTable so it didn't conflict with
    the API of Table by Vexorian. However, the damage is done and it's too
    late to change the library name now. To help with damage control, I
    have provided an extension library called TableBC, which bridges all
    the functionality of Vexorian's Table except for 2-D string arrays &
    the ".flush(integer)" method. I use ".flush()" to flush a child hash-
    table, because I wanted the API in NewTable to reflect the API of real
    hashtables (I thought this would be more intuitive).

    API

    ------------
    struct Table
    | static method create takes nothing returns Table
    |     create a new Table
    |
    | method destroy takes nothing returns nothing
    |     destroy it
    |
    | method flush takes nothing returns nothing
    |     flush all stored values inside of it
    |
    | method remove takes integer key returns nothing
    |     remove the value at index "key"
    |
    | method operator []= takes integer key, $TYPE$ value returns nothing
    |     assign "value" to index "key"
    |
    | method operator [] takes integer key returns $TYPE$
    |     load the value at index "key"
    |
    | method has takes integer key returns boolean
    |     whether or not the key was assigned
    |
    ----------------
    struct TableArray
    | static method operator [] takes integer array_size returns TableArray
    |     create a new array of Tables of size "array_size"
    |
    | method destroy takes nothing returns nothing
    |     destroy it
    |
    | method flush takes nothing returns nothing
    |     flush and destroy it
    |
    | method operator size takes nothing returns integer
    |     returns the size of the TableArray
    |
    | method operator [] takes integer key returns Table
    |     returns a Table accessible exclusively to index "key"
*/
    
globals
    private integer less = 0    //Index generation for TableArrays (below 0).
    private integer more = 8190 //Index generation for Tables.
    //Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
    
    private hashtable ht = InitHashtable()
    private key sizeK
    private key listK
endglobals

private struct dex extends array
    static method operator size takes nothing returns Table
        return sizeK
    endmethod
    static method operator list takes nothing returns Table
        return listK
    endmethod
endstruct

private struct handles extends array
    method has takes integer key returns boolean
        return HaveSavedHandle(ht, this, key)
    endmethod
    method remove takes integer key returns nothing
        call RemoveSavedHandle(ht, this, key)
    endmethod
endstruct

private struct agents extends array
    method operator []= takes integer key, agent value returns nothing
        call SaveAgentHandle(ht, this, key, value)
    endmethod
endstruct

//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
    method operator [] takes integer key returns $TYPE$
        return Load$FUNC$(ht, this, key)
    endmethod
    method operator []= takes integer key, $TYPE$ value returns nothing
        call Save$FUNC$(ht, this, key, value)
    endmethod
    method has takes integer key returns boolean
        return HaveSaved$SUPER$(ht, this, key)
    endmethod
    method remove takes integer key returns nothing
        call RemoveSaved$SUPER$(ht, this, key)
    endmethod
endstruct
private module $TYPE$m
    method operator $TYPE$ takes nothing returns $TYPE$s
        return this
    endmethod
endmodule
//! endtextmacro
    
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
    method operator [] takes integer key returns $TYPE$
        return Load$FUNC$Handle(ht, this, key)
    endmethod
    method operator []= takes integer key, $TYPE$ value returns nothing
        call Save$FUNC$Handle(ht, this, key, value)
    endmethod
    method has takes integer key returns boolean
        return HaveSavedHandle(ht, this, key)
    endmethod
    method remove takes integer key returns nothing
        call RemoveSavedHandle(ht, this, key)
    endmethod
endstruct
private module $TYPE$m
    method operator $TYPE$ takes nothing returns $TYPE$s
        return this
    endmethod
endmodule
//! endtextmacro

//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")

//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")

struct Table extends array

    // Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
    implement realm
    implement integerm
    implement booleanm
    implement stringm
    implement playerm
    implement widgetm
    implement destructablem
    implement itemm
    implement unitm
    implement abilitym
    implement timerm
    implement triggerm
    implement triggerconditionm
    implement triggeractionm
    implement eventm
    implement forcem
    implement groupm
    implement locationm
    implement rectm
    implement boolexprm
    implement soundm
    implement effectm
    implement unitpoolm
    implement itempoolm
    implement questm
    implement questitemm
    implement defeatconditionm
    implement timerdialogm
    implement leaderboardm
    implement multiboardm
    implement multiboarditemm
    implement trackablem
    implement dialogm
    implement buttonm
    implement texttagm
    implement lightningm
    implement imagem
    implement ubersplatm
    implement regionm
    implement fogstatem
    implement fogmodifierm
    implement hashtablem

    method operator handle takes nothing returns handles
        return this
    endmethod

    method operator agent takes nothing returns agents
        return this
    endmethod

    //set this = tb[GetSpellAbilityId()]
    method operator [] takes integer key returns Table
        return LoadInteger(ht, this, key) //return this.integer[key]
    endmethod

    //set tb[389034] = 8192
    method operator []= takes integer key, Table tb returns nothing
        call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
    endmethod

    //set b = tb.has(2493223)
    method has takes integer key returns boolean
        return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
    endmethod

    //call tb.remove(294080)
    method remove takes integer key returns nothing
        call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
    endmethod

    //Remove all data from a Table instance
    method flush takes nothing returns nothing
        call FlushChildHashtable(ht, this)
    endmethod

    //local Table tb = Table.create()
    static method create takes nothing returns Table
        local Table this = dex.list[0]
        
        if this == 0 then
            set this = more + 1
            set more = this
        else
            set dex.list[0] = dex.list[this]
            call dex.list.remove(this) //Clear hashed memory
        endif
        
        debug set dex.list[this] = -1
        return this
    endmethod

    // Removes all data from a Table instance and recycles its index.
    //
    //     call tb.destroy()
    //
    method destroy takes nothing returns nothing
        debug if dex.list[this] != -1 then
            debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
            debug return
        debug endif

        call this.flush()

        set dex.list[this] = dex.list[0]
        set dex.list[0] = this
    endmethod

    //! runtextmacro optional TABLE_BC_METHODS()
endstruct
    
//! runtextmacro optional TABLE_BC_STRUCTS()
    
struct TableArray extends array
    
    //Returns a new TableArray to do your bidding. Simply use:
    //
    //    local TableArray ta = TableArray[array_size]
    //
    static method operator [] takes integer array_size returns TableArray
        local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
        local TableArray this = tb[0]         //The last-destroyed TableArray that had this array size
        
        debug if array_size <= 0 then
            debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
            debug return 0
        debug endif
        
        if this == 0 then
            set this = less - array_size
            set less = this
        else
            set tb[0] = tb[this]  //Set the last destroyed to the last-last destroyed
            call tb.remove(this)  //Clear hashed memory
        endif
        
        set dex.size[this] = array_size //This remembers the array size
        return this
    endmethod
    
    //Returns the size of the TableArray
    method operator size takes nothing returns integer
        return dex.size[this]
    endmethod
    
    //This magic method enables two-dimensional[array][syntax] for Tables,
    //similar to the two-dimensional utility provided by hashtables them-
    //selves.
    //
    //ta[integer a].unit[integer b] = unit u
    //ta[integer a][integer c] = integer d
    //
    //Inline-friendly when not running in debug mode
    //
    method operator [] takes integer key returns Table
        static if DEBUG_MODE then
            local integer i = this.size
            if i == 0 then
                call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
                return 0
            elseif key < 0 or key >= i then
                call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
                return 0
            endif
        endif
        return this + key
    endmethod
    
    //Destroys a TableArray without flushing it; I assume you call .flush()
    //if you want it flushed too. This is a public method so that you don't
    //have to loop through all TableArray indices to flush them if you don't
    //need to (ie. if you were flushing all child-keys as you used them).
    //
    method destroy takes nothing returns nothing
        local Table tb = dex.size[this.size]
        
        debug if this.size == 0 then
            debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
            debug return
        debug endif
        
        if tb == 0 then
            //Create a Table to index recycled instances with their array size
            set tb = Table.create()
            set dex.size[this.size] = tb
        endif
        
        call dex.size.remove(this) //Clear the array size from hash memory
        
        set tb[this] = tb[0]
        set tb[0] = this
    endmethod
    
    private static Table tempTable
    private static integer tempEnd
    
    //Avoids hitting the op limit
    private static method clean takes nothing returns nothing
        local Table tb = .tempTable
        local integer end = tb + 0x1000
        if end < .tempEnd then
            set .tempTable = end
            call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
        else
            set end = .tempEnd
        endif
        loop
            call tb.flush()
            set tb = tb + 1
            exitwhen tb == end
        endloop
    endmethod
    
    //Flushes the TableArray and also destroys it. Doesn't get any more
    //similar to the FlushParentHashtable native than this.
    //
    method flush takes nothing returns nothing
        debug if this.size == 0 then
            debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
            debug return
        debug endif
        set .tempTable = this
        set .tempEnd = this + this.size
        call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
        call this.destroy()
    endmethod
    
endstruct
    
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array

    //Enables myHash[parentKey][childKey] syntax.
    //Basically, it creates a Table in the place of the parent key if
    //it didn't already get created earlier.
    method operator [] takes integer index returns Table
        local Table t = Table(this)[index]
        if t == 0 then
            set t = Table.create()
            set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
        endif
        return t
    endmethod

    //You need to call this on each parent key that you used if you
    //intend to destroy the HashTable or simply no longer need that key.
    method remove takes integer index returns nothing
        local Table t = Table(this)[index]
        if t != 0 then
            call t.destroy()
            call Table(this).remove(index)
        endif
    endmethod

    //Added in version 4.1
    method has takes integer index returns boolean
        return Table(this).has(index)
    endmethod

    //HashTables are just fancy Table indices.
    method destroy takes nothing returns nothing
        call Table(this).destroy()
    endmethod

    //Like I said above...
    static method create takes nothing returns thistype
        return Table.create()
    endmethod

endstruct

endlibrary
library Utilities requires TimerUtils, Indexer, TimedHandles, RegisterPlayerUnitEvent
    /* --------------------------------------- Utilities v1.9 --------------------------------------- */
    // How to Import:
    // 1 - Copy this library into your map
    // 2 - Copy the dummy unit in object editor and match its raw code below
    // 3 - Copy the TimerUtils library into your map and follow its install instructions
    // 4 - Copy the Indexer library over to your map and follow its install instructions
    // 5 - Copy the TimedHandles library over to your map and follow its install instructions
    // 6 - Copy the RegisterPlayerUnitEvent library over to your map and follow its install instructions
    /* ---------------------------------------- By Chopinski ---------------------------------------- */
    
    /* ---------------------------------------------------------------------------------------------- */
    /*                                          Configuration                                         */
    /* ---------------------------------------------------------------------------------------------- */
    globals
        // The dummy caster unit id 
        public  constant integer DUMMY     = 'dumi'
        // Update period
        private constant real    PERIOD    = 0.031250000
        // location z
        private location         LOCZ      = Location(0,0)
        // One hashtable to rule them all
        private hashtable        table     = InitHashtable()
        // Closest Unit
        private unit             bj_closestUnitGroup
    endglobals

    /* ---------------------------------------------------------------------------------------------- */
    /*                                            JASS API                                            */
    /* ---------------------------------------------------------------------------------------------- */
    // Only one declaration per map required
    native UnitAlive takes unit id returns boolean

    // Returns the terrain Z value (Desync safe)
    function GetLocZ takes real x, real y returns real
        call MoveLocation(LOCZ, x, y)
        return GetLocationZ(LOCZ)
    endfunction
    
    // Similar to GetUnitX and GetUnitY but for Z axis
    function GetUnitZ takes unit u returns real
        return GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
    endfunction
    
    // Similar to SetUnitX and SetUnitY but for Z axis
    function SetUnitZ takes unit u, real z returns nothing
        call SetUnitFlyHeight(u, z - GetLocZ(GetUnitX(u), GetUnitY(u)), 0)
    endfunction

    // Anlge between 2D points
    function AngleBetweenCoordinates takes real x, real y, real x2, real y2 returns real
        return Atan2(y2 - y, x2 - x)
    endfunction

    // Similar to AddSpecialEffect but scales the effect and considers z and return it
    function AddSpecialEffectEx takes string model, real x, real y, real z, real scale returns effect
        set bj_lastCreatedEffect = AddSpecialEffect(model, x, y)

        if z != 0 then
            call BlzSetSpecialEffectZ(bj_lastCreatedEffect, z + GetLocZ(x, y))
        endif
        call BlzSetSpecialEffectScale(bj_lastCreatedEffect, scale)
        
        return bj_lastCreatedEffect
    endfunction

    // Returns a group of enemy units of the specified player within the specified AOE of x and y
    function GetEnemyUnitsInRange takes player enemyOf, real x, real y, real aoe, boolean structures, boolean magicImmune returns group
        local group g = CreateGroup()
        local group h = CreateGroup()
        local unit  w
        
        call GroupEnumUnitsInRange(h, x, y, aoe, null)
        loop
            set w = FirstOfGroup(h)
            exitwhen w == null
                if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and (structures or (not IsUnitType(w, UNIT_TYPE_STRUCTURE))) and (magicImmune or (not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE))) then
                    call GroupAddUnit(g, w)
                endif
            call GroupRemoveUnit(h, w)
        endloop
        call DestroyGroup(h)
    
        set h = null
        return g
    endfunction

    // Returns the closest unit in a unit group with center at x and y
    function GetClosestUnitGroup takes real x, real y, group g returns unit
        local unit    u
        local real    dx
        local real    dy
        local real    md = 100000
        local integer i  = 0
        local integer size = BlzGroupGetSize(g)
        
        set bj_closestUnitGroup = null
        loop
            exitwhen i == size
                set u = BlzGroupUnitAt(g, i)
                if UnitAlive(u) then
                    set dx = GetUnitX(u) - x
                    set dy = GetUnitY(u) - y
                    
                    if (dx*dx + dy*dy)/100000 < md then
                        set bj_closestUnitGroup = u
                        set md = (dx*dx + dy*dy)/100000
                    endif
                endif
            set i = i + 1
        endloop
        
        return bj_closestUnitGroup
    endfunction
    
    // Removes a destructable after a period of time
    function RemoveDestructableTimed takes destructable dest, real timeout returns nothing
        call TimedDestructable.create(dest, timeout)
    endfunction

    // Link an effect to a unit buff or ability
    function LinkEffectToBuff takes unit target, integer buffId, string model, string attach returns nothing
        call EffectLink.BuffLink(target, buffId, model, attach)
    endfunction

    // Link an effect to an unit item.
    function LinkEffectToItem takes unit target, item i, string model, string attach returns nothing
        call EffectLink.ItemLink(target, i, model, attach)
    endfunction

    // Pretty obvious.
    function R2I2S takes real r returns string
        return I2S(R2I(r))
    endfunction

    // Spams the specified effect model at a location with the given interval for the number of times count
    function SpamEffect takes string model, real x, real y, real z, real scale, real interval, integer count returns nothing
        call EffectSpam.spam(null, model, "", x, y, z, scale, interval, count)
    endfunction

    // Spams the specified effect model attached to a unit for the given interval for the number of times count
    function SpamEffectUnit takes unit target, string model, string attach, real interval, integer count returns nothing
        call EffectSpam.spam(target, model, attach, 0, 0, 0, 0, interval, count)
    endfunction   

    // Add the specified ability to the specified unit for the given duration. Use hide to show or not the ability button.
    function UnitAddAbilityTimed takes unit whichUnit, integer abilityId, real duration, integer level, boolean hide returns nothing
        call TimedAbility.add(whichUnit, abilityId, duration, level, hide)
    endfunction

    // Resets the specified unit ability cooldown
    function ResetUnitAbilityCooldown takes unit whichUnit, integer abilCode returns nothing
        call ResetCooldown.reset(whichUnit, abilCode)
    endfunction 

    // Returns the distance between 2 coordinates in Warcraft III units
    function DistanceBetweenCoordinates takes real x1, real y1, real x2, real y2 returns real
        local real dx = (x2 - x1)
        local real dy = (y2 - y1)
    
        return SquareRoot(dx*dx + dy*dy)
    endfunction

    // Makes the specified source damage an area respecting some basic unit filters
    function UnitDamageArea takes unit source, real x, real y, real aoe, real damage, attacktype atkType, damagetype dmgType, boolean structures, boolean magicImmune, boolean allies returns nothing
        local group h = CreateGroup()
        local player enemyOf = GetOwningPlayer(source)
        local unit w
        
        call GroupEnumUnitsInRange(h, x, y, aoe, null)
        call GroupRemoveUnit(h, source)
        loop
            set w = FirstOfGroup(h)
            exitwhen w == null
                if UnitAlive(w) and (allies or IsUnitEnemy(w, enemyOf)) and (structures or (not IsUnitType(w, UNIT_TYPE_STRUCTURE))) and (magicImmune or (not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE))) then
                    call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                endif
            call GroupRemoveUnit(h, w)
        endloop
        call DestroyGroup(h)
    
        set h = null
        set enemyOf = null
    endfunction

    // Makes the specified source damage a group. Creates a special effect if specified
    function UnitDamageGroup takes unit u, group g, real damage, attacktype atk_type, damagetype dmg_type, string sfx, string atch_point, boolean destroy returns group
        local unit    v
        local integer i = 0
        local integer t = BlzGroupGetSize(g)

        loop
            exitwhen i == t
                set v = BlzGroupUnitAt(g, i)
                call UnitDamageTarget(u, v, damage, true, false, atk_type, dmg_type, null)

                if sfx != "" and atch_point != "" then
                    call DestroyEffect(AddSpecialEffectTarget(sfx, v, atch_point))
                endif
            set i = i + 1
        endloop

        if destroy then
            call DestroyGroup(g)
        endif

        return g
    endfunction

    // Returns a random range given a max value
    function GetRandomRange takes real radius returns real
        local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)

        if r > 1 then
            return (2 - r)*radius
        endif

        return r*radius
    endfunction

    // Returns a random value in the x/y coordinates depending on the value of boolean x
    function GetRandomCoordInRange takes real center, real radius, boolean x returns real
        local real theta = 2*bj_PI*GetRandomReal(0, 1)
        local real r

        if x then
            set r = center + radius*Cos(theta)
        else
            set r = center + radius*Sin(theta)
        endif

        return r
    endfunction

    // Clones the items in the source unit inventory to the target unit
    function CloneItems takes unit source, unit target, boolean isIllusion returns nothing
        local integer i = 0
        local integer j
        local item k
        
        loop
            exitwhen i > bj_MAX_INVENTORY
                set k = UnitItemInSlot(source, i)
                set j = GetItemCharges(k)
                set k = CreateItem(GetItemTypeId(k), GetUnitX(target), GetUnitY(target))
                call SetItemCharges(k, j)
                call UnitAddItem(target, k)

                if isIllusion then
                    if GetItemTypeId(k) == 'ankh' then
                        call BlzItemRemoveAbility(k, 'AIrc')
                    endif

                    call BlzSetItemBooleanField(k, ITEM_BF_ACTIVELY_USED, false)
                endif
            set i = i + 1
        endloop
        
        set k = null
    endfunction

    // Add the mount for he unit mana pool
    function AddUnitMana takes unit whichUnit, real amount returns nothing
        call SetUnitState(whichUnit, UNIT_STATE_MANA, (GetUnitState(whichUnit, UNIT_STATE_MANA) + amount))
    endfunction

    // Add the specified amounts to a hero str/agi/int base amount
    function UnitAddStat takes unit whichUnit, integer strength, integer agility, integer intelligence returns nothing
        if strength != 0 then
            call SetHeroStr(whichUnit, GetHeroStr(whichUnit, false) + strength, true)
        endif
    
        if agility != 0 then
            call SetHeroAgi(whichUnit, GetHeroAgi(whichUnit, false) + agility, true)
        endif
    
        if intelligence != 0 then
            call SetHeroInt(whichUnit, GetHeroInt(whichUnit, false) + intelligence, true)
        endif
    endfunction

    // Returns the closest unit from the x and y coordinates in the map
    function GetClosestUnit takes real x, real y, boolexpr e returns unit
        local real  md = 100000
        local group g = CreateGroup()
        local unit  u
        local real  dx
        local real  dy

        set bj_closestUnitGroup = null
        
        call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, e)
        loop
            set u = FirstOfGroup(g)
            exitwhen u == null
                if UnitAlive(u) then
                    set dx = GetUnitX(u) - x
                    set dy = GetUnitY(u) - y
                    
                    if (dx*dx + dy*dy)/100000 < md then
                        set bj_closestUnitGroup = u
                        set md = (dx*dx + dy*dy)/100000
                    endif
                endif
            call GroupRemoveUnit(g, u)
        endloop
        call DestroyGroup(g)
        call DestroyBoolExpr(e)
        set g = null

        return bj_closestUnitGroup
    endfunction
    
    // Creates a chain lightning with the specified ligihtning effect with the amount of bounces
    function CreateChainLightning takes unit source, unit target, real damage, real aoe, real duration, real interval, integer bounceCount, attacktype attackType, damagetype damageType, string lightningType, string sfx, string attachPoint, boolean canRebounce returns nothing
        call ChainLightning.create(source, target, damage, aoe, duration, interval, bounceCount, attackType, damageType, lightningType, sfx, attachPoint, canRebounce)
    endfunction

    // Add the specified amount to the specified player gold amount
    function AddPlayerGold takes player whichPlayer, integer amount returns nothing
        call SetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_GOLD) + amount)
    endfunction

    // Creates a text tag in an unit position for a duration
    function CreateTextOnUnit takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
        local texttag tx = CreateTextTag()
        
        call SetTextTagText(tx, text, 0.015)
        call SetTextTagPosUnit(tx, whichUnit, 0)
        call SetTextTagColor(tx, red, green, blue, alpha)
        call SetTextTagLifespan(tx, duration)
        call SetTextTagVelocity(tx, 0.0, 0.0355)
        call SetTextTagPermanent(tx, false)
        
        set tx = null
    endfunction

    // Add health regeneration to the unit base value
    function UnitAddHealthRegen takes unit whichUnit, real regen returns nothing
        call BlzSetUnitRealField(whichUnit, UNIT_RF_HIT_POINTS_REGENERATION_RATE, BlzGetUnitRealField(whichUnit, UNIT_RF_HIT_POINTS_REGENERATION_RATE) + regen)
    endfunction

    // Retrieves a dummy from the pool. Facing angle in radians
    function DummyRetrieve takes player owner, real x, real y, real z, real face returns unit
        return DummyPool.retrieve(owner, x, y, z, face)
    endfunction

    // Recycles a dummy unit type, putting it back into the pool.
    function DummyRecycle takes unit dummy returns nothing
        call DummyPool.recycle(dummy)
    endfunction

    // Recycles a dummy with a delay.
    function DummyRecycleTimed takes unit dummy, real delay returns nothing
        call DummyPool.recycleTimed(dummy, delay)
    endfunction

    // Casts an ability in the target unit. Must have no casting time
    function CastAbilityTarget takes unit target, integer id, string order, integer level returns nothing
        local unit dummy = DummyRetrieve(GetOwningPlayer(target), 0, 0, 0, 0)
        
        call UnitAddAbility(dummy, id)
        call SetUnitAbilityLevel(dummy, id, level)
        call IssueTargetOrder(dummy, order, target)
        call UnitRemoveAbility(dummy, id)
        call DummyRecycle(dummy)

        set dummy = null
    endfunction

    // Returns a random unit within a group
    function GroupPickRandomUnitEx takes group g returns unit
        if BlzGroupGetSize(g) > 0 then
            return BlzGroupUnitAt(g, GetRandomInt(0, BlzGroupGetSize(g) - 1))
        else
            return null
        endif
    endfunction

    // Returns true if a unit is within a cone given a facing and fov angle in degrees (Less precise)
    function IsUnitInConeEx takes unit u, real x, real y, real face, real fov returns boolean
        return Acos(Cos((Atan2(GetUnitY(u) - y, GetUnitX(u) - x)) - face*bj_DEGTORAD)) < fov*bj_DEGTORAD/2
    endfunction

    // Returns true if a unit is within a cone given a facing, fov angle and a range in degrees (takes collision into consideration). Credits to AGD.
    function IsUnitInCone takes unit u, real x, real y, real range, real face, real fov returns boolean
        if IsUnitInRangeXY(u, x, y, range) then
            set x = GetUnitX(u) - x
            set y = GetUnitY(u) - y
            set range = x*x + y*y
    
            if range > 0. then
                set face = face*bj_DEGTORAD - Atan2(y, x)
                set fov = fov*bj_DEGTORAD/2 + Asin(BlzGetUnitCollisionSize(u)/SquareRoot(range))
    
                return RAbsBJ(face) <= fov or RAbsBJ(face - 2.00*bj_PI) <= fov
            endif
    
            return true
        endif
    
        return false
    endfunction

    // Makes the source unit damage enemy unit in a cone given a direction, foy and range
    function UnitDamageCone takes unit source, real x, real y, real face, real fov, real aoe, real damage, attacktype atkType, damagetype dmgType, boolean structures, boolean magicImmune, boolean allies returns nothing
        local group h = CreateGroup()
        local player enemyOf = GetOwningPlayer(source)
        local unit w
        
        call GroupEnumUnitsInRange(h, x, y, aoe, null)
        call GroupRemoveUnit(h, source)
        loop
            set w = FirstOfGroup(h)
            exitwhen w == null
                if (UnitAlive(w) and IsUnitInCone(w, x, y, aoe, face, fov)) then
                    if (allies or IsUnitEnemy(w, enemyOf)) and (structures or (not IsUnitType(w, UNIT_TYPE_STRUCTURE))) and (magicImmune or (not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE))) then
                        call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                    endif
                endif
            call GroupRemoveUnit(h, w)
        endloop
        call DestroyGroup(h)
    
        set h = null
        set enemyOf = null
    endfunction

    // Heals all allied units of specified player in an area
    function HealArea takes player alliesOf, real x, real y, real aoe, real amount, string fxpath, string attchPoint returns nothing
        local group g = CreateGroup()
        local unit v
        
        call GroupEnumUnitsInRange(g, x, y, aoe, null)
        loop
            set v = FirstOfGroup(g)
            exitwhen v == null
                if IsUnitAlly(v, alliesOf) and UnitAlive(v) and not IsUnitType(v, UNIT_TYPE_STRUCTURE) then
                    call SetWidgetLife(v, GetWidgetLife(v) + amount)
                    if fxpath != "" then
                        if attchPoint != "" then
                            call DestroyEffect(AddSpecialEffectTarget(fxpath, v, attchPoint))
                        else
                            call DestroyEffect(AddSpecialEffect(fxpath, GetUnitX(v), GetUnitY(v)))
                        endif
                    endif
                endif
            call GroupRemoveUnit(g, v)
        endloop
        call DestroyGroup(g)
    
        set g = null
    endfunction

    // Returns an ability real level field as a string. Usefull for toolltip manipulation.
    function AbilityRealField takes unit u, integer abilityId, abilityreallevelfield field, integer level, integer multiplier, boolean asInteger returns string
        if asInteger then
            return R2I2S(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, level)*multiplier)
        else
            return R2SW(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, level)*multiplier,1,1)
        endif
    endfunction

    // Fix for camera pan desync. credits do Daffa
    function SmartCameraPanBJModified takes player whichPlayer, location loc, real duration returns nothing 
        local real tx = GetLocationX(loc)
        local real ty = GetLocationY(loc)
        local real dx = tx - GetCameraTargetPositionX()
        local real dy = ty - GetCameraTargetPositionY()
        local real dist = SquareRoot(dx * dx + dy * dy)

        if (GetLocalPlayer() == whichPlayer) then
            if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
                call PanCameraToTimed(tx, ty, duration)
                // Far away = snap camera immediately to point
            elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
                call PanCameraToTimed(tx, ty, duration)
                // Moderately close = pan camera over duration
            else
                // User is close, don't move camera
            endif
        endif
    endfunction
    
    // Fix for camera pan desync. credits do Daffa
    function SmartCameraPanBJModifiedXY takes player whichPlayer, real x, real y, real duration returns nothing 
        local real dx = x - GetCameraTargetPositionX()
        local real dy = y - GetCameraTargetPositionY()
        local real dist = SquareRoot(dx * dx + dy * dy)

        if (GetLocalPlayer() == whichPlayer) then
            if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
                call PanCameraToTimed(x, y, duration)
                // Far away = snap camera immediately to point
            elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
                call PanCameraToTimed(x, y, duration)
                // Moderately close = pan camera over duration
            else
                // User is close, don't move camera
            endif
        endif
    endfunction

    // Start the cooldown for the source unit unit the new value
    function StartUnitAbilityCooldown takes unit source, integer abilCode, real cooldown returns nothing
        call AbilityCooldown.start(source, abilCode, cooldown)
    endfunction
    
    // Pauses or Unpauses a unit after a delay. If flag = true than the unit will be paused and unpaused after the duration. If flag = false than the unit will be unpaused and paused after the duration.
    function PauseUnitTimed takes unit u, real duration, boolean flag returns nothing
        call TimedPause.create(u, duration, flag)
    endfunction

    /* ---------------------------------------------------------------------------------------------- */
    /*                                             Systems                                            */
    /* ---------------------------------------------------------------------------------------------- */
    /* ----------------------------------- Reset Ability Cooldown ----------------------------------- */
    struct ResetCooldown
        timer timer
        unit unit
        integer ability

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call BlzEndUnitAbilityCooldown(unit, ability)
            call ReleaseTimer(timer)
            call deallocate()
            
            set unit = null
            set timer  = null
        endmethod

        static method reset takes unit u, integer id returns nothing
            local thistype this = thistype.allocate()

            set timer = NewTimerEx(this)
            set unit = u
            set ability = id

            call TimerStart(timer, 0.01, false, function thistype.onExpire)
        endmethod 
    endstruct

    /* ---------------------------------------- Timed Ability --------------------------------------- */
    struct TimedAbility
        static timer timer = CreateTimer()
        static integer key = -1
        static thistype array array

        unit unit
        integer ability
        real duration

        method remove takes integer i returns integer
            call UnitRemoveAbility(unit, ability)
            call RemoveSavedInteger(table, GetHandleId(unit), ability)

            set array[i] = array[key]
            set key = key - 1
            set unit = null

            if key == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > key
                    set this = array[i]

                    if duration <= 0 then
                        set i = remove(i)
                    endif
                    set duration = duration - 0.1
                set i = i + 1
            endloop
        endmethod


        static method add takes unit u, integer id, real duration, integer level, boolean hide returns nothing
            local thistype this = LoadInteger(table, GetHandleId(u), id)
            
            if this == 0 then
                set this = thistype.allocate()
                set unit = u
                set ability = id
                set key = key + 1
                set array[key] = this

                call SaveInteger(table, GetHandleId(unit), ability, this)

                if key == 0 then
                    call TimerStart(timer, 0.1, true, function thistype.onPeriod)
                endif
            endif

            if GetUnitAbilityLevel(unit, ability) != level then
                call UnitAddAbility(unit, ability)
                call SetUnitAbilityLevel(unit, ability, level)
                call UnitMakeAbilityPermanent(unit, true, ability)
                call BlzUnitHideAbility(unit, ability, hide)
            endif

            set .duration = duration
        endmethod
    endstruct

    /* ----------------------------------------- Effect Spam ---------------------------------------- */
    struct EffectSpam
        timer timer
        unit unit 
        integer i 
        string effect
        string point
        real scale
        real x
        real y
        real z

        private static method onPeriod takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            if i > 0 then
                if unit == null then
                    call DestroyEffect(AddSpecialEffectEx(effect, x, y, z, scale))
                else
                    call DestroyEffect(AddSpecialEffectTarget(effect, unit, point))
                endif
            else
                call ReleaseTimer(timer)
                call deallocate()
                set timer = null
                set unit = null
            endif
            set i = i - 1
        endmethod

        static method spam takes unit target, string model, string attach, real x, real y, real z, real scale, real interval, integer count returns nothing
            local thistype this = thistype.allocate()

            set timer = NewTimerEx(this)
            set unit = target
            set i = count
            set effect = model
            set .x = x
            set .y = y
            set .z = z
            set .scale = scale
            set point = attach

            call TimerStart(timer, interval, true, function thistype.onPeriod)
        endmethod
    endstruct

    /* --------------------------------------- Chain Lightning -------------------------------------- */
    struct ChainLightning
        timer      timer
        unit       unit
        unit       prev
        unit       self
        unit       next
        group      group
        group      damaged
        player     player
        real       damage
        real       range
        real       duration
        integer    bounces
        attacktype attacktype
        damagetype damagetype
        string     lightning
        string     effect
        string     attach
        boolean    rebounce

        private method destroy takes nothing returns nothing
            call DestroyGroup(group)
            call ReleaseTimer(timer)
            call DestroyGroup(damaged)

            set prev       = null
            set self       = null
            set next       = null 
            set unit       = null
            set group      = null
            set timer      = null
            set player     = null
            set damaged    = null
            set attacktype = null
            set damagetype = null

            call deallocate()
        endmethod

        private static method onPeriod takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            
            call DestroyGroup(group)
            if bounces > 0 then
                set group = GetEnemyUnitsInRange(player, GetUnitX(self), GetUnitY(self), range, false, false)
                call GroupRemoveUnit(group, self)
                
                if not rebounce then
                    call BlzGroupRemoveGroupFast(damaged, group)
                endif
                
                if BlzGroupGetSize(group) == 0 then
                    call destroy()
                else
                    set next = GetClosestUnitGroup(GetUnitX(self), GetUnitY(self), group)
                    
                    if next == prev and BlzGroupGetSize(group) > 1 then
                        call GroupRemoveUnit(group, prev)
                        set next = GetClosestUnitGroup(GetUnitX(self), GetUnitY(self), group)
                    endif
                    
                    if next != null then
                        call DestroyLightningTimed(AddLightningEx(lightning, true, GetUnitX(self), GetUnitY(self), GetUnitZ(self) + 60.0, GetUnitX(next), GetUnitY(next), GetUnitZ(next) + 60.0), duration)
                        call DestroyEffect(AddSpecialEffectTarget(effect, next, attach))
                        call GroupAddUnit(damaged, next)
                        call UnitDamageTarget(unit, next, damage, false, false, attacktype, damagetype, null)
                        call DestroyGroup(group)
                        set prev = self
                        set self = next
                        set next = null
                    else
                        call destroy()
                    endif
                endif
            else
                call destroy()
            endif
            set bounces = bounces - 1
        endmethod

        static method create takes unit source, unit target, real dmg, real aoe, real dur, real interval, integer bounceCount, attacktype attackType, damagetype damageType, string lightningType, string sfx, string attachPoint, boolean canRebounce returns thistype
            local group    g
            local thistype this

            set g = GetEnemyUnitsInRange(GetOwningPlayer(source), GetUnitX(target), GetUnitY(target), aoe, false, false)

            if BlzGroupGetSize(g) == 1 then
                call DestroyLightningTimed(AddLightningEx(lightningType, true, GetUnitX(source), GetUnitY(source), BlzGetUnitZ(source) + 60.0, GetUnitX(target), GetUnitY(target), BlzGetUnitZ(target) + 60.0), dur)
                call DestroyEffect(AddSpecialEffectTarget(sfx, target, attachPoint))
                call UnitDamageTarget(source, target, dmg, false, false, attackType, damageType, null)
            else
                set this       = thistype.allocate()
                set timer      = NewTimerEx(this)
                set prev       = null
                set self       = target
                set next       = null
                set unit       = source
                set player     = GetOwningPlayer(source)
                set damage     = dmg
                set range      = aoe
                set duration   = dur
                set bounces    = bounceCount
                set attacktype = attackType
                set damagetype = damageType
                set lightning  = lightningType
                set effect     = sfx
                set attach     = attachPoint
                set rebounce   = canRebounce
                set damaged    = CreateGroup()

                call GroupRemoveUnit(g, target)
                call GroupAddUnit(damaged, target)
                call DestroyEffect(AddSpecialEffectTarget(sfx, target, attachPoint))
                call UnitDamageTarget(source, target, damage, false, false, attacktype, damagetype, null)
                call TimerStart(timer, interval, true, function thistype.onPeriod)
            endif
            call DestroyGroup(g)
            set g = null

            return this
        endmethod
    endstruct

    /* ----------------------------------------- Dummy Pool ----------------------------------------- */
    struct DummyPool
        private static player player = Player(PLAYER_NEUTRAL_PASSIVE)
        private static group  group  = CreateGroup()

        timer timer
        unit  unit

        static method recycle takes unit dummy returns nothing
            if GetUnitTypeId(dummy) != DUMMY then
                debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
            else
                call GroupAddUnit(group, dummy)
                call SetUnitX(dummy, WorldBounds.maxX)
                call SetUnitY(dummy, WorldBounds.maxY)
                call SetUnitOwner(dummy, player, false)
                call ShowUnit(dummy, false)
                call BlzPauseUnitEx(dummy, true)
            endif
        endmethod

        static method retrieve takes player owner, real x, real y, real z, real face returns unit
            if BlzGroupGetSize(group) > 0 then
                set bj_lastCreatedUnit = FirstOfGroup(group)
                call BlzPauseUnitEx(bj_lastCreatedUnit, false)
                call ShowUnit(bj_lastCreatedUnit, true)
                call GroupRemoveUnit(group, bj_lastCreatedUnit)
                call SetUnitX(bj_lastCreatedUnit, x)
                call SetUnitY(bj_lastCreatedUnit, y)
                call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
                call BlzSetUnitFacingEx(bj_lastCreatedUnit, face*bj_RADTODEG)
                call SetUnitOwner(bj_lastCreatedUnit, owner, false)
            else
                set bj_lastCreatedUnit = CreateUnit(owner, DUMMY, x, y, face*bj_RADTODEG)
                call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
            endif

            return bj_lastCreatedUnit
        endmethod

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call recycle(unit)
            call ReleaseTimer(timer)
            
            set timer = null
            set unit  = null

            call deallocate()
        endmethod

        static method recycleTimed takes unit dummy, real delay returns nothing
            local thistype this

            if GetUnitTypeId(dummy) != DUMMY then
                debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
            else
                set this = thistype.allocate()

                set timer = NewTimerEx(this)
                set unit  = dummy
                
                call TimerStart(timer, delay, false, function thistype.onExpire)
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            local integer i = 0
            local unit    u

            loop
                exitwhen i == 20
                    set u = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
                    call BlzPauseUnitEx(u, false)
                    call GroupAddUnit(group, u)
                set i = i + 1
            endloop

            set u = null
        endmethod
    endstruct

    /* ----------------------------------------- Effect Link ---------------------------------------- */
    struct EffectLink
        static timer timer = CreateTimer()
        //Dynamic Indexing for buff and timed
        static integer didx = -1
        static thistype array data
        //Dynamic Indexing for items
        static integer ditem = -1
        static thistype array items

        unit    unit
        effect  effect
        item    item
        integer buff

        method remove takes integer i, boolean isItem returns integer
            call DestroyEffect(effect)

            if isItem then
                set  items[i] = items[ditem]
                set  ditem    = ditem - 1
            else
                set  data[i] = data[didx]
                set  didx    = didx - 1

                if didx == -1 then
                    call PauseTimer(timer)
                endif
            endif

            set unit   = null
            set item   = null
            set effect = null

            call deallocate()

            return i - 1
        endmethod

        static method onDrop takes nothing returns nothing
            local item     j = GetManipulatedItem()
            local integer  i = 0
            local thistype this

            loop
                exitwhen i > ditem
                    set this = items[i]

                    if item == j then
                        set i = remove(i, true)
                    endif
                set i = i + 1
            endloop

            set j = null
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > didx
                    set this = data[i]

                    if GetUnitAbilityLevel(unit, buff) == 0 then
                        set i = remove(i, false)
                    endif
                set i = i + 1
            endloop
        endmethod

        static method BuffLink takes unit target, integer id, string model, string attach returns nothing
            local thistype this = thistype.allocate()

            set unit       = target
            set buff       = id
            set effect     = AddSpecialEffectTarget(model, target, attach)
            set didx       = didx + 1
            set data[didx] = this
            
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod

        static method ItemLink takes unit target, item i, string model, string attach returns nothing
            local thistype this = thistype.allocate()

            set item         = i
            set effect       = AddSpecialEffectTarget(model, target, attach)
            set ditem        = ditem + 1
            set items[ditem] = this
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
        endmethod
    endstruct

    /* ----------------------------------- Start Ability Cooldown ----------------------------------- */
    struct AbilityCooldown
        timer   timer
        unit    unit
        integer ability
        real    newCd

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call BlzStartUnitAbilityCooldown(unit, ability, newCd)
            call ReleaseTimer(timer)
            call deallocate()

            set timer = null
            set unit  = null
        endmethod

        static method start takes unit source, integer abilCode, real cooldown returns nothing
            local thistype this = thistype.allocate()

            set timer   = NewTimerEx(this)
            set unit    = source
            set ability = abilCode
            set newCd   = cooldown

            call TimerStart(timer, 0.01, false, function thistype.onExpire)
        endmethod
    endstruct

    /* ------------------------------------- Destructable Timed ------------------------------------- */
    struct TimedDestructable
        private static constant real    period = 0.03125000
        private static timer            timer  = CreateTimer()
        private static integer          id    = -1
        private static thistype array   array

        destructable destructable
        real duration

        private method remove takes integer i returns integer
            call RemoveDestructable(destructable)

            set destructable = null
            set array[i] = array[id]
            set id = id - 1

            if id == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this

            loop
                exitwhen i > id
                    set this = array[i]

                    if duration <= 0 then
                        set i = remove(i)
                    endif
                    set duration = duration - period
                set i = i + 1
            endloop
        endmethod

        static method create takes destructable dest, real timeout returns thistype
            local thistype this = thistype.allocate()

            set destructable = dest
            set duration     = timeout
            set id           = id + 1
            set array[id]    = this

            if id == 0 then
                call TimerStart(timer, period, true, function thistype.onPeriod)
            endif

            return this
        endmethod
    endstruct

    /* ----------------------------------------- Timed Pause ---------------------------------------- */
    struct TimedPause
        static integer array array

        timer timer
        unit unit
        integer key
        boolean flag

        static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            set array[key] = array[key] - 1
            if array[key] == 0 then
                call BlzPauseUnitEx(unit, not flag)
            endif
            call ReleaseTimer(timer)
            call deallocate()
            
            set timer = null
            set unit = null
        endmethod


        static method create takes unit u, real duration, boolean pause returns thistype
            local thistype this = thistype.allocate()

            set timer = NewTimerEx(this)
            set unit = u
            set flag = pause
            set key = GetUnitUserData(u)
            
            if array[key] == 0 then
                call BlzPauseUnitEx(u, pause)
            endif
            set array[key] = array[key] + 1
            
            call TimerStart(timer, duration, false, function thistype.onExpire)
            
            return this
        endmethod
    endstruct
endlibrary
library Indexer
    /* ------------------------ Indexer v1.1 by Chopinski ----------------------- */
    // Simple unit indexer for version 1.31+
    // Simply copy and paste to import
    /* ----------------------------------- END ---------------------------------- */
    
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct Indexer
        private static integer ability = 'Adef'
        private static integer array array
        private static integer key = -1
        private static integer id = -1
        readonly static unit unit
        readonly static trigger onIndex = CreateTrigger()
        readonly static trigger onDeindex = CreateTrigger()
        
        private static method index takes unit source returns nothing
            if GetUnitUserData(source) == 0 then
                set unit = source
                
                if key > - 1 then
                    set id = array[key]
                    set array[key] = 0
                    set key = key - 1
                else
                    set id = id + 1
                endif
                
                if GetUnitAbilityLevel(unit, ability) == 0 then
                    call UnitAddAbility(unit, ability)
                    call UnitMakeAbilityPermanent(unit, true, ability)
                    call BlzUnitDisableAbility(unit, ability, true, true)
                endif
                call SetUnitUserData(unit, id)
                call TriggerEvaluate(onIndex)
                
                set unit = null
            endif
        endmethod
    
        private static method onOrder takes nothing returns nothing
            if GetIssuedOrderId() == 852056 then
                if GetUnitAbilityLevel(GetTriggerUnit(), ability) == 0 then
                    set unit = GetTriggerUnit()
                    call TriggerEvaluate(onDeindex)
                    set key = key + 1
                    set array[key] = GetUnitUserData(unit)
                    set unit = null
                endif
            endif
        endmethod
    
        private static method onEnter takes nothing returns nothing
            call index(GetFilterUnit())
        endmethod
    
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local region r = CreateRegion()
            local rect map = GetWorldBounds()
            local integer i = 0
            
            call RegionAddRect(r, map)
            call RemoveRect(map)
            call TriggerRegisterEnterRegion(CreateTrigger(), r, Filter(function thistype.onEnter))
            loop
                exitwhen i == bj_MAX_PLAYER_SLOTS
                    call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), null)
                    loop
                        set unit = FirstOfGroup(bj_lastCreatedGroup)
                        exitwhen unit == null
                            call index(unit)
                        call GroupRemoveUnit(bj_lastCreatedGroup, unit)
                    endloop
                    call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
                set i = i + 1
            endloop
            call TriggerAddCondition(t, Filter(function thistype.onOrder))
            
            set r = null
            set map = null
        endmethod
    endstruct
    
    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function RegisterUnitIndexEvent takes code c returns nothing
        call TriggerAddCondition(Indexer.onIndex, Filter(c))
    endfunction
    
    function RegisterUnitDeindexEvent takes code c returns nothing
        call TriggerAddCondition(Indexer.onDeindex, Filter(c))
    endfunction
    
    function GetIndexUnit takes nothing returns unit
        return Indexer.unit
    endfunction
endlibrary
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//*  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.wc3c.net
//*
//* For your timer needs:
//*  * Attaching
//*  * Recycling (with double-free protection)
//*
//* set t=NewTimer()      : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x)   : Get a timer (alternative to CreateTimer), call
//*                            Initialize timer data as x, instead of 0.
//*
//* 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.
//*
//* Multi-flavor:
//*    Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************

//================================================================
    globals
        //How to tweak timer utils:
        // USE_HASH_TABLE = true  (new blue)
        //  * SAFEST
        //  * SLOWEST (though hash tables are kind of fast)
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true  (orange)
        //  * kinda safe (except there is a limit in the number of timers)
        //  * ALMOST FAST
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
        //  * THE FASTEST (though is only  faster than the previous method
        //                  after using the optimizer on the map)
        //  * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
        //                     work)
        //
        private constant boolean USE_HASH_TABLE      = true
        private constant boolean USE_FLEXIBLE_OFFSET = false

        private constant integer OFFSET     = 0x100000
        private          integer VOFFSET    = OFFSET
              
        //Timers to preload at map init:
        private constant integer QUANTITY   = 256
        
        //Changing this  to something big will allow you to keep recycling
        // timers even when there are already AN INCREDIBLE AMOUNT of timers in
        // the stack. But it will make things far slower so that's probably a bad idea...
        private constant integer ARRAY_SIZE = 8190

    endglobals

    //==================================================================================================
    globals
        private integer array data[ARRAY_SIZE]
        private hashtable     ht
    endglobals
    
    

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetTimerData takes timer t, integer value returns nothing
        static if(USE_HASH_TABLE) then
            // new blue
            call SaveInteger(ht,0,GetHandleId(t), value)
            
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-VOFFSET]=value
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-OFFSET]=value
        endif        
    endfunction

    function GetTimerData takes timer t returns integer
        static if(USE_HASH_TABLE) then
            // new blue
            return LoadInteger(ht,0,GetHandleId(t) )
            
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-VOFFSET]
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-OFFSET]
        endif        
    endfunction

    //==========================================================================================
    globals
        private timer array tT[ARRAY_SIZE]
        private integer tN = 0
        private constant integer HELD=0x28829022
        //use a totally random number here, the more improbable someone uses it, the better.
        
        private boolean       didinit = false
    endglobals
    private keyword init

    //==========================================================================================
    // I needed to decide between duplicating code ignoring the "Once and only once" rule
    // and using the ugly textmacros. I guess textmacros won.
    //
    //! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
    // On second thought, no.
    //! endtextmacro

    function NewTimerEx takes integer value returns timer
        if (tN==0) then
            if (not didinit) then 
                //This extra if shouldn't represent a major performance drawback
                //because QUANTITY rule is not supposed to be broken every day. 
                call init.evaluate()
                set tN = tN - 1
            else
                //If this happens then the QUANTITY rule has already been broken, try to fix the
                // issue, else fail.
                debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
                set tT[0]=CreateTimer()
                static if( not USE_HASH_TABLE) then
                    debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
                    static if( USE_FLEXIBLE_OFFSET) then
                        if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
                            //all right, couldn't fix it
                            call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                            return null
                        endif
                    else
                        if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
                            //all right, couldn't fix it
                            call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                            return null
                        endif
                    endif
                endif
            endif
        else
            set tN=tN-1
        endif
        call SetTimerData(tT[tN],value)
     return tT[tN]
    endfunction
    
    function NewTimer takes nothing returns timer
        return NewTimerEx(0)
    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==ARRAY_SIZE) 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
     local integer i=0
     local integer o=-1
     local boolean oops = false
        if ( didinit ) then
            return
        else
            set didinit = true
        endif
     
        static if( USE_HASH_TABLE ) then
            set ht = InitHashtable()
            loop
                exitwhen(i==QUANTITY)
                set tT[i]=CreateTimer()
                call SetTimerData(tT[i], HELD)
                set i=i+1
            endloop
            set tN = QUANTITY
        else
            loop
                set i=0
                loop
                    exitwhen (i==QUANTITY)
                    set tT[i] = CreateTimer()
                    if(i==0) then
                        set VOFFSET = GetHandleId(tT[i])
                        static if(USE_FLEXIBLE_OFFSET) then
                            set o=VOFFSET
                        else
                            set o=OFFSET
                        endif
                    endif
                    if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
                        exitwhen true
                    endif
                    if (GetHandleId(tT[i])-o>=0)  then
                        set i=i+1
                    endif
                endloop
                set tN = i
                exitwhen(tN == QUANTITY)
                set oops = true
                exitwhen not USE_FLEXIBLE_OFFSET
                debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")               
            endloop
            
            if(oops) then
                static if ( USE_FLEXIBLE_OFFSET) then
                    debug call BJDebugMsg("The problem has been fixed.")
                    //If this message doesn't appear then there is so much
                    //handle id fragmentation that it was impossible to preload
                    //so many timers and the thread crashed! Therefore this
                    //debug message is useful.
                elseif(DEBUG_MODE) then
                    call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
                    call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
                    call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
                    call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
                endif
            endif
        endif

    endfunction

endlibrary
library MouseUtils
/*
    -------------------
        MouseUtils
         - MyPad
         
         1.0.2.2
    -------------------
   
    ----------------------------------------------------------------------------
        A simple snippet that allows one to
        conveniently use the mouse natives
        as they were meant to be...
       
     -------------------
    |    API            |
     -------------------
   
        struct UserMouse extends array
            static method operator [] (player p) -> thistype
                - Returns the player's id + 1
               
            static method getCurEventType() -> integer
                - Returns the custom event that got executed.
               
            method operator player -> player
                - Returns Player(this - 1)
               
            readonly real mouseX
            readonly real mouseY
                - Returns the current mouse coordinates.
               
            readonly method operator isMouseClicked -> boolean
                - Determines whether any mouse key has been clicked,
                  and will return true on the first mouse key.
                 
            method isMouseButtonClicked(mousebuttontype mouseButton)
                - Returns true if the mouse button hasn't been
                  released yet.
            method setMousePos(real x, y) (introduced in 1.0.2.2)
                - Sets the mouse position for a given player.
                 
            static method registerCode(code c, integer ev) -> triggercondition
                - Lets code run upon the execution of a certain event.
                - Returns a triggercondition that can be removed later.
               
            static method unregisterCallback(triggercondition trgHndl, integer ev)
                - Removes a generated triggercondition from the trigger.
               
        functions:
            GetPlayerMouseX(player p) -> real
            GetPlayerMouseY(player p) -> real
                - Returns the coordinates of the mouse of the player.
               
            OnMouseEvent(code func, integer eventId) -> triggercondition
                - See UserMouse.registerCode
               
            GetMouseEventType() -> integer
                - See UserMouse.getCurEventType
               
            UnregisterMouseCallback(triggercondition t, integer eventId)
                - See UserMouse.unregisterCallback
            SetUserMousePos(player p, real x, real y)
                - See UserMouse.setMousePos
   
  Unique Global Constants:
   IMPL_LOCK (Introduced in v.1.0.2.2)
    - Enables or disables the lock option
     -------------------
    |    Credits        |
     -------------------
     
        -   Pyrogasm for pointing out a comparison logic flaw
            in operator isMouseClicked.
           
        -   Illidan(Evil)X for the useful enum handles that
            grant more functionality to this snippet.
       
        -   TriggerHappy for the suggestion to include
            associated events and callbacks to this snippet.
           
        -   Quilnez for pointing out a bug related to the
            method isMouseButtonClicked not working as intended
            in certain situations.
           
    ----------------------------------------------------------------------------
*/
//  Arbitrary constants
globals
    constant integer EVENT_MOUSE_UP     = 1024
    constant integer EVENT_MOUSE_DOWN   = 2048
 constant integer EVENT_MOUSE_MOVE   = 3072
 
 private constant boolean IMPL_LOCK = false
endglobals
private module Init
    private static method onInit takes nothing returns nothing
        call thistype.init()
    endmethod
endmodule
struct UserMouse extends array
 static if IMPL_LOCK then
  //  Determines the minimum interval that a mouse move event detector
  //  will be deactivated. (Globally-based)
  //  You can configure it to any amount you like.
  private static constant real INTERVAL           = 0.031250000
 
  //  Determines how many times a mouse move event detector can fire
  //  before being deactivated. (locally-based)
  //  You can configure this to any integer value. (Preferably positive)
  private static constant integer MOUSE_COUNT_MAX  = 16
 
  // Determines the amount to be deducted from mouseEventCount
  // per INTERVAL. Runs independently of resetTimer
  private static constant integer MOUSE_COUNT_LOSS = 8
  private static constant boolean IS_INSTANT   = INTERVAL <= 0.
 endif
    private static integer currentEventType         = 0
    private static integer updateCount               = 0
    private static trigger stateDetector             = null
 static if IMPL_LOCK and not IS_INSTANT then
  private static timer resetTimer                 = null
 endif
    private static trigger array evTrigger
   
    private static integer array mouseButtonStack
 
 static if IMPL_LOCK and not IS_INSTANT then
  private integer  mouseEventCount
  private timer mouseEventReductor
 endif
    private thistype next
    private thistype prev
   
    private thistype resetNext
    private thistype resetPrev
    private trigger posDetector
 private integer mouseClickCount
   
    readonly real mouseX
    readonly real mouseY
   
    //  Converts the enum type mousebuttontype into an integer
    private static method toIndex takes mousebuttontype mouseButton returns integer
        return GetHandleId(mouseButton)
    endmethod
   
    static method getCurEventType takes nothing returns integer
        return currentEventType
    endmethod
   
    static method operator [] takes player p returns thistype
        if thistype(GetPlayerId(p) + 1).posDetector != null then
            return GetPlayerId(p) + 1
        endif
        return 0
    endmethod
       
    method operator player takes nothing returns player
        return Player(this - 1)
    endmethod
    method operator isMouseClicked takes nothing returns boolean
        return .mouseClickCount > 0
    endmethod
    method isMouseButtonClicked takes mousebuttontype mouseButton returns boolean
        return UserMouse.mouseButtonStack[(this - 1)*3 + UserMouse.toIndex(mouseButton)] > 0
 endmethod
    method setMousePos takes integer x, integer y returns nothing
        if GetLocalPlayer() == this.player then
            call BlzSetMousePos(x, y)
        endif
    endmethod
 static if IMPL_LOCK then
 private static method getMouseEventReductor takes timer t returns thistype
  local thistype this = thistype(0).next
  loop
   exitwhen this.mouseEventReductor == t or this == 0
   set this = this.next
  endloop
  return this
 endmethod
    private static method onMouseUpdateListener takes nothing returns nothing
        local thistype this = thistype(0).resetNext
        set updateCount     = 0
       
        loop
            exitwhen this == 0
            set updateCount = updateCount + 1
                       
            set this.mouseEventCount = 0
            call EnableTrigger(this.posDetector)
           
            set this.resetNext.resetPrev = this.resetPrev
            set this.resetPrev.resetNext = this.resetNext
           
            set this                     = this.resetNext
  endloop
 
  if updateCount > 0 then
   static if not IS_INSTANT then
    call TimerStart(resetTimer, INTERVAL, false, function thistype.onMouseUpdateListener)
   else
    call onMouseUpdateListener()
   endif
  else
   static if not IS_INSTANT then
    call TimerStart(resetTimer, 0.00, false, null)
    call PauseTimer(resetTimer)
   endif
        endif
 endmethod
 
 private static method onMouseReductListener takes nothing returns nothing
  local thistype this  = getMouseEventReductor(GetExpiredTimer())
  if this.mouseEventCount <= 0 then
   call PauseTimer(this.mouseEventReductor)
  else
   set this.mouseEventCount = IMaxBJ(this.mouseEventCount - MOUSE_COUNT_LOSS, 0)
   call TimerStart(this.mouseEventReductor, INTERVAL, false, function thistype.onMouseReductListener)
  endif
 endmethod
 endif
    private static method onMouseUpOrDown takes nothing returns nothing
        local thistype this = thistype[GetTriggerPlayer()]
        local integer index = (this - 1)*3 + UserMouse.toIndex(BlzGetTriggerPlayerMouseButton())
        local boolean releaseFlag   = false
       
        if GetTriggerEventId() == EVENT_PLAYER_MOUSE_DOWN then
            set this.mouseClickCount    = IMinBJ(this.mouseClickCount + 1, 3)
            set releaseFlag          = UserMouse.mouseButtonStack[index] <= 0
            set UserMouse.mouseButtonStack[index]  = IMinBJ(UserMouse.mouseButtonStack[index] + 1, 1)
           
            if releaseFlag then
                set currentEventType = EVENT_MOUSE_DOWN
                call TriggerEvaluate(evTrigger[EVENT_MOUSE_DOWN])
            endif
        else
            set this.mouseClickCount = IMaxBJ(this.mouseClickCount - 1, 0)
            set releaseFlag          = UserMouse.mouseButtonStack[index] > 0
            set UserMouse.mouseButtonStack[index]  = IMaxBJ(UserMouse.mouseButtonStack[index] - 1, 0)
           
            if releaseFlag then
                set currentEventType = EVENT_MOUSE_UP
                call TriggerEvaluate(evTrigger[EVENT_MOUSE_UP])
            endif
        endif
    endmethod
   
    private static method onMouseMove takes nothing returns nothing
  local thistype this   = thistype[GetTriggerPlayer()]
  local boolean started  = false
               
        set this.mouseX      = BlzGetTriggerPlayerMouseX()
        set this.mouseY      = BlzGetTriggerPlayerMouseY()
 
  static if IMPL_LOCK then
   set this.mouseEventCount  = this.mouseEventCount + 1
   if this.mouseEventCount <= 1 then
    call TimerStart(this.mouseEventReductor, INTERVAL, false, function thistype.onMouseReductListener)
   endif
  endif
  set currentEventType   = EVENT_MOUSE_MOVE
  call TriggerEvaluate(evTrigger[EVENT_MOUSE_MOVE])  
  static if IMPL_LOCK then
   if this.mouseEventCount >= thistype.MOUSE_COUNT_MAX then
    call DisableTrigger(this.posDetector)                  
   
    if thistype(0).resetNext == 0 then
     static if not IS_INSTANT then
      call TimerStart(resetTimer, INTERVAL, false, function thistype.onMouseUpdateListener)
      // Mouse event reductor should be paused
     else
      set started  = true
     endif
     call PauseTimer(this.mouseEventReductor)
    endif
   
    set this.resetNext              = 0
    set this.resetPrev              = this.resetNext.resetPrev
    set this.resetPrev.resetNext    = this
    set this.resetNext.resetPrev    = this  
     
    if started then
     call onMouseUpdateListener()
    endif
   endif
  endif
    endmethod
       
    private static method init takes nothing returns nothing
        local thistype this = 1
        local player p      = this.player
 
  static if IMPL_LOCK and not IS_INSTANT then
   set resetTimer  = CreateTimer()
  endif
        set stateDetector   = CreateTrigger()
       
        set evTrigger[EVENT_MOUSE_UP]   = CreateTrigger()
        set evTrigger[EVENT_MOUSE_DOWN] = CreateTrigger()
        set evTrigger[EVENT_MOUSE_MOVE] = CreateTrigger()
       
        call TriggerAddCondition( stateDetector, Condition(function thistype.onMouseUpOrDown))
        loop
            exitwhen integer(this) > bj_MAX_PLAYER_SLOTS
           
            if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                set this.next             = 0
                set this.prev             = thistype(0).prev
                set thistype(0).prev.next = this
                set thistype(0).prev      = this
               
                set this.posDetector         = CreateTrigger()
                static if IMPL_LOCK and not IS_INSTANT then
                    set this.mouseEventReductor  = CreateTimer()
                endif
                call TriggerRegisterPlayerEvent( this.posDetector, p, EVENT_PLAYER_MOUSE_MOVE )
                call TriggerAddCondition( this.posDetector, Condition(function thistype.onMouseMove))                
               
                call TriggerRegisterPlayerEvent( stateDetector, p, EVENT_PLAYER_MOUSE_UP )
                call TriggerRegisterPlayerEvent( stateDetector, p, EVENT_PLAYER_MOUSE_DOWN )
            endif
           
            set this = this + 1
            set p    = this.player
        endloop
    endmethod
   
    static method registerCode takes code handlerFunc, integer eventId returns triggercondition
        return TriggerAddCondition(evTrigger[eventId], Condition(handlerFunc))
    endmethod
   
    static method unregisterCallback takes triggercondition whichHandler, integer eventId returns nothing
        call TriggerRemoveCondition(evTrigger[eventId], whichHandler)
    endmethod
   
    implement Init
endstruct
function GetPlayerMouseX takes player p returns real
    return UserMouse[p].mouseX
endfunction
function GetPlayerMouseY takes player p returns real
    return UserMouse[p].mouseY
endfunction
function OnMouseEvent takes code func, integer eventId returns triggercondition
    return UserMouse.registerCode(func, eventId)
endfunction
function GetMouseEventType takes nothing returns integer
    return UserMouse.getCurEventType()
endfunction
function UnregisterMouseCallback takes triggercondition whichHandler, integer eventId returns nothing
    call UserMouse.unregisterCallback(whichHandler, eventId)
endfunction
function SetUserMousePos takes player p, integer x, integer y returns nothing
    call UserMouse[p].setMousePos(x, y)
endfunction
endlibrary
library WorldBounds /* v2.0.0.0
************************************************************************************
*
*	struct WorldBounds extends array
*
*	Fields
*	-------------------------
*
*	readonly static integer maxX
*	readonly static integer maxY
*	readonly static integer minX
*	readonly static integer minY
*
*	readonly static integer centerX
*	readonly static integer centerY
*
*	readonly static rect world
*	readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit

    private static method onInit takes nothing returns nothing
        set world = GetWorldBounds()
        set maxX = R2I(GetRectMaxX(world))
        set maxY = R2I(GetRectMaxY(world))
        set minX = R2I(GetRectMinX(world))
        set minY = R2I(GetRectMinY(world))
        set centerX = R2I((maxX + minX)/2)
        set centerY = R2I((minY + maxY)/2)
        set playMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
        set playMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
        set playMinX = GetRectMinX(bj_mapInitialPlayableArea)
        set playMinY = GetRectMinY(bj_mapInitialPlayableArea)
        set worldRegion = CreateRegion()
        call RegionAddRect(worldRegion, world)
        endmethod
    endmodule
    
    struct WorldBounds extends array
        readonly static integer maxX
        readonly static integer maxY
        readonly static integer minX
        readonly static integer minY
        readonly static integer centerX
        readonly static integer centerY
        readonly static rect world
        readonly static region worldRegion
        readonly static real playMaxX
        readonly static real playMaxY
        readonly static real playMinX
        readonly static real playMinY
        implement WorldBoundInit
    endstruct
    
endlibrary
library LineSegmentEnumeration /* v2.2a -- hiveworkshop.com/threads/line-segment-enumeration-v1-1.286552/

    Information
    ¯¯¯¯¯¯¯¯¯¯¯

        Allows to enumerate widgets inside a line segment with an offset.
        So basicly it will result in a rect, but which is also allowed to be rotated.
     
     
    Mechanics
    ¯¯¯¯¯¯¯¯¯
     
        (Issue:)
     
        The problem with normal jass rects is that they aren't defined by 4 points, but only by 4 values: x/y -min/max.
        The result is that a jass rect is never rotated, so it's always paralel to the x/y axis.
     
        But when we draw a line from point A to point B we might also create a non-axix-parelel rect, and then
        we can't use the normal rect natives from jass anymore to find out if a point is inside the rect.

        (Solution:)
     
        To solve this problem the system does following:
     
        jass rect: rectangular defined by 4 values (axis paralel)
        custom rect: the real rectangular that is defined by user (not axis parelel)

        1. Create a big jass rect that is big enough so we can ensure to enum all widgets that are potentialy inside our custom rect. (Enum_Group)
           This Enum_Group will contain all wanted units, but may also contain not wanted units.

        2. Construct the custom rect following a form with the same parameters as in this image, but in 2D:
           https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Ellipsoide.svg/800px-Ellipsoide.svg.png

        3. Loop through Enum_Group and define each widget's coordinates relative to the center of the custom rect as a 2D vector

        4. Get the components of the widget's position vector on the local (rotated) x-y axis of the custom rect

        5. Check if the projected lengths (absolute value of components) of the widget's position is less than <a> and <b> as described in the
           image linked above.

*/
//  --- API ---
//! novjass

    struct LineSegment

        static constant real MAX_UNIT_COLLISION
 
        static method EnumUnitsEx takes group whichgroup, real ax, real ay, real bx, real by, real offset, boolean checkCollision returns nothing
        static method EnumUnits takes group whichgroup, real ax, real ay, real bx, real by, real offset returns nothing
 
        static method EnumDestructables takes real ax, real ay, real bx, real by, real offset returns nothing
         
        //  after enumerated destructables you have access to:
     
            static integer DestructableCounter      // starts with index "0"
            static destructable array Destructable
         
        static method EnumItems takes real ax, real ay, real bx, real by, real offset returns nothing
         
        //  after enumerated items you have access to:
     
            static integer ItemCounter      // starts with index "0"
            static destructable array Item
         
//! endnovjass
// ==== End API ====

    struct LineSegment extends array

        public static constant real MAX_UNIT_COLLISION = 197.00

        private static constant rect RECT = Rect(0, 0, 0, 0)

        private static constant group GROUP = CreateGroup()

        private static real ox
        private static real oy
        private static real dx
        private static real dy
        private static real da
        private static real db
        private static real ui
        private static real uj
        private static real wdx
        private static real wdy

        private static method PrepareRect takes real ax, real ay, real bx, real by, real offset, real offsetCollision returns nothing
            local real maxX
            local real maxY
            local real minX
            local real minY

            // get center coordinates of rectangle
            set ox = 0.5*(ax + bx)
            set oy = 0.5*(ay + by)

            // get rectangle major axis as vector
            set dx = 0.5*(bx - ax)
            set dy = 0.5*(by - ay)

            // get half of rectangle length (da) and height (db)
            set da = SquareRoot(dx*dx + dy*dy)
            set db = offset

            // get unit vector of the major axis
            set ui = dx/da
            set uj = dy/da

            // Prepare the bounding Jass Rect
            set offset = offset + offsetCollision

            if ax > bx then
                set maxX = ax + offset
                set minX = bx - offset
            else
                set maxX = bx + offset
                set minX = ax - offset
            endif

            if ay > by then
                set maxY = ay + offset
                set minY = by - offset
            else
                set maxY = by + offset
                set minY = ay - offset
            endif

            call SetRect(RECT, minX, minY, maxX, maxY)
        endmethod

        private static method RotateWidgetCoordinates takes widget w returns nothing
            // distance of widget from rectangle center in vector form
            set wdx = GetWidgetX(w) - ox
            set wdy = GetWidgetY(w) - oy

            set dx = wdx*ui + wdy*uj    // get the component of above vector in the rect's major axis
            set dy = wdx*(-uj) + wdy*ui // get the component of above vector in the rect's transverse axis
        endmethod

        private static method IsWidgetInRect takes widget w returns boolean
            call RotateWidgetCoordinates(w)

            // Check if the components above are less than half the length and height of the rectangle
            // (Square them to compare absolute values)
            return dx*dx <= da*da and dy*dy <= db*db
        endmethod

        private static method IsUnitInRect takes unit u, boolean checkCollision returns boolean
            if checkCollision then
                call RotateWidgetCoordinates(u)

                // Check if the perpendicular distances of the unit from both axes of the rect are less than
                // da and db
                return IsUnitInRangeXY(u, ox - dy*uj, oy + dy*ui, RAbsBJ(da)) /*
                */ and IsUnitInRangeXY(u, ox + dx*ui, oy + dx*uj, RAbsBJ(db))
            endif

            return IsWidgetInRect(u)
        endmethod

        public static method EnumUnitsEx takes group whichgroup, real ax, real ay, real bx, real by, real offset, boolean checkCollision returns nothing
            local unit u

            if checkCollision then
                call PrepareRect(ax, ay, bx, by, offset, MAX_UNIT_COLLISION)
            else
                call PrepareRect(ax, ay, bx, by, offset, 0.00)
            endif

            call GroupEnumUnitsInRect(GROUP, RECT, null)

            // enum through all tracked units, and check if it's inside bounds
            call GroupClear(whichgroup)
            loop
                set u = FirstOfGroup(GROUP)
                exitwhen u == null

                if IsUnitInRect(u, checkCollision) then
                    call GroupAddUnit(whichgroup, u)
                endif

                call GroupRemoveUnit(GROUP, u)
            endloop
        endmethod

        public static method EnumUnits takes group whichgroup, real ax, real ay, real bx, real by, real offset returns nothing
            call EnumUnitsEx(whichgroup, ax, ay, bx, by, offset, false)
        endmethod

    //! textmacro LSE_WIDGET takes TYPE, NAME
        public static integer $NAME$Counter = -1
        public static $TYPE$ array $NAME$
     
        private static method on$NAME$Filter takes nothing returns nothing
            local $TYPE$ t = GetFilter$NAME$()

            if IsWidgetInRect(t) then
                set $NAME$Counter = $NAME$Counter + 1
                set $NAME$[$NAME$Counter] = t
            endif

            set t = null
        endmethod
     
        public static method Enum$NAME$s takes real ax, real ay, real bx, real by, real offset returns nothing
            call PrepareRect(ax, ay, bx, by, offset, 0.00)

            set $NAME$Counter = -1
            call Enum$NAME$sInRect(RECT, Filter(function thistype.on$NAME$Filter), null)
        endmethod
    //! endtextmacro

    //! runtextmacro LSE_WIDGET("destructable", "Destructable")
    //! runtextmacro LSE_WIDGET("item", "Item")
     
    endstruct
endlibrary
library TimedHandles uses optional TimerUtils
/**************************************************************
*
*   v1.0.5 by TriggerHappy
*   ----------------------
*
*   Use this to destroy a handle after X amount seconds.
*
*   It's useful for things like effects where you may
*   want it to be temporary, but not have to worry
*   about the cleaning memory leak. By default it supports
*   effects, lightning, weathereffect, items, ubersplats, and units.
*
*   If you want to add your own handle types copy a textmacro line
*   at the bottom and add whichever handle you want along with it's destructor.
*
*   Example: //! runtextmacro TIMEDHANDLES("handle", "DestroyHandle")
*
*   Installation
    ----------------------
*       1. Copy this script and over to your map inside a blank trigger.
*       2. If you want more efficiency copy TimerUtils over as well.
*
*   API
*   ----------------------
*       call DestroyEffectTimed(AddSpecialEffect("effect.mdx", 0, 0), 5)
*       call DestroyLightningTimed(AddLightning("CLPB", true, 0, 0, 100, 100), 5)
*
*   Credits to Vexorian for TimerUtils and his help on the script.
*
**************************************************************/

    globals
        // If you don't want a timer to be ran each instance
        // set this to true.
        private constant boolean SINGLE_TIMER = true
        // If you chose a single timer then this will be the speed
        // at which the timer will update
        private constant real    UPDATE_PERIOD = 0.05
    endglobals

    // here you may add or remove handle types
    //! runtextmacro TIMEDHANDLES("effect", "DestroyEffect")
    //! runtextmacro TIMEDHANDLES("lightning", "DestroyLightning")
    //! runtextmacro TIMEDHANDLES("weathereffect", "RemoveWeatherEffect")
    //! runtextmacro TIMEDHANDLES("item", "RemoveItem")
    //! runtextmacro TIMEDHANDLES("unit", "RemoveUnit")
    //! runtextmacro TIMEDHANDLES("ubersplat", "DestroyUbersplat")
    
    // Do not edit below this line
    
    //! textmacro TIMEDHANDLES takes HANDLE,DESTROY
        
        struct $HANDLE$Timed
        
            $HANDLE$ $HANDLE$_var
            static integer index = -1
            static thistype array instance
            static real REAL=UPDATE_PERIOD
            
            static if SINGLE_TIMER then
                static timer timer = CreateTimer()
                real duration
                real elapsed = 0
            else static if not LIBRARY.TimerUtils then
                static hashtable table = InitHashtable()
            endif
            
            method destroy takes nothing returns nothing
                call $DESTROY$(this.$HANDLE$_var)
                set this.$HANDLE$_var = null
                
                static if SINGLE_TIMER then
                    set this.elapsed = 0
                endif
                
                call this.deallocate()
            endmethod
            
            private static method remove takes nothing returns nothing
                static if SINGLE_TIMER then
                    local integer i = 0
                    local thistype this
                    loop
                        exitwhen i > thistype.index
                        set this = instance[i]
                        set this.elapsed = this.elapsed + UPDATE_PERIOD
                        if (this.elapsed >= this.duration) then
                            set instance[i] = instance[index]
                            set i = i - 1
                            set index = index - 1
                            call this.destroy()
                            if (index == -1) then
                                call PauseTimer(thistype.timer)
                            endif
                        endif
                        set i = i + 1
                    endloop
                else
                    local timer t = GetExpiredTimer()
                    static if LIBRARY.TimerUtils then
                        local $HANDLE$Timed this = GetTimerData(t)
                        call ReleaseTimer(t)
                        call this.destroy()
                    else
                        local $HANDLE$Timed this = LoadInteger(table, 0, GetHandleId(t))
                        call DestroyTimer(t)
                        set t = null
                        call this.destroy()
                    endif
                endif
            endmethod
            
            static method create takes $HANDLE$ h, real timeout returns $HANDLE$Timed
                local $HANDLE$Timed this = $HANDLE$Timed.allocate()
                
                static if SINGLE_TIMER then
                    set index = index + 1
                    set instance[index] = this
                    if (index == 0) then
                        call TimerStart(thistype.timer, UPDATE_PERIOD, true, function thistype.remove)
                    endif
                    set this.duration = timeout
                else
                    static if LIBRARY.TimerUtils then
                        call TimerStart(NewTimerEx(this), timeout, false, function $HANDLE$timed.remove)
                    else
                        local timer t = CreateTimer()
                        call SaveInteger(thistype.table, 0, GetHandleId(t), this)
                        call TimerStart(t, timeout, false, function $HANDLE$Timed.remove)
                        set t = null
                    endif
                endif  
                
                set this.$HANDLE$_var = h
                
                return this
            endmethod
            
        endstruct
        
        function $DESTROY$Timed takes $HANDLE$ h, real duration returns $HANDLE$Timed
            return $HANDLE$Timed.create(h, duration)
        endfunction

    //! endtextmacro
    
endlibrary
library PluginSpellEffect requires RegisterPlayerUnitEvent
    /* ------------------- SpellEffectPlugin v1.2 by Chopinski ------------------ */
    // Simple plugin for the SpellEffectEvent library by Bribe to cache some usefull
    // values.

    // Credits to Bribe and Magtheridon96
    /* ----------------------------------- END ---------------------------------- */

    private struct SUnit
        unit    unit
        player  player
        integer handle
        boolean isHero
        boolean isStructure
        integer id
        real    x
        real    y
        real    z
        
        static method create takes nothing returns thistype
            return thistype.allocate()
        endmethod
    endstruct
    
    private module Event
        readonly static location location = Location(0, 0)
        readonly static SUnit    source
        readonly static SUnit    target
        readonly static ability  ability
        readonly static integer  level
        readonly static integer  id
        readonly static real     x
        readonly static real     y
        readonly static real     z

        private static method GetUnitZ takes unit u returns real
            call MoveLocation(location, GetUnitX(u), GetUnitY(u))
            return GetUnitFlyHeight(u) + GetLocationZ(location)
        endmethod

        private static method GetSpellTargetZ takes nothing returns real
            call MoveLocation(location, x, y)
            if target.unit != null then
                return GetUnitZ(target.unit)
            else
                return GetLocationZ(location)
            endif
        endmethod

        private static method onCast takes nothing returns nothing
            if GetUnitAbilityLevel(GetTriggerUnit(), 'Aloc') == 0 then
                set source.unit   = GetTriggerUnit()
                set source.player = GetOwningPlayer(source.unit)
                set source.handle = GetHandleId(source.unit)
                set source.id     = GetUnitUserData(source.unit)
                set source.x      = GetUnitX(source.unit)
                set source.y      = GetUnitY(source.unit)
                set source.z      = GetUnitZ(source.unit)
                set source.isHero = IsUnitType(source.unit, UNIT_TYPE_HERO)
                set source.isStructure = IsUnitType(source.unit, UNIT_TYPE_STRUCTURE)
                
                set target.unit   = GetSpellTargetUnit()
                set target.player = GetOwningPlayer(target.unit)
                set target.handle = GetHandleId(target.unit)
                set target.id     = GetUnitUserData(target.unit)
                set target.x      = GetUnitX(target.unit)
                set target.y      = GetUnitY(target.unit)
                set target.z      = GetUnitZ(target.unit)
                set target.isHero = IsUnitType(target.unit, UNIT_TYPE_HERO)
                set target.isStructure = IsUnitType(target.unit, UNIT_TYPE_STRUCTURE)
                
                set x             = GetSpellTargetX()
                set y             = GetSpellTargetY()
                set z             = GetSpellTargetZ()
                set id            = GetSpellAbilityId()
                set level         = GetUnitAbilityLevel(source.unit, id)
                set ability       = BlzGetUnitAbility(source.unit, id)
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            set source = SUnit.create()
            set target = SUnit.create()
        
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
        endmethod
    endmodule

    struct Spell extends array
        implement Event
    endstruct
endlibrary
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
//     RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
//     RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
//     Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
 
    //============================================================================
    private module M
       
        static if LIBRARY_Table then
            static Table tb
        else
            static hashtable ht = InitHashtable()
        endif
       
        static method onCast takes nothing returns nothing
            static if LIBRARY_Table then
                call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
            else
                call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
            endif
        endmethod
     
        private static method onInit takes nothing returns nothing
            static if LIBRARY_Table then
                set .tb = Table.create()
            endif
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
        endmethod
    endmodule
     
    //============================================================================
    private struct S extends array
        implement M
    endstruct
     
    //============================================================================
    function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
        static if LIBRARY_Table then
            if not S.tb.handle.has(abil) then
                set S.tb.trigger[abil] = CreateTrigger()
            endif
            call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
        else
            if not HaveSavedHandle(S.ht, 0, abil) then
                call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
            endif
            call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
        endif
    endfunction
endlibrary
// Arcing Text Tag v1.0.0.3 by Maker

library FloatingTextArc
    globals
        private constant    real    SIZE_MIN        = 0.014         // Minimum size of text
        private constant    real    SIZE_BONUS      = 0.000         // Text size increase
        private constant    real    TIME_LIFE       = 1.0           // How long the text lasts
        private constant    real    TIME_FADE       = 0.3           // When does the text start to fade
        private constant    real    Z_OFFSET        = 50            // Height above unit
        private constant    real    Z_OFFSET_BON    = 50            // How much extra height the text gains
        private constant    real    VELOCITY        = 2             // How fast the text move in x/y plane
        private constant    real    ANGLE           = bj_PI/2       // Movement angle of the text. Does not apply if
                                                                    // ANGLE_RND is true
        private constant    boolean ANGLE_RND       = true          // Is the angle random or fixed
        private             timer   TMR             = CreateTimer()
    endglobals
    
    struct ArcingTextTag extends array
        private texttag tt
        private real as         // angle, sin component
        private real ac         // angle, cos component
        private real ah         // arc height
        private real t          // time
        private real x          // origin x
        private real y          // origin y
        private string s        // text
        private static integer array next
        private static integer array prev
        private static integer array rn
        private static integer ic           = 0       // Instance count   
        
        private static method update takes nothing returns nothing
            local thistype this=next[0]
            local real p
            loop
                set p = Sin(bj_PI*.t)
                set .t = .t - 0.03125
                set .x = .x + .ac
                set .y = .y + .as
                call SetTextTagPos(.tt, .x, .y, Z_OFFSET + Z_OFFSET_BON * p)
                call SetTextTagText(.tt, .s, SIZE_MIN + SIZE_BONUS * p)
                if .t <= 0 then
                    set .tt = null
                    set next[prev[this]] = next[this]
                    set prev[next[this]] = prev[this]
                    set rn[this] = rn[0]
                    set rn[0] = this
                    if next[0]==0 then
                        call PauseTimer(TMR)
                    endif
                endif
                set this = next[this]
                exitwhen this == 0
            endloop
        endmethod
        
        public static method create takes string s, unit u returns thistype
            local thistype this = rn[0]
            static if ANGLE_RND then
                local real a = GetRandomReal(0, 2*bj_PI)
            else
                local real a = ANGLE
            endif
            if this == 0 then
                set ic = ic + 1
                set this = ic
            else
                set rn[0] = rn[this]
            endif
            
            set next[this] = 0
            set prev[this] = prev[0]
            set next[prev[0]] = this
            set prev[0] = this
            
            set .s = s
            set .x = GetUnitX(u)
            set .y = GetUnitY(u)
            set .t = TIME_LIFE
            set .as = Sin(a)*VELOCITY
            set .ac = Cos(a)*VELOCITY
            set .ah = 0.
        
            if IsUnitVisible(u, GetLocalPlayer()) then
                set .tt = CreateTextTag()
                call SetTextTagPermanent(.tt, false)
                call SetTextTagLifespan(.tt, TIME_LIFE)
                call SetTextTagFadepoint(.tt, TIME_FADE)
                call SetTextTagText(.tt, s, SIZE_MIN)
                call SetTextTagPos(.tt, .x, .y, Z_OFFSET)
            endif
            
            if prev[this] == 0 then
                call TimerStart(TMR, 0.03125, true, function thistype.update)
            endif
            
            return this
        endmethod
    endstruct
endlibrary
/**************************************************************
*
*   RegisterPlayerUnitEvent
*   v5.1.0.1
*   By Magtheridon96
*
*   I would like to give a special thanks to Bribe, azlier
*   and BBQ for improving this library. For modularity, it only 
*   supports player unit events.
*
*   Functions passed to RegisterPlayerUnitEvent must either
*   return a boolean (false) or nothing. (Which is a Pro)
*
*   Warning:
*   --------
*
*       - Don't use TriggerSleepAction inside registered code.
*       - Don't destroy a trigger unless you really know what you're doing.
*
*   API:
*   ----
*
*       - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
*           - Registers code that will execute when an event fires.
*       - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
*           - Registers code that will execute when an event fires for a certain player.
*       - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
*           - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
    globals
        private trigger array t
    endglobals
    
    function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
        local integer i = GetHandleId(p)
        local integer k = 15
        if t[i] == null then
            set t[i] = CreateTrigger()
            loop
                call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
                exitwhen k == 0
                set k = k - 1
            endloop
        endif
        call TriggerAddCondition(t[i], Filter(c))
    endfunction
    
    function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
        local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
        if t[i] == null then
            set t[i] = CreateTrigger()
            call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
        endif
        call TriggerAddCondition(t[i], Filter(c))
    endfunction
    
    function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
        return t[GetHandleId(p)]
    endfunction
endlibrary
library MissileEffect requires WorldBounds, Alloc
    /* ------------------------------------- Missile Effect v2.8 ------------------------------------ */
    // This is a simple helper library for the Relativistic Missiles system.
    // Credits:
    //     Sevion for the Alloc module
    //         - www.hiveworkshop.com/threads/snippet-alloc.192348/
    //     Nestharus for World Bounds Library
    /* ---------------------------------------- By Chopinski ---------------------------------------- */
    
    /* ---------------------------------------------------------------------------------------------- */
    /*                                             System                                             */
    /* ---------------------------------------------------------------------------------------------- */
    private module LinkedList
        readonly thistype next
        readonly thistype prev

        method init takes nothing returns thistype
            set next = this
            set prev = this

            return this
        endmethod

        method pushBack takes thistype node returns thistype
            set node.prev = prev
            set node.next = this
            set prev.next = node
            set prev = node

            return node
        endmethod

        method pushFront takes thistype node returns thistype
            set node.prev = this
            set node.next = next
            set next.prev = node
            set next = node

            return node
        endmethod

        method pop takes nothing returns nothing
            set prev.next = next
            set next.prev = prev
        endmethod
    endmodule

    private struct Effect extends array
        implement LinkedList
        implement Alloc

        real    x
        real    y
        real    z
        real    size
        real    yaw
        real    pitch
        real    roll
        string  path
        effect  effect

        method remove takes nothing returns nothing
            call DestroyEffect(effect)
            call pop()
            call deallocate()
            set effect = null
        endmethod

        method insert takes string fxpath, real x, real y, real z, real scale returns thistype
            local thistype node = pushBack(allocate())

            set node.x      = x
            set node.y      = y
            set node.z      = z
            set node.yaw    = 0.
            set node.pitch  = 0.
            set node.roll   = 0.
            set node.path   = fxpath
            set node.size   = scale
            set node.effect = AddSpecialEffect(fxpath, x, y)
            call BlzSetSpecialEffectZ(node.effect, z)
            call BlzSetSpecialEffectScale(node.effect, scale)

            return node
        endmethod

        static method create takes nothing returns thistype
            return thistype(allocate()).init()
        endmethod
    endstruct

    struct MissileEffect
        real    size
        real    yaw
        real    pitch
        real    roll
        real    time
        integer transparency
        integer animtype
        integer playercolor
        string  path
        effect  effect
        Effect  attachments

        /* -------------------------------- Operators ------------------------------- */
        method operator timeScale= takes real newTimeScale returns nothing
            set time = newTimeScale
            call BlzSetSpecialEffectTimeScale(effect, time)
        endmethod
        
        method operator timeScale takes nothing returns real
            return time
        endmethod

        method operator alpha= takes integer newAlpha returns nothing
            set transparency = newAlpha
            call BlzSetSpecialEffectAlpha(effect, transparency)
        endmethod

        method operator alpha takes nothing returns integer
            return transparency
        endmethod

        method operator playerColor= takes integer playerId returns nothing
            set playercolor = playerId
            call BlzSetSpecialEffectColorByPlayer(effect, Player(playerId))
        endmethod

        method operator playerColor takes nothing returns integer
            return playercolor
        endmethod

        method operator animation= takes integer animType returns nothing
            set animtype = animType
            call BlzPlaySpecialEffect(effect, ConvertAnimType(animtype))
        endmethod

        method operator animation takes nothing returns integer
            return animtype
        endmethod

        /* --------------------------------- Methods -------------------------------- */
        method scale takes effect sfx, real scale returns nothing
            set size = scale
            call BlzSetSpecialEffectScale(sfx, scale)
        endmethod   

        method orient takes real yaw, real pitch, real roll returns nothing
            local Effect node = attachments.next

            set .yaw   = yaw
            set .pitch = pitch
            set .roll  = roll
            call BlzSetSpecialEffectOrientation(effect, yaw, pitch, roll)

            loop
                exitwhen node == attachments
                    set node.yaw   = yaw
                    set node.pitch = pitch
                    set node.roll  = roll
                    call BlzSetSpecialEffectOrientation(node.effect, yaw, pitch, roll)
                set node = node.next
            endloop
        endmethod

        method move takes real x, real y, real z returns boolean
            local Effect node = attachments.next

            if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
                call BlzSetSpecialEffectPosition(effect, x, y, z)
                loop
                    exitwhen node == attachments
                        call BlzSetSpecialEffectPosition(node.effect, x - node.x, y - node.y, z - node.z)
                    set node = node.next
                endloop
                return true
            endif
            return false
        endmethod

        method attach takes string fxpath, real dx, real dy, real dz, real scale returns effect
            local Effect node = attachments.insert(fxpath, dx, dy, dz, scale)

            call BlzSetSpecialEffectPosition(node.effect, BlzGetLocalSpecialEffectX(effect) - dx, BlzGetLocalSpecialEffectY(effect) - dy, BlzGetLocalSpecialEffectZ(effect) - dz)

            return node.effect
        endmethod

        method detach takes effect sfx returns nothing
            local Effect node = attachments.next

            loop
                exitwhen node == attachments
                    if GetHandleId(node.effect) == GetHandleId(sfx) then
                        call node.remove()
                        exitwhen true
                    endif
                set node = node.next
            endloop
        endmethod

        method setColor takes integer red, integer green, integer blue returns nothing
            call BlzSetSpecialEffectColor(effect, red, green, blue)
        endmethod

        /* -------------------------- Contructor/Destructor ------------------------- */
        method destroy takes nothing returns nothing
            local Effect node = attachments.next

            loop
                exitwhen node == attachments
                    call node.remove()
                set node = node.next
            endloop
            call DestroyEffect(effect)
            call attachments.deallocate()
            
            set effect = null
            set path   = null
            set size   = 1.
            call deallocate()
        endmethod

        static method create takes real x, real y, real z returns thistype
            local thistype this = thistype.allocate()

            set effect = AddSpecialEffect("", x, y)
            set path = ""
            set size = 1
            set time = 0
            set transparency = 0
            set animtype = 0
            set playercolor = 0
            set attachments = Effect.create()
            call BlzSetSpecialEffectZ(effect, z)

            return this
        endmethod
    endstruct
endlibrary
library MissileUtils requires Missiles, Alloc
    /* ------------------------------------- Missile Utils v2.8 ------------------------------------- */
    // This is a simple Utils library for the Relativistic Missiles system.
    // Credits:
    //     Sevion for the Alloc module
    //         - www.hiveworkshop.com/threads/snippet-alloc.192348/
    /* ---------------------------------------- By Chopinski ---------------------------------------- */
    
    /* ---------------------------------------------------------------------------------------------- */
    /*                                            JASS API                                            */
    /* ---------------------------------------------------------------------------------------------- */
    function CreateMissileGroup takes nothing returns MissileGroup
        return MissileGroup.create()
    endfunction
    
    function DestroyMissileGroup takes MissileGroup missiles returns nothing
        if missiles != 0 then
            call missiles.clear()
            call missiles.destroy()
        endif
    endfunction
    
    function MissileGroupGetSize takes MissileGroup missiles returns integer
        if missiles != 0 then
            return missiles.size
        else
            return 0
        endif
    endfunction
    
    function GroupMissileAt takes MissileGroup missiles, integer position returns Missiles
        if missiles != 0 then
            return missiles.missileAt(position)
        else
            return 0
        endif
    endfunction
    
    function ClearMissileGroup takes MissileGroup missiles returns nothing
        if missiles != 0 then
            call missiles.clear()
        endif
    endfunction
    
    function IsMissileInGroup takes Missiles missile, MissileGroup missiles returns boolean
        if missiles != 0 and missile != 0 then
            if missiles.size > 0 then
                return missiles.contains(missile)
            else
                return false
            endif
        else
            return false
        endif
    endfunction
    
    function GroupRemoveMissile takes MissileGroup missiles, Missiles missile returns nothing
        if missiles != 0 and missile != 0 then
            if missiles.size > 0 then
                call missiles.remove(missile)
            endif
        endif
    endfunction
    
    function GroupAddMissile takes MissileGroup missiles, Missiles missile returns nothing
        if missiles != 0 and missile != 0 then
            if not missiles.contains(missile) then
                call missiles.insert(missile)
            endif
        endif
    endfunction
    
    function GroupPickRandomMissile takes MissileGroup missiles returns Missiles
        if missiles != 0 then
            if missiles.size > 0 then
                return missiles.missileAt(GetRandomInt(0, missiles.size - 1))
            else
                return 0
            endif
        else
            return 0
        endif
    endfunction
    
    function FirstOfMissileGroup takes MissileGroup missiles returns Missiles
        if missiles != 0 then
            if missiles.size > 0 then
                return missiles.group.next.missile
            else
                return 0
            endif
        else
            return 0
        endif
    endfunction
    
    function GroupAddMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
        if source != 0 and destiny != 0 then
            if source.size > 0 and source != destiny then
                call destiny.addGroup(source)
            endif
        endif
    endfunction
    
    function GroupRemoveMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
        if source != 0 and destiny != 0 then
            if source == destiny then
                call source.clear()
            elseif source.size > 0 then
                call destiny.removeGroup(source)
            endif
        endif
    endfunction
    
    function GroupEnumMissilesOfType takes MissileGroup missiles, integer whichType returns nothing
        local integer i
        local Missiles missile
        
        if missiles != 0 then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count
                        set missile = Missiles.collection[i]
                        
                        if missile.type == whichType then
                            call missiles.insert(missile)
                        endif
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesOfTypeCounted takes MissileGroup missiles, integer whichType, integer amount returns nothing
        local integer i
        local integer j = amount
        local Missiles missile
        
        if missiles != 0 then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count or j == 0
                        set missile = Missiles.collection[i]
                        
                        if missile.type == whichType then
                            call missiles.insert(missile)
                        endif
                        set j = j - 1
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesOfPlayer takes MissileGroup missiles, player p returns nothing
        local integer i
        local Missiles missile
        
        if missiles != 0 then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count
                        set missile = Missiles.collection[i]
                        
                        if missile.owner == p then
                            call missiles.insert(missile)
                        endif
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesOfPlayerCounted takes MissileGroup missiles, player p, integer amount returns nothing
        local integer i
        local integer j = amount
        local Missiles missile
        
        if missiles != 0 then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count or j == 0
                        set missile = Missiles.collection[i]
                        
                        if missile.owner == p then
                            call missiles.insert(missile)
                        endif
                        set j = j - 1
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesInRect takes MissileGroup missiles, rect r returns nothing
        local integer i
        local Missiles missile
        
        if missiles != 0 and r != null then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count
                        set missile = Missiles.collection[i]
                        
                        if GetRectMinX(r) <= missile.x and missile.x <= GetRectMaxX(r) and GetRectMinY(r) <= missile.y and missile.y <= GetRectMaxY(r) then
                            call missiles.insert(missile)
                        endif
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesInRectCounted takes MissileGroup missiles, rect r, integer amount returns nothing
        local integer i
        local integer j = amount
        local Missiles missile
        
        if missiles != 0 and r != null then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count or j == 0
                        set missile = Missiles.collection[i]
                        
                        if GetRectMinX(r) <= missile.x and missile.x <= GetRectMaxX(r) and GetRectMinY(r) <= missile.y and missile.y <= GetRectMaxY(r) then
                            call missiles.insert(missile)
                        endif
                        set j = j - 1
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesInRangeOfLoc takes MissileGroup missiles, location loc, real radius returns nothing
        local real dx
        local real dy
        local integer i
        local Missiles missile
    
        if missiles != 0 and radius > 0 and loc != null then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count
                        set missile = Missiles.collection[i]
                        set dx = missile.x - GetLocationX(loc)
                        set dy = missile.y - GetLocationY(loc)
                        
                        if SquareRoot(dx*dx + dy*dy) <= radius then
                            call missiles.insert(missile)
                        endif
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesInRangeOfLocCounted takes MissileGroup missiles, location loc, real radius, integer amount returns nothing
        local real dx
        local real dy
        local integer i
        local integer j = amount
        local Missiles missile
    
        if missiles != 0 and radius > 0 and loc != null then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count or j == 0
                        set missile = Missiles.collection[i]
                        set dx = missile.x - GetLocationX(loc)
                        set dy = missile.y - GetLocationY(loc)
                        
                        if SquareRoot(dx*dx + dy*dy) <= radius then
                            call missiles.insert(missile)
                        endif
                        set j = j - 1
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesInRange takes MissileGroup missiles, real x, real y, real radius returns nothing
        local real dx
        local real dy
        local integer i
        local Missiles missile
    
        if missiles != 0 and radius > 0 then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count
                        set missile = Missiles.collection[i]
                        set dx = missile.x - x
                        set dy = missile.y - y
                        
                        if SquareRoot(dx*dx + dy*dy) <= radius then
                            call missiles.insert(missile)
                        endif
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
    
    function GroupEnumMissilesInRangeCounted takes MissileGroup missiles, real x, real y, real radius, integer amount returns nothing
        local real dx
        local real dy
        local integer i
        local integer j = amount
        local Missiles missile
    
        if missiles != 0 and radius > 0 then
            if Missiles.count > -1 then
                set i = 0
                
                if missiles.size > 0 then
                    call missiles.clear()
                endif
                
                loop
                    exitwhen i > Missiles.count or j == 0
                        set missile = Missiles.collection[i]
                        set dx = missile.x - x
                        set dy = missile.y - y
                        
                        if SquareRoot(dx*dx + dy*dy) <= radius then
                            call missiles.insert(missile)
                        endif
                        set j = j - 1
                    set i = i + 1
                endloop
            endif
        endif
    endfunction

    /* ---------------------------------------------------------------------------------------------- */
    /*                                             System                                             */
    /* ---------------------------------------------------------------------------------------------- */
    private module LinkedList
        readonly thistype next
        readonly thistype prev

        method init takes nothing returns thistype
            set next = this
            set prev = this

            return this
        endmethod

        method pushBack takes thistype node returns thistype
            set node.prev = prev
            set node.next = this
            set prev.next = node
            set prev = node

            return node
        endmethod

        method pushFront takes thistype node returns thistype
            set node.prev = this
            set node.next = next
            set next.prev = node
            set next = node

            return node
        endmethod

        method pop takes nothing returns nothing
            set prev.next = next
            set next.prev = prev
        endmethod
    endmodule

    private struct MGroup extends array
        implement LinkedList
        implement Alloc
        
        Missiles missile
        
        method remove takes nothing returns nothing
            call pop()
            call deallocate()
        endmethod

        method insert takes Missiles m returns thistype
            local thistype node = pushBack(allocate())

            set node.missile = m

            return node
        endmethod
        
        static method create takes nothing returns thistype
            return thistype(allocate()).init()
        endmethod
    endstruct

    struct MissileGroup
        MGroup group
        integer size
        
        method destroy takes nothing returns nothing
            call group.deallocate()
            call deallocate()
        endmethod
        
        method missileAt takes integer i returns Missiles
            local MGroup node = group.next
            local integer j = 0
        
            if size > 0 and i <= size - 1 then
                loop
                    exitwhen j == i
                        set node = node.next
                    set j = j + 1
                endloop
                
                return node.missile
            else
                return 0
            endif
        endmethod
        
        method remove takes Missiles missile returns nothing
            local MGroup node = group.next
        
            loop
                exitwhen node == group
                    if node.missile == missile then
                        set size = size - 1
                        call node.remove()
                        exitwhen true
                    endif
                set node = node.next
            endloop
        endmethod
        
        method insert takes Missiles missile returns nothing
            set size = size + 1
            call group.insert(missile)
        endmethod
        
        method clear takes nothing returns nothing
            local MGroup node = group.next
            
            loop
                exitwhen node == group
                    call node.remove()
                set node = node.next
            endloop
            
            set size = 0
        endmethod
        
        method contains takes Missiles missile returns boolean
            local MGroup node = group.next
            local boolean found = false
        
            loop
                exitwhen node == group
                    if node.missile == missile then
                        set found = true
                        exitwhen true
                    endif
                set node = node.next
            endloop
            
            return found
        endmethod
        
        method addGroup takes MissileGroup source returns nothing
            local MGroup node = source.group.next
        
            loop
                exitwhen node == source.group
                    if not contains(node.missile) then
                        call insert(node.missile)
                    endif
                set node = node.next
            endloop
        endmethod
        
        method removeGroup takes MissileGroup source returns nothing
            local MGroup node = source.group.next
        
            loop
                exitwhen node == source.group
                    if contains(node.missile) then
                        call remove(node.missile)
                    endif
                set node = node.next
            endloop
        endmethod
        
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            
            set group = MGroup.create()
            set size = 0
            
            return this
        endmethod
    endstruct
endlibrary
library Missiles requires MissileEffect, TimerUtils, WorldBounds
    /* ---------------------------------------- Missiles v2.8 --------------------------------------- */
    // Thanks and Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
    // this Missiles library. Credits and thanks to AGD and for the effect orientation ideas.
    // This version of Missiles requires patch 1.31+
    
    // How to Import:
    //     1 - Copy this, MissileEffect and optionaly the MissileUtils libraries to your map
    /* ---------------------------------------- By Chopinski ---------------------------------------- */
    
    /* ---------------------------------------------------------------------------------------------- */
    /*                                             System                                             */
    /* ---------------------------------------------------------------------------------------------- */
    globals
        // The update period of the system
        public  constant real    PERIOD             = 1./40.
        // The max amount of Missiles processed in a PERIOD
        // You can play around with both these values to find
        // your sweet spot. If equal to 0, the system will
        // process all missiles at once every period.
        public  constant real    SWEET_SPOT         = 150
        // the avarage collision size compensation when detecting collisions
        private constant real    COLLISION_SIZE     = 128.
        // item size used in z collision
        private constant real    ITEM_SIZE          = 16.
        // Raw code of the dummy unit used for vision
        private constant integer DUMMY              = 'dumi'
        // Needed, don't touch.
        private location         LOC                = Location(0., 0.)
    endglobals

    private interface MissileEvents
        method onHit takes unit hit returns boolean defaults false
        method onMissile takes Missiles missile returns boolean defaults false
        method onDestructable takes destructable dest returns boolean defaults false
        method onItem takes item i returns boolean defaults false
        method onCliff takes nothing returns boolean defaults false
        method onTerrain takes nothing returns boolean defaults false
        method onTileset takes integer tileset returns boolean defaults false
        method onPeriod takes nothing returns boolean defaults false
        method onFinish takes nothing returns boolean defaults false
        method onBoundaries takes nothing returns boolean defaults false
        method onPause takes nothing returns boolean defaults false
        method onResume takes nothing returns boolean defaults false
        method onRemove takes nothing returns nothing defaults nothing
    endinterface
    
    private function GetLocZ takes real x, real y returns real
        call MoveLocation(LOC, x, y)
        return GetLocationZ(LOC)
    endfunction
    
    private function GetUnitZ takes unit u returns real
        return GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
    endfunction
    
    private function SetUnitZ takes unit u, real z returns nothing
        call SetUnitFlyHeight(u, z - GetLocZ(GetUnitX(u), GetUnitY(u)), 0)
    endfunction
    
    private function GetMapCliffLevel takes nothing returns integer
        return GetTerrainCliffLevel(WorldBounds.maxX, WorldBounds.maxY)
    endfunction

    private struct Pool
        private static player player = Player(PLAYER_NEUTRAL_PASSIVE)
        private static group  group  = CreateGroup()

        timer timer
        unit  unit

        static method recycle takes unit dummy returns nothing
            if GetUnitTypeId(dummy) == DUMMY then
                call GroupAddUnit(group, dummy)
                call SetUnitX(dummy, WorldBounds.maxX)
                call SetUnitY(dummy, WorldBounds.maxY)
                call SetUnitOwner(dummy, player, false)
                call PauseUnit(dummy, true)
            endif
        endmethod

        static method retrieve takes real x, real y, real z, real face returns unit
            if BlzGroupGetSize(group) > 0 then
                set bj_lastCreatedUnit = FirstOfGroup(group)
                call PauseUnit(bj_lastCreatedUnit, false)
                call GroupRemoveUnit(group, bj_lastCreatedUnit)
                call SetUnitX(bj_lastCreatedUnit, x)
                call SetUnitY(bj_lastCreatedUnit, y)
                call SetUnitZ(bj_lastCreatedUnit, z)
                call BlzSetUnitFacingEx(bj_lastCreatedUnit, face)
            else
                set bj_lastCreatedUnit = CreateUnit(player, DUMMY, x, y, face)
                call SetUnitZ(bj_lastCreatedUnit, z)
                call UnitRemoveAbility(bj_lastCreatedUnit, 'Amrf')
            endif

            return bj_lastCreatedUnit
        endmethod

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call recycle(unit)
            call ReleaseTimer(timer)
            
            set timer = null
            set unit  = null

            call deallocate()
        endmethod

        static method recycleTimed takes unit dummy, real delay returns nothing
            local thistype this

            if GetUnitTypeId(dummy) != DUMMY then
                debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
            else
                set this = thistype.allocate()

                set timer = NewTimerEx(this)
                set unit  = dummy
                
                call TimerStart(timer, delay, false, function thistype.onExpire)
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            local integer i = 0
            local unit u

            loop
                exitwhen i == SWEET_SPOT
                    set u = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
                    call PauseUnit(u, false)
                    call GroupAddUnit(group, u)
                    call UnitRemoveAbility(u, 'Amrf')
                set i = i + 1
            endloop

            set u = null
        endmethod
    endstruct

    private struct Coordinates
        readonly real x
        readonly real y
        readonly real z
        readonly real angle
        readonly real distance
        readonly real square
        readonly real slope
        readonly real alpha

        // Creates an origin - impact link.
        private thistype ref

        private static method math takes thistype a, thistype b returns nothing
            local real dx
            local real dy
            loop
                set dx = b.x - a.x
                set dy = b.y - a.y
                set dx = dx*dx + dy*dy
                set dy = SquareRoot(dx)
                exitwhen dx != 0. and dy != 0.
                set b.x = b.x + .01
                set b.z = b.z - GetLocZ(b.x -.01, b.y) + GetLocZ(b.x, b.y)
            endloop

            set a.square   = dx
            set a.distance = dy
            set a.angle    = Atan2(b.y - a.y, b.x - a.x)
            set a.slope    = (b.z - a.z)/dy
            set a.alpha    = Atan(a.slope)
            // Set b.
            if b.ref == a then
                set b.angle     = a.angle + bj_PI
                set b.distance  = dy
                set b.slope     = -a.slope
                set b.alpha     = -a.alpha
                set b.square    = dx
            endif
        endmethod

        static method link takes thistype a, thistype b returns nothing
            set a.ref = b
            set b.ref = a
            call math(a, b)
        endmethod

        method move takes real toX, real toY, real toZ returns nothing
            set x = toX
            set y = toY
            set z = toZ + GetLocZ(toX, toY)
            if ref != this then
                call math(this, ref)
            endif
        endmethod

        method destroy takes nothing returns nothing
            call .deallocate()
        endmethod

        static method create takes real x, real y, real z returns Coordinates
            local thistype this = thistype.allocate()
            set ref = this
            call move(x, y, z)
            return this
        endmethod
    endstruct
        
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private module OnHit
        set o = origin
        set h = height
        set c = open
        set d = o.distance
    
        if .onHit.exists then
            if allocated and collision > 0 then
                call GroupEnumUnitsInRange(group, x, y, collision + COLLISION_SIZE, null)
                loop
                    set u = FirstOfGroup(group)
                    exitwhen u == null
                        if not HaveSavedBoolean(table, this, GetHandleId(u)) then
                            if IsUnitInRangeXY(u, x, y, collision) then
                                if collideZ then
                                    set dx = GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
                                    set dy = BlzGetUnitCollisionSize(u)
                                    if dx + dy >= z - collision and dx <= z + collision then
                                        call SaveBoolean(table, this, GetHandleId(u), true)
                                        if allocated and .onHit(u) then
                                            call terminate()
                                            exitwhen true
                                        endif
                                    endif
                                else
                                    call SaveBoolean(table, this, GetHandleId(u), true)
                                    if allocated and .onHit(u) then
                                        call terminate()
                                        exitwhen true
                                    endif
                                endif
                            endif
                        endif
                    call GroupRemoveUnit(group, u)
                endloop
            endif
        endif
    endmodule
    
    private module OnMissile
        if .onMissile.exists then
            if allocated and collision > 0 then
                set k = 0
                loop
                    exitwhen k > count
                        set missile = collection[k]
                        if missile != this then
                            if not HaveSavedBoolean(table, this, missile) then
                                set dx = missile.x - x
                                set dy = missile.y - y
                                if SquareRoot(dx*dx + dy*dy) <= collision then
                                    call SaveBoolean(table, this, missile, true)
                                    if allocated and .onMissile(missile) then
                                        call terminate()
                                        exitwhen true
                                    endif
                                endif
                            endif
                        endif
                    set k = k + 1
                endloop
            endif
        endif
    endmodule

    private module OnDestructable
        if .onDestructable.exists then
            if allocated and collision > 0 then
                set dx = collision
                call SetRect(rect, x - dx, y - dx, x + dx, y + dx)
                call EnumDestructablesInRect(rect, null, function thistype.onDest)
            endif
        endif
    endmodule

    private module OnItem
        if .onItem.exists then
            if allocated and collision > 0 then
                set dx = collision
                call SetRect(rect, x - dx, y - dx, x + dx, y + dx)
                call EnumItemsInRect(rect, null, function thistype.onItems)
            endif
        endif
    endmodule
       
    private module OnCliff
        if .onCliff.exists then
            set dx = GetTerrainCliffLevel(nextX, nextY)
            set dy = GetTerrainCliffLevel(x, y) 
            if dy < dx and z  < (dx - GetMapCliffLevel())*bj_CLIFFHEIGHT then
                if allocated and .onCliff() then
                    call terminate()
                endif
            endif
        endif
    endmodule
       
    private module OnTerrain
        if .onTerrain.exists then
            if GetLocZ(x, y) > z then
                if allocated and .onTerrain() then
                    call terminate()
                endif
            endif
        endif
    endmodule
    
    private module OnTileset
        if .onTileset.exists then
            set k = GetTerrainType(x, y)
            if k != tileset then
                if allocated and .onTileset(k) then
                    call terminate()
                endif
            endif
            set tileset = k
        endif
    endmodule
    
    private module OnPeriod
        if .onPeriod.exists then
            if allocated and .onPeriod() then
                call terminate()
            endif
        endif
    endmodule
    
    private module OnOrient
        // Homing or not
        set u = target
        if u != null and GetUnitTypeId(u) != 0 then
            call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + toZ)
            set dx = impact.x - nextX
            set dy = impact.y - nextY
            set a = Atan2(dy, dx)
            set travel = o.distance - SquareRoot(dx*dx + dy*dy)
        else
            set a = o.angle
            set target = null
        endif
        
        // turn rate
        if turn != 0 and not (Cos(cA-a) >= Cos(turn)) then
            if Sin(a-cA) >= 0 then
                set cA = cA + turn
            else
                set cA = cA - turn
            endif
        else
            set cA = a
        endif

        set vel = veloc*dilation
        set yaw = cA
        set s = travel + vel
        set veloc = veloc + acceleration
        set travel = s
        set pitch = o.alpha
        set prevX = x
        set prevY = y
        set prevZ = z
        set x = nextX
        set y = nextY
        set z = nextZ
        set nextX = x + vel*Cos(yaw)
        set nextY = y + vel*Sin(yaw)

        // arc calculation
        if h != 0 or o.slope != 0 then
            set nextZ = 4*h*s*(d-s)/(d*d) + o.slope*s + o.z
            set pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))
        endif
        
        // curve calculation
        if c != 0 then
            set dx = 4*c*s*(d-s)/(d*d)
            set a = yaw + bj_PI/2
            set x = x + dx*Cos(a)
            set y = y + dx*Sin(a)
            set yaw = yaw + Atan(-((4*c)*(2*s - d))/(d*d))
        endif
    endmodule
    
    private module OnFinish
        if s >= d - 0.0001 then
            set finished = true
            if .onFinish.exists then
                if allocated and .onFinish() then
                    call terminate()
                else
                    if travel > 0 and not paused then
                        call terminate()
                    endif
                endif
            else
                call terminate()
            endif
        else
            if not roll then
                call effect.orient(yaw, -pitch, 0)
            else
                call effect.orient(yaw, -pitch, Atan2(c, h))
            endif
        endif
    endmodule
    
    private module OnBoundaries
        if not effect.move(x, y, z) then
            if .onBoundaries.exists then
                if allocated and .onBoundaries() then
                    call terminate()
                endif
            endif
        else
            if dummy != null then
                call SetUnitX(dummy, x)
                call SetUnitY(dummy, y)
            endif
        endif
    endmodule
    
    private module OnPause
        set pid = pid + 1
        set pkey = pid
        set frozen[pid] = this
        
        if .onPause.exists then
            if allocated and .onPause() then
                call terminate()
            endif
        endif
    endmodule
    
    private module OnResume
        local thistype aux
        
        set paused = flag
        if not paused and pkey != -1 then
            set id = id + 1
            set missiles[id] = this
            set aux = frozen[pid]
            set aux.pkey = pkey
            set frozen[pkey] = frozen[pid]
            set pid = pid - 1
            set pkey = -1

            if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
                set dilation = (id + 1)/SWEET_SPOT
            else
                set dilation = 1.
            endif

            if id == 0 then
                call TimerStart(timer, PERIOD, true, function thistype.move)
            endif
            
            if .onResume.exists then
                if allocated and .onResume() then
                    call terminate()
                else
                    if finished then
                        call terminate()
                    endif
                endif
            else
                if finished then
                    call terminate()
                endif
            endif
        endif
    endmodule
    
    private module OnRemove
        local thistype aux
    
        if allocated and launched then
            set allocated = false
            
            if pkey != -1 then
                set aux = frozen[pid]
                set aux.pkey = pkey
                set frozen[pkey] = frozen[pid]
                set pid = pid - 1
                set pkey = -1
            endif
            
            if .onRemove.exists then
                call .onRemove()
            endif
            
            if dummy != null then
                call Pool.recycle(dummy)
            endif
            
            set aux = collection[count]
            set aux.index = index
            set collection[index] = collection[count]
            set count = count - 1
            set index = -1
            
            call origin.destroy()
            call impact.destroy()
            call effect.destroy()
            call reset()
            call FlushChildHashtable(table, this)
        endif
    endmodule
    
    private module Operators
        /* -------------------------- Model of the missile -------------------------- */
        method operator model= takes string fx returns nothing
            call DestroyEffect(effect.effect)
            set effect.path = fx
            set effect.effect = AddSpecialEffect(fx, origin.x, origin.y)
            call BlzSetSpecialEffectZ(effect.effect, origin.z)
            call BlzSetSpecialEffectYaw(effect.effect, cA)
        endmethod

        method operator model takes nothing returns string
            return effect.path
        endmethod
        
        /* ----------------------------- Curved movement ---------------------------- */
        method operator curve= takes real value returns nothing
            set open = Tan(value*bj_DEGTORAD)*origin.distance
        endmethod
        
        method operator curve takes nothing returns real
            return Atan(open/origin.distance)*bj_RADTODEG
        endmethod
        
        /* ----------------------------- Arced Movement ----------------------------- */
        method operator arc= takes real value returns nothing
            set height = Tan(value*bj_DEGTORAD)*origin.distance/4
        endmethod
        
        method operator arc takes nothing returns real
            return Atan(4*height/origin.distance)*bj_RADTODEG
        endmethod
        
        /* ------------------------------ Effect scale ------------------------------ */
        method operator scale= takes real value returns nothing
            set effect.size = value
            call effect.scale(effect.effect, value)
        endmethod

        method operator scale takes nothing returns real
            return effect.size
        endmethod

        /* ------------------------------ Missile Speed ----------------------------- */
        method operator speed= takes real newspeed returns nothing
            local real d = origin.distance
            local real s
            local real vel
        
            set veloc = newspeed*PERIOD
            set vel = veloc*dilation
            set s = travel + vel
            set nextX = x + vel*Cos(cA)
            set nextY = y + vel*Sin(cA)

            if height != 0 or origin.slope != 0 then
                set nextZ = 4*height*s*(d-s)/(d*d) + origin.slope*s + origin.z
                set z = nextZ
            endif
        endmethod

        method operator speed takes nothing returns real
            return veloc/PERIOD
        endmethod

        /* ------------------------------- Flight Time ------------------------------ */
        method operator duration= takes real flightTime returns nothing
            local real d = origin.distance
            local real s
            local real vel
        
            set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
            set time = flightTime
            set vel = veloc*dilation
            set s = travel + vel
            set nextX = x + vel*Cos(cA)
            set nextY = y + vel*Sin(cA)

            if height != 0 or origin.slope != 0 then
                set nextZ = 4*height*s*(d-s)/(d*d) + origin.slope*s + origin.z
                set z = nextZ
            endif
        endmethod
        
        method operator duration takes nothing returns real
            return time
        endmethod
        
        /* ------------------------------- Sight Range ------------------------------ */
        method operator vision= takes real sightRange returns nothing
            set sight = sightRange
            
            if dummy == null then
                if owner == null then
                    if source != null then
                        set dummy = Pool.retrieve(x, y, z, 0)
                        call SetUnitOwner(dummy, GetOwningPlayer(source), false)
                        call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
                    endif
                else
                    set dummy = Pool.retrieve(x, y, z, 0)
                    call SetUnitOwner(dummy, owner, false)
                    call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
                endif
            else
                call SetUnitOwner(dummy, owner, false)
                call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
            endif
        endmethod
        
        method operator vision takes nothing returns real
            return sight
        endmethod

        /* ------------------------------- Time Scale ------------------------------- */
        method operator timeScale= takes real newTimeScale returns nothing
            set effect.timeScale = newTimeScale
        endmethod
        
        method operator timeScale takes nothing returns real
            return effect.timeScale
        endmethod

        /* ---------------------------------- Alpha --------------------------------- */
        method operator alpha= takes integer newAlpha returns nothing
            set effect.alpha = newAlpha
        endmethod

        method operator alpha takes nothing returns integer
            return effect.alpha
        endmethod

        /* ------------------------------ Player Color ------------------------------ */
        method operator playerColor= takes integer playerId returns nothing
            set effect.playerColor = playerId
        endmethod

        method operator playerColor takes nothing returns integer
            return effect.playerColor
        endmethod

        /* -------------------------------- Animation ------------------------------- */
        method operator animation= takes integer animType returns nothing
            set effect.animation = animType
        endmethod

        method operator animation takes nothing returns integer
            return effect.animation
        endmethod
    endmodule
    
    private module Methods
        /* --------------------------- Bounce and Deflect --------------------------- */
        method bounce takes nothing returns nothing
            call origin.move(x, y, z - GetLocZ(x, y))
            
            set travel = 0
            set finished = false
        endmethod

        method deflect takes real tx, real ty, real tz returns nothing
            local real locZ = GetLocZ(x, y)
            
            set target = null
            set toZ = tz
            
            if z < locZ and .onTerrain.exists then
                set nextX = prevX
                set nextY = prevY
                set nextZ = prevZ
            endif
            
            call impact.move(tx, ty, tz)
            call origin.move(x, y, z - locZ)
            
            set travel = 0
            set finished = false
        endmethod
        
        method deflectTarget takes unit u returns nothing
            call deflect(GetUnitX(u), GetUnitY(u), toZ)
            set target = u
        endmethod

        /* ---------------------------- Flush hit targets --------------------------- */
        method flushAll takes nothing returns nothing
            call FlushChildHashtable(table, this)
        endmethod

        method flush takes widget w returns nothing
            if w != null then
                call RemoveSavedBoolean(table, this, GetHandleId(w))
            endif
        endmethod

        method hitted takes widget w returns boolean
            return HaveSavedBoolean(table, this, GetHandleId(w))
        endmethod

        /* ----------------------- Missile attachment methods ----------------------- */
        method attach takes string model, real dx, real dy, real dz, real scale returns effect
            return effect.attach(model, dx, dy, dz, scale)
        endmethod

        method detach takes effect attachment returns nothing
            if attachment != null then
                call effect.detach(attachment)
            endif
        endmethod

        /* ------------------------------ Missile Pause ----------------------------- */
        method pause takes boolean flag returns nothing
            implement OnResume
        endmethod

        /* ---------------------------------- Color --------------------------------- */
        method color takes integer red, integer green, integer blue returns nothing
            call effect.setColor(red, green, blue)
        endmethod
        
        /* ---------------------- Destructable collision method --------------------- */
        static method onDest takes nothing returns nothing
            local thistype this  = temp
            local destructable d = GetEnumDestructable()
            local real dz
            local real tz

            if not HaveSavedBoolean(table, this, GetHandleId(d)) then
                if collideZ then
                    set dz = GetLocZ(GetWidgetX(d), GetWidgetY(d))
                    set tz = GetDestructableOccluderHeight(d)
                    if dz + tz >= z - collision and dz <= z + collision then
                        call SaveBoolean(table, this, GetHandleId(d), true)
                        if allocated and .onDestructable(d) then
                            set d = null
                            call terminate()
                            return
                        endif
                    endif
                else
                    call SaveBoolean(table, this, GetHandleId(d), true)
                    if allocated and .onDestructable(d) then
                        set d = null
                        call terminate()
                        return
                    endif
                endif
            endif

            set d = null
        endmethod

        /* -------------------------- Item collision method ------------------------- */
        static method onItems takes nothing returns nothing
            local thistype this  = temp
            local item i = GetEnumItem()
            local real dz

            if not HaveSavedBoolean(table, this, GetHandleId(i)) then
                if collideZ then
                    set dz = GetLocZ(GetItemX(i), GetItemY(i))
                    if dz + ITEM_SIZE >= z - collision and dz <= z + collision then
                        call SaveBoolean(table, this, GetHandleId(i), true)
                        if allocated and .onItem(i) then
                            set i = null
                            call terminate()
                            return
                        endif
                    endif
                else
                    call SaveBoolean(table, this, GetHandleId(i), true)
                    if allocated and .onItem(i) then
                        set i = null
                        call terminate()
                        return
                    endif
                endif
            endif

            set i = null
        endmethod

        /* -------------------------------- Terminate ------------------------------- */
        method terminate takes nothing returns nothing
            implement OnRemove
        endmethod
    endmodule

    struct Missiles extends MissileEvents
        private static timer timer = CreateTimer()
        private static group group = CreateGroup()
        private static rect rect = Rect(0., 0., 0., 0.)
        private static hashtable table = InitHashtable()
        private static integer last = 0 
        private static thistype temp = 0
        private static integer id = -1
        private static integer pid = -1
        private static thistype array missiles
        private static thistype array frozen
        private static real dilation = 1
        
        readonly static thistype array collection
        readonly static integer count = -1
        
        private real     cA
        private real     height
        private real     open
        private real     toZ
        private real     time
        private real     sight
        private unit     dummy
        private integer  pkey
        private integer  index
        
        Coordinates      impact
        Coordinates      origin
        MissileEffect    effect
        
        readonly real    x
        readonly real    y
        readonly real    z
        readonly real    prevX
        readonly real    prevY
        readonly real    prevZ
        readonly real    nextX
        readonly real    nextY
        readonly real    nextZ
        readonly real    turn
        readonly real    veloc
        readonly real    travel
        readonly boolean launched
        readonly boolean allocated
        readonly boolean finished
        readonly boolean paused
        readonly integer tileset
        
        unit             source
        unit             target
        player           owner
        boolean          collideZ
        real             collision
        real             damage
        real             acceleration
        integer          data
        integer          type
        boolean          roll
        
        implement Operators
        implement Methods

        /* ------------------------------ Reset members ----------------------------- */
        private method reset takes nothing returns nothing
            set launched     = false
            set finished     = false
            set collideZ     = false
            set paused       = false
            set roll         = false
            set source       = null
            set target       = null
            set owner        = null
            set dummy        = null
            set open         = 0.
            set height       = 0.
            set veloc        = 0.
            set acceleration = 0.
            set collision    = 0.
            set damage       = 0.
            set travel       = 0.
            set turn         = 0.
            set time         = 0.
            set sight        = 0.
            set data         = 0
            set type         = 0
            set tileset      = 0
            set pkey         = -1
            set index        = -1
        endmethod

        /* -------------------------- Destroys the missile -------------------------- */
        private method remove takes integer i returns integer
            if paused then
                implement OnPause
            else
                implement OnRemove
            endif
            
            set missiles[i] = missiles[id]
            set id = id - 1

            if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
                set dilation = (id + 1)/SWEET_SPOT
            else
                set dilation = 1
            endif

            if id == -1 then
                call PauseTimer(timer)
            endif
            
            if not allocated then
                call deallocate()
            endif

            return i - 1
        endmethod
        
        /* ---------------------------- Missiles movement --------------------------- */
        private static method move takes nothing returns nothing
            local integer     j = 0
            local integer     i
            local integer     k
            local unit        u
            local real        a
            local real        d
            local real        s
            local real        h
            local real        c
            local real        dx
            local real        dy
            local real        vel
            local real        yaw
            local real        pitch
            local Missiles    missile
            local Coordinates o
            local thistype    this

            if SWEET_SPOT > 0 then
                set i = last
            else
                set i = 0
            endif
            
            loop
                exitwhen ((j >= SWEET_SPOT and SWEET_SPOT > 0) or j > id)
                    set this = missiles[i]
                    set temp = this
                    if allocated and not paused then
                        implement OnHit
                        implement OnMissile
                        implement OnDestructable
                        implement OnItem
                        implement OnCliff
                        implement OnTerrain
                        implement OnTileset
                        implement OnPeriod
                        implement OnOrient
                        implement OnFinish
                        implement OnBoundaries
                    else
                        set i = remove(i)
                        set j = j - 1
                    endif
                set i = i + 1
                set j = j + 1

                if i > id and SWEET_SPOT > 0 then
                    set i = 0
                endif
            endloop
            set last = i

            set u = null
        endmethod
        
        /* --------------------------- Launch the Missile --------------------------- */
        method launch takes nothing returns nothing
            if not launched and allocated then
                set launched = true
                set id = id + 1
                set missiles[id] = this
                set count = count + 1
                set index = count
                set collection[count] = this
                
                if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
                    set dilation = (id + 1)/SWEET_SPOT
                else
                    set dilation = 1.
                endif

                if id == 0 then
                    call TimerStart(timer, PERIOD, true, function thistype.move)
                endif
            endif
        endmethod

        /* --------------------------- Main Creator method -------------------------- */
        static method create takes real x, real y, real z, real toX, real toY, real toZ returns thistype
            local thistype this  = thistype.allocate()

            call .reset()
            set .origin = Coordinates.create(x, y, z)
            set .impact = Coordinates.create(toX, toY, toZ)
            set .effect = MissileEffect.create(x, y, origin.z)
            call Coordinates.link(origin, impact)
            set .allocated = true
            set .cA = origin.angle
            set .x = x
            set .y = y
            set .z = impact.z
            set .prevX = x
            set .prevY = y
            set .prevZ = impact.z
            set .nextX = x
            set .nextY = y
            set .nextZ = impact.z
            set .toZ = toZ
            
            return this
        endmethod
    endstruct
endlibrary
library Tenacity requires Indexer, Alloc
    /* ---------------------------------------- Tenacity v1.0 --------------------------------------- */
    // Intro
    //      This library intension in to introduce to warcraft an easy way to 
    //      manipulate the duration of crowd control on units.
    //
    // How it Works?
    //      Working in conjuction with the Crowd Control Library this library allows you to control the 
    //      duration of disables provided in the Crowd Control library. It work similar to the Tenacity
    //      status in League of Legends or the Status Resistence in Dota 2.
    //
    //      The are basically 3 types of tenacity: Normal (stacks multiplicatively), 
    //      Flat and Offset (stacks additively).The formula for calculation is:
    //          newDuration = (duration - Offset) * [(1 - value1)*(1 - value2)*...] * (1 - Flat)
    //
    //      The system also allow negative values for Tenacity, resulting in increased
    //      crowd control duration. Also note that tenacity will only work on CC applied through
    //      the Crowd Control API
    //
    // How to Import
    //      1. Copy the Indexer and Alloc libraries into your map
    //      2. Copy this library into your map and you are done
    /* ---------------------------------------- By Chopinski ---------------------------------------- */

    /* ---------------------------------------------------------------------------------------------- */
    /*                                            JASS API                                            */
    /* ---------------------------------------------------------------------------------------------- */
    function GetUnitTenacity takes unit u returns real
        return Tenacity.get(u, 0)
    endfunction

    function GetUnitTenacityFlat takes unit u returns real
        return Tenacity.get(u, 1)
    endfunction

    function GetUnitTenacityOffset takes unit u returns real
        return Tenacity.get(u, 2)
    endfunction

    function SetUnitTenacity takes unit u, real value returns nothing
        call Tenacity.Set(u, value, 0)
    endfunction

    function SetUnitTenacityFlat takes unit u, real value returns nothing
        call Tenacity.Set(u, value, 1)
    endfunction

    function SetUnitTenacityOffset takes unit u, real value returns nothing
        call Tenacity.Set(u, value, 2)
    endfunction

    function UnitAddTenacity takes unit u, real value returns nothing
        call Tenacity.add(u, value, 0)
    endfunction

    function UnitAddTenacityFlat takes unit u, real value returns nothing
        call Tenacity.add(u, value, 1)
    endfunction

    function UnitAddTenacityOffset takes unit u, real value returns nothing
        call Tenacity.add(u, value, 2)
    endfunction

    function UnitRemoveTenacity takes unit u, real value returns nothing
        call Tenacity.remove(u, value)
    endfunction

    function GetTenacityDuration takes unit u, real duration returns real
        return Tenacity.calculate(u, duration)
    endfunction 
    
    function RegisterTenacityUnit takes unit u returns Tenacity
        return Tenacity.register(u)
    endfunction

    function DisplayTenacityStatus takes unit u returns nothing
        call Tenacity.print(u)
    endfunction

    /* ---------------------------------------------------------------------------------------------- */
    /*                                             System                                             */
    /* ---------------------------------------------------------------------------------------------- */
    private module ListModule
        readonly thistype next
        readonly thistype prev

        method init takes nothing returns thistype
            set next = this
            set prev = this

            return this
        endmethod

        method push takes thistype node returns thistype
            set node.prev = prev
            set node.next = this
            set prev.next = node
            set prev = node

            return node
        endmethod

        method pop takes nothing returns nothing
            set prev.next = next
            set next.prev = prev
        endmethod
    endmodule
    
    private struct List extends array
        implement Alloc
        implement ListModule

        real tenacity
        real value
        integer size

        method destroy takes nothing returns nothing
            local thistype node = this.next
            
            loop
                exitwhen node == this
                    set node.value = 0
                    call node.pop()
                    call node.deallocate()
                set node = node.next
            endloop

            call deallocate()
        endmethod

        method insert takes real value returns thistype
            local thistype node = push(allocate())

            set node.value = value
            set size = size + 1
            call update()

            return node
        endmethod

        method remove takes real value returns nothing
            local thistype node = this.next
        
            loop
                exitwhen node == this
                    if node.value == value then
                        set size = size - 1
                        call node.pop()
                        call node.deallocate()
                        exitwhen true
                    endif
                set node = node.next
            endloop

            call update()
        endmethod

        method update takes nothing returns nothing
            local thistype node = this.next
            local integer i = 0

            if size > 0 then
                loop
                    exitwhen node == this
                        if i > 0 then
                            set tenacity = tenacity * (1 - node.value) 
                        else
                            set tenacity = 1 - node.value
                        endif
                        set i = i + 1
                    set node = node.next
                endloop
            endif
        endmethod

        static method create takes nothing returns thistype
            local thistype this = thistype(allocate()).init()

            set size = 0
            set tenacity = 0
            
            return this
        endmethod
    endstruct

    struct Tenacity
        private static thistype array struct

        private List list
        private real flat
        private real offset

        static method get takes unit source, integer types returns real
            local integer id = GetUnitUserData(source)
            local thistype this

            if struct[id] != 0 then
                set this = struct[id]
                
                if types == 0 then
                    if list.size > 0 then
                        return 1 - list.tenacity
                    else
                        return 0.
                    endif
                elseif types == 1 then
                    return flat
                else
                    return offset
                endif
            endif

            return 0.
        endmethod

        static method Set takes unit source, real value, integer types returns nothing
            local integer id = GetUnitUserData(source)
            local thistype this

            if struct[id] == 0 then
                set this = register(source)
            else
                set this = struct[id]
            endif

            if types == 0 then
                set list.tenacity = value
            elseif types == 1 then
                set flat = value
            else
                set offset = value
            endif
        endmethod

        static method add takes unit source, real value, integer types returns nothing
            local integer id = GetUnitUserData(source)
            local thistype this

            if value != 0 then
                if struct[id] == 0 then
                    set this = register(source)
                else
                    set this = struct[id]
                endif

                if types == 0 then
                    call list.insert(value)
                elseif types == 1 then
                    set flat = flat + value
                else
                    set offset = offset + value
                endif
            endif
        endmethod

        static method remove takes unit source, real value returns nothing
            local integer id = GetUnitUserData(source)
            local thistype this

            if value != 0 and struct[id] != 0 then
                set this = struct[id]
                call list.remove(value)
            endif
        endmethod

        static method calculate takes unit source, real duration returns real
            local integer id = GetUnitUserData(source)
            local thistype this

            if duration != 0 and struct[id] != 0 then
                set this = struct[id]

                if list.size > 0 then
                    set duration = (duration - offset) * list.tenacity * (1 - flat)
                else
                    set duration = (duration - offset) * (1 - flat)
                endif
                
                if duration <= 0 then
                    return 0.
                endif

                return duration
            endif

            return duration
        endmethod

        static method register takes unit source returns thistype
            local integer id = GetUnitUserData(source)
            local thistype this

            if struct[id] == 0 then
                set this = thistype.allocate()
                set list = List.create()
                set flat = 0
                set offset = 0
                set struct[GetUnitUserData(source)] = this
            endif

            return this
        endmethod

        static method print takes unit source returns nothing
            local integer id = GetUnitUserData(source)
            local thistype this
            local List node
            local integer i = 0
            local real array bonus

            if struct[id] != 0 then
                set this = struct[id]

                if list.size > 0 then
                    set node = list.next

                    loop
                        exitwhen node == list
                            set bonus[i] = node.value
                            set i = i + 1
                        set node = node.next
                    endloop
                endif

                call ClearTextMessages()
                call BJDebugMsg("Tenacity Status for " + GetUnitName(source))
                call BJDebugMsg("Tenacity List [" + R2S(bonus[0]) + " | " + R2S(bonus[1]) + " | " + R2S(bonus[2]) + " | " + R2S(bonus[3]) + " | " + R2S(bonus[4]) + " | " + R2S(bonus[5]) + " | ...] = " + R2S(get(source, 0)))
                call BJDebugMsg("Tenacity Flat [" + R2S(get(source, 1)) + "]")
                call BJDebugMsg("Tenacity Offset [" + R2S(get(source, 2)) + "]")
            endif
        endmethod

        private static method onDeindex takes nothing returns nothing
            local unit source = GetIndexUnit()
            local integer id = GetUnitUserData(source)
            local thistype this

            if struct[id] != 0 then
                set this = struct[id]
                
                call list.destroy()
                call deallocate()

                set struct[id] = 0
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterUnitDeindexEvent(function thistype.onDeindex)
        endmethod
    endstruct
endlibrary
library TenacityUtils requires Tenacity
    /* ------------------------------------- Tenacity Utils v1.0 ------------------------------------ */
    // Utility Library that include a few extra functions to deal with Tenacity
    /* ---------------------------------------- By Chopinski ---------------------------------------- */

    /* ---------------------------------------------------------------------------------------------- */
    /*                                            JASS API                                            */
    /* ---------------------------------------------------------------------------------------------- */
    function UnitAddTenacityTimed takes unit u, real value, real duration returns nothing
        call TenacityUtils.addTimed(u, value, duration, 0)
    endfunction

    function UnitAddTenacityFlatTimed takes unit u, real value, real duration returns nothing
        call TenacityUtils.addTimed(u, value, duration, 1)
    endfunction

    function UnitAddTenacityOffsetTimed takes unit u, real value, real duration returns nothing
        call TenacityUtils.addTimed(u, value, duration, 2)
    endfunction

    /* ---------------------------------------------------------------------------------------------- */
    /*                                             System                                             */
    /* ---------------------------------------------------------------------------------------------- */
    struct TenacityUtils extends Tenacity
        private static timer timer = CreateTimer()
        private static thistype array array
        private static integer key = -1
        private static real period = 0.03125000

        private unit unit
        private real value
        private integer type
        private real duration

        private method destroy takes nothing returns nothing
            if key == -1 then
                call PauseTimer(timer)
            endif

            set unit = null
            call deallocate()
        endmethod

        private static method onPeriod takes nothing returns nothing
            local thistype this
            local integer i = 0
            
            loop
                exitwhen i > key
                    set this = array[i]

                    if duration <= 0 then
                        if type == 0 then
                            call remove(unit, value)
                        else
                            call add(unit, -value, type)
                        endif

                        set array[i] = array[key]
                        set key = key - 1
                        set i = i - 1
                        call destroy()
                    endif
                    set duration = duration - period
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration, integer types returns nothing
            local thistype this = thistype.allocate()

            set unit = u
            set value = amount
            set type = types
            set .duration = duration
            set key = key + 1
            set array[key] = this

            call add(unit, value, type)
            
            if key == 0 then
                call TimerStart(timer, period, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
endlibrary
library GetMainSelectedUnit initializer init_function

globals
    private framehandle containerFrame
    private framehandle array frames
    private group Group = CreateGroup()
    private unit array units
    private integer unitsCount = 0
    private filterfunc filter
endglobals

function GetUnitOrderValue takes unit u returns integer
    //heroes use the handleId
    if IsUnitType(u, UNIT_TYPE_HERO) then
        return GetHandleId(u)
    else
    //units use unitCode
        return GetUnitTypeId(u)
    endif
endfunction

private function FilterFunction takes nothing returns boolean
    local unit u = GetFilterUnit()
    local real prio = BlzGetUnitRealField(u, UNIT_RF_PRIORITY)
    local boolean found = false
    local integer loopA = 1
    local integer loopB = 0
    // compare the current u with allready found, to place it in the right slot
    loop
        exitwhen loopA > unitsCount
        if BlzGetUnitRealField(units[loopA], UNIT_RF_PRIORITY) < prio then
            set unitsCount = unitsCount + 1
            set loopB = unitsCount
            loop
                exitwhen loopB <= loopA
                set units[loopB] = units[loopB - 1]
                set loopB = loopB - 1
            endloop
            set units[loopA] = u
            set found = true
            exitwhen true
        // equal prio and better colisions Value
        elseif BlzGetUnitRealField(units[loopA], UNIT_RF_PRIORITY) == prio and GetUnitOrderValue(units[loopA]) > GetUnitOrderValue(u) then
            set unitsCount = unitsCount + 1
            set loopB = unitsCount
            loop
                exitwhen loopB <= loopA
                set units[loopB] = units[loopB - 1]
                set loopB = loopB - 1
            endloop
            set units[loopA] = u
            set found = true
            exitwhen true
        endif
        set loopA = loopA + 1
    endloop
   
    // not found add it at the end
    if not found then
        set unitsCount = unitsCount + 1
        set units[unitsCount] = u
    endif

    set u = null
    return false
endfunction

    function GetSelectedUnitIndex takes nothing returns integer
        local integer i = 0
        // local player is in group selection?
        if BlzFrameIsVisible(containerFrame) then
            // find the first visible yellow Background Frame
            loop
                exitwhen i > 11
                if BlzFrameIsVisible(frames[i]) then
                    return i
                endif
                set i = i + 1
            endloop
        endif
        return -1
    endfunction  

    function GetMainSelectedUnit takes integer index returns unit
        if index >= 0 then
            call GroupEnumUnitsSelected(Group, GetLocalPlayer(), filter)
            set bj_groupRandomCurrentPick = units[index + 1]
            //clear table
            loop
                exitwhen unitsCount <= 0
                set units[unitsCount] = null
                set unitsCount = unitsCount - 1
            endloop
            return bj_groupRandomCurrentPick
        else
            call GroupEnumUnitsSelected(Group, GetLocalPlayer(), null)
            return FirstOfGroup(Group)
        endif
    endfunction

    // returns the local current main selected unit, using it in a sync gamestate relevant manner breaks the game.
    function GetMainSelectedUnitEx takes nothing returns unit
        return GetMainSelectedUnit(GetSelectedUnitIndex())
    endfunction

    private function init_functionAt0s takes nothing returns nothing
        local integer i = 0
        local framehandle console = BlzGetFrameByName("ConsoleUI", 0)
        local framehandle bottomUI = BlzFrameGetChild(console, 1)
        local framehandle groupframe = BlzFrameGetChild(bottomUI, 5)
        local framehandle buttonContainer
        //globals
        set containerFrame = BlzFrameGetChild(groupframe, 0)
        set Group = CreateGroup()
        // give this frames a handleId
        loop 
            exitwhen i >= BlzFrameGetChildrenCount(containerFrame) - 1
            set buttonContainer = BlzFrameGetChild(containerFrame, i)
            set frames[i] = BlzFrameGetChild(buttonContainer, 0)
            set i = i + 1
        endloop
        call DestroyTimer(GetExpiredTimer())
    endfunction

    private function init_function takes nothing returns nothing
        set filter = Filter(function FilterFunction)
        call TimerStart(CreateTimer(), 0, false, function init_functionAt0s)
    endfunction
endlibrary
library Interface requires RegisterPlayerUnitEvent, GetMainSelectedUnit
    /* --------------------------------------- Interface v1.4 --------------------------------------- */
    // Credits
    //      - Tasyen         - GetMainSelectedUnit
    //      - Magtheridon96  - RegisterPlayerUnitEvent
    /* ---------------------------------------- By Chopinski ---------------------------------------- */

    /* ---------------------------------------------------------------------------------------------- */
    /*                                          Configuration                                         */
    /* ---------------------------------------------------------------------------------------------- */
    globals
        // Set this to a texture to replace the default gold icon
        private constant string GOLD_ICON   = ""
        // Set this to a texture to replace the default lumber icon
        private constant string LUMBER_ICON = ""
        // When true and a unit that has "Select Unit" or "Select Hero" or "Shop Purchase Item" 
        // abilities is select a panel above the portrait is created to show the items/units
        private constant boolean DISPLAY_SHOP = true
    endglobals

    /* ---------------------------------------------------------------------------------------------- */
    /*                                             System                                             */
    /* ---------------------------------------------------------------------------------------------- */
    private struct UI
        private static trigger maptrigger = CreateTrigger()
        private static trigger herotrigger = CreateTrigger()
        private static trigger menutrigger = CreateTrigger()
        private static trigger trigger = CreateTrigger()
        private static timer timer = CreateTimer()

        private static integer key = -1
        private static thistype array array
        private static thistype array struct

        private static framehandle handle = null
        private static framehandle UI = null
        private static framehandle ShopSlots = null
        private static framehandle HealthBar = null
        private static framehandle ManaBar = null
        private static framehandle HeroCheck = null
        private static framehandle HPText = null
        private static framehandle MPText = null
        private static framehandle Gold = null
        private static framehandle Lumber = null
        private static framehandle CheckBL = null
        private static framehandle CheckBR = null
        private static framehandle Minimap = null
        private static framehandle MenuCheck = null
        private static framehandle LumberIcon = null
        private static framehandle GoldIcon = null

        private static real array x1
        private static real array x2
        private static real array y01
        private static real array y02
        private static real array y11
        private static real array y12
        private static real array y21
        private static real array y22
        private static real array y31
        private static real array y32
        private static real array y41
        private static real array y42
        private static real array y51
        private static real array y52
        private static real array y61
        private static real array y62

        private static real array mapX1
        private static real array mapY1
        private static real array mapX2
        private static real array mapY2

        private static real array frameX1
        private static real array frameY1
        private static real array frameX2
        private static real array frameY2

        private static real array command0X1
        private static real array command0Y1
        private static real array command0X2
        private static real array command0Y2
        private static real array command1X1
        private static real array command1Y1
        private static real array command1X2
        private static real array command1Y2
        private static real array command2X1
        private static real array command2Y1
        private static real array command2X2
        private static real array command2Y2
        private static real array command3X1
        private static real array command3Y1
        private static real array command3X2
        private static real array command3Y2
        private static real array command4X1
        private static real array command4Y1
        private static real array command4X2
        private static real array command4Y2
        private static real array command5X1
        private static real array command5Y1
        private static real array command5X2
        private static real array command5Y2
        private static real array command6X1
        private static real array command6Y1
        private static real array command6X2
        private static real array command6Y2
        private static real array command7X1
        private static real array command7Y1
        private static real array command7X2
        private static real array command7Y2
        private static real array command8X1
        private static real array command8Y1
        private static real array command8X2
        private static real array command8Y2
        private static real array command9X1
        private static real array command9Y1
        private static real array command9X2
        private static real array command9Y2
        private static real array command10X1
        private static real array command10Y1
        private static real array command10X2
        private static real array command10Y2
        private static real array command11X1
        private static real array command11Y1
        private static real array command11X2
        private static real array command11Y2

        private static boolean array shop
        private static unit array main

        private static boolean array checkL
        private static boolean array checkR

        private static boolean array checkMenu

        unit unit
        player player
        integer id
        real health
        real mana
        string hp
        string mp

        method remove takes integer i returns integer
            set array[i] = array[key]
            set key = key - 1
            set struct[id] = 0
            set unit = null
            set player = null

            if key == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        private static method onCommandButtons takes nothing returns nothing
            local integer id = GetPlayerId(GetLocalPlayer())

            if shop[id] then
                set command0X1[id] = 0.333500
                set command0Y1[id] = 0.213950
                set command0X2[id] = 0.366760
                set command0Y2[id] = 0.180700
                set command1X1[id] = 0.370500
                set command1Y1[id] = 0.213950
                set command1X2[id] = 0.403760
                set command1Y2[id] = 0.180700
                set command2X1[id] = 0.407400
                set command2Y1[id] = 0.213650
                set command2X2[id] = 0.440660
                set command2Y2[id] = 0.180400
                set command3X1[id] = 0.444400
                set command3Y1[id] = 0.213650
                set command3X2[id] = 0.477660
                set command3Y2[id] = 0.180400
                set command4X1[id] = 0.333500
                set command4Y1[id] = 0.175250
                set command4X2[id] = 0.366760
                set command4Y2[id] = 0.142000
                set command5X1[id] = 0.370500
                set command5Y1[id] = 0.175250
                set command5X2[id] = 0.403760
                set command5Y2[id] = 0.142000 
                set command6X1[id] = 0.407400
                set command6Y1[id] = 0.175250
                set command6X2[id] = 0.440660
                set command6Y2[id] = 0.142000
                set command7X1[id] = 0.444400
                set command7Y1[id] = 0.175250
                set command7X2[id] = 0.477660
                set command7Y2[id] = 0.142000
                set command8X1[id] = 0.333500
                set command8Y1[id] = 0.136850
                set command8X2[id] = 0.366760
                set command8Y2[id] = 0.103600
                set command9X1[id] = 0.370500
                set command9Y1[id] = 0.136850
                set command9X2[id] = 0.403760
                set command9Y2[id] = 0.103600
                set command10X1[id] = 0.407400
                set command10Y1[id] = 0.136850
                set command10X2[id] = 0.440660
                set command10Y2[id] = 0.103600
                set command11X1[id] = 0.444400
                set command11Y1[id] = 0.136850
                set command11X2[id] = 0.477660
                set command11Y2[id] = 0.103600
            else
                set command0X1[id] = 999.0
                set command0Y1[id] = 999.0
                set command0X2[id] = 999.0
                set command0Y2[id] = 999.0
                set command1X1[id] = 999.0
                set command1Y1[id] = 999.0
                set command1X2[id] = 999.0
                set command1Y2[id] = 999.0
                set command2X1[id] = 999.0
                set command2Y1[id] = 999.0
                set command2X2[id] = 999.0
                set command2Y2[id] = 999.0
                set command3X1[id] = 999.0
                set command3Y1[id] = 999.0
                set command3X2[id] = 999.0
                set command3Y2[id] = 999.0
                set command4X1[id] = 999.0
                set command4Y1[id] = 999.0
                set command4X2[id] = 999.0
                set command4Y2[id] = 999.0 
                set command5X1[id] = 0.186900
                set command5Y1[id] = 0.0467700
                set command5X2[id] = 0.216900
                set command5Y2[id] = 0.0150000
                set command6X1[id] = 0.223700
                set command6Y1[id] = 0.0467700
                set command6X2[id] = 0.254200
                set command6Y2[id] = 0.0150000
                set command7X1[id] = 0.00242900
                set command7Y1[id] = 0.0467700
                set command7X2[id] = 0.0342090
                set command7Y2[id] = 0.0150000
                set command8X1[id] = 0.0399070
                set command8Y1[id] = 0.0467700
                set command8X2[id] = 0.0716870
                set command8Y2[id] = 0.0150000
                set command9X1[id] = 0.0765555
                set command9Y1[id] = 0.0467700
                set command9X2[id] = 0.1095555
                set command9Y2[id] = 0.0150000
                set command10X1[id] = 0.113070
                set command10Y1[id] = 0.0467700
                set command10X2[id] = 0.144850
                set command10Y2[id] = 0.0150000
                set command11X1[id] = 0.150070
                set command11Y1[id] = 0.0467700
                set command11X2[id] = 0.181850
                set command11Y2[id] = 0.0150000
            endif

            // Display the 12 slot grid
            call BlzFrameSetVisible(ShopSlots, shop[id])

            // Reposition the Move command button
            set handle = BlzGetFrameByName("CommandButton_0", 0) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command0X1[id], command0Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command0X2[id], command0Y2[id]) 
            call BlzFrameSetScale(handle, 0.82)

            // Reposition the Stop command button
            set handle = BlzGetFrameByName("CommandButton_1", 0) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command1X1[id], command1Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command1X2[id], command1Y2[id])
            call BlzFrameSetScale(handle, 0.82)
            
            // Reposition the Hold command button
            set handle = BlzGetFrameByName("CommandButton_2", 0) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command2X1[id], command2Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command2X2[id], command2Y2[id]) 
            call BlzFrameSetScale(handle, 0.82)

            // Reposition the Attack command button
            set handle = BlzGetFrameByName("CommandButton_3", 0) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command3X1[id], command3Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command3X2[id], command3Y2[id])
            call BlzFrameSetScale(handle, 0.82)

            // Reposition the Patrol command button
            set handle = BlzGetFrameByName("CommandButton_4", 0) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command4X1[id], command4Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command4X2[id], command4Y2[id]) 
            call BlzFrameSetScale(handle, 0.82)

            // Reposition the D command button
            set handle = BlzGetFrameByName("CommandButton_5", 0) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command5X1[id], command5Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command5X2[id], command5Y2[id]) 
            call BlzFrameSetScale(handle, 0.82)
            
            // Reposition the F command button
            set handle = BlzGetFrameByName("CommandButton_6", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command6X1[id], command6Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command6X2[id], command6Y2[id]) 
            call BlzFrameSetScale(handle, 0.82)
            
            // Reposition the + command button
            set handle = BlzGetFrameByName("CommandButton_7", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command7X1[id], command7Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command7X2[id], command7Y2[id])
            call BlzFrameSetScale(handle, 0.82)
            
            // Reposition the Q command button
            set handle = BlzGetFrameByName("CommandButton_8", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command8X1[id], command8Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command8X2[id], command8Y2[id]) 
            call BlzFrameSetScale(handle, 0.82)
            
            // Reposition the W command button
            set handle = BlzGetFrameByName("CommandButton_9", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command9X1[id], command9Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command9X2[id], command9Y2[id])
            call BlzFrameSetScale(handle, 0.82)
            
            // Reposition the E command button
            set handle = BlzGetFrameByName("CommandButton_10", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command10X1[id], command10Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command10X2[id], command10Y2[id]) 
            call BlzFrameSetScale(handle, 0.82)
            
            // Reposition the R command button
            set handle = BlzGetFrameByName("CommandButton_11", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, command11X1[id], command11Y1[id]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, command11X2[id], command11Y2[id])
            call BlzFrameSetScale(handle, 0.82)

            set handle = null
        endmethod

        private static method onInventoryButtons takes nothing returns nothing
            // Reposition the 0 inventory button
            set handle = BlzGetFrameByName("InventoryButton_0", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.552700, 0.0467700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.584480, 0.0150000) 
            call BlzFrameSetSize(handle, 0.03178, 0.03178)
            
            // Reposition the 1 inventory button
            set handle = BlzGetFrameByName("InventoryButton_1", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.589100, 0.0467700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.621900, 0.0150000) 
            call BlzFrameSetSize(handle, 0.03178, 0.03178)
            
            // Reposition the 2 inventory button
            set handle = BlzGetFrameByName("InventoryButton_2", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.625750, 0.0467700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.657530, 0.0150000)
            call BlzFrameSetSize(handle, 0.03178, 0.03178)
            
            // Reposition the 3 inventory button
            set handle = BlzGetFrameByName("InventoryButton_3", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.662999, 0.0467700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.694999, 0.0150000)
            call BlzFrameSetSize(handle, 0.03178, 0.03178)
            
            // Reposition the 4 inventory button
            set handle = BlzGetFrameByName("InventoryButton_4", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.699700, 0.0467700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.731480, 0.0150000) 
            call BlzFrameSetSize(handle, 0.03178, 0.03178)
            
            // Reposition the 5 inventory button
            set handle = BlzGetFrameByName("InventoryButton_5", 0) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.736555, 0.0467700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.768555, 0.0150000)
            call BlzFrameSetSize(handle, 0.03178, 0.03178)
            
            set handle = null
        endmethod

        private static method onInfoPanel takes nothing returns nothing
            // Reposition the Buff bar
            set handle = BlzGetOriginFrame(ORIGIN_FRAME_UNIT_PANEL_BUFF_BAR, 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.364600, 0.0280800) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.476200, 0.0147800)  
            call BlzFrameSetScale(handle, 0.9)
            
            //Remove the Status text
            call BlzFrameSetScale(BlzGetOriginFrame(ORIGIN_FRAME_UNIT_PANEL_BUFF_BAR_LABEL, 0), 0.00001)
            
            // Remove Names and Descriptions
            call BlzFrameSetScale(BlzGetFrameByName("SimpleNameValue", 0), 0.00001)
            call BlzFrameSetScale(BlzGetFrameByName("SimpleClassValue", 0), 0.00001)
            call BlzFrameSetScale(BlzGetFrameByName("SimpleBuildingNameValue", 1), 0.00001)
            call BlzFrameSetScale(BlzGetFrameByName("SimpleBuildingActionLabel", 1), 0.00001)
            call BlzFrameSetScale(BlzGetFrameByName("SimpleHoldNameValue", 2), 0.00001)
            call BlzFrameSetScale(BlzGetFrameByName("SimpleHoldDescriptionNameValue", 2), 0.00001)
            call BlzFrameSetScale(BlzGetFrameByName("SimpleItemNameValue", 3), 0.00001)
            call BlzFrameSetScale(BlzGetFrameByName("SimpleItemDescriptionValue", 3), 0.00001)
            call BlzFrameSetScale(BlzGetFrameByName("SimpleDestructableNameValue", 4), 0.00001)
            
            // Reposition the Hero Main Stat
            set handle = BlzGetFrameByName("InfoPanelIconHeroIcon", 6)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.449800, 0.0581100) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.474930, 0.0329900)

            // Reposition the Strength label and value
            set handle = BlzGetFrameByName("InfoPanelIconHeroStrengthLabel", 6)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.476900, 0.0757800) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.530850, 0.0624800) 
            set handle = BlzGetFrameByName("InfoPanelIconHeroStrengthValue", 6) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.480000, 0.0657200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.550000, 0.0553800) 

            // Reposition the Agility label and value
            set handle = BlzGetFrameByName("InfoPanelIconHeroAgilityLabel", 6)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.477400, 0.0559200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.532090, 0.0426200) 
            set handle = BlzGetFrameByName("InfoPanelIconHeroAgilityValue", 6) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.480300, 0.0445700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.550000, 0.0342300) 

            // Reposition the Intelligence label and value
            set handle = BlzGetFrameByName("InfoPanelIconHeroIntellectLabel", 6)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.476900, 0.0346500) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.531590, 0.0213500) 
            set handle = BlzGetFrameByName("InfoPanelIconHeroIntellectValue", 6) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.480600, 0.0240700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.550000, 0.0137300)  

            // Reposition the Timed Life bar
            set handle = BlzGetFrameByName("SimpleProgressIndicator", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.00000, 0.0100000) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.770000, 0.00000)
            call BlzFrameSetSize(handle, 0.77000, 0.01000)
            
            // Reposition the XP bar
            set handle = BlzGetFrameByName("SimpleHeroLevelBar", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.00000, 0.0100000) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.770000, 0.00000)
            call BlzFrameSetSize(handle, 0.77000, 0.01000)

            // Reposition the Training bar
            set handle = BlzGetFrameByName("SimpleBuildTimeIndicator", 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.00000, 0.0100000) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.770000, 0.00000)
            call BlzFrameSetSize(handle, 0.77000, 0.01000)

            // Reposition the Attack 1 block
            set handle = BlzGetFrameByName("InfoPanelIconBackdrop", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.261800, 0.0723200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.289140, 0.0449800) 
            call BlzFrameSetSize(handle, 0.02734, 0.02734)
            
            // Reposition the Armor block
            set handle = BlzGetFrameByName("InfoPanelIconBackdrop", 2)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.261100, 0.0439700) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.288440, 0.0166300) 
            call BlzFrameSetSize(handle, 0.02734, 0.02734)
            
            set handle = null
        endmethod

        private static method onPortrait takes nothing returns nothing
            set handle = BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT, 0)

            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.373500, 0.0977600) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.431555, 0.0157400)

            set handle = null
        endmethod

        private static method onHeroCheck takes nothing returns nothing
            local integer i = GetPlayerId(GetLocalPlayer())

            if BlzGetTriggerFrameEvent() == FRAMEEVENT_CHECKBOX_CHECKED then
                if GetLocalPlayer() == GetTriggerPlayer() then
                    set x1[i] = -0.131300
                    set x2[i] = -0.103220 
                    set y01[i] = 0.581980
                    set y02[i] = 0.553900
                    set y11[i] = 0.544980
                    set y12[i] = 0.516900
                    set y21[i] = 0.510680
                    set y22[i] = 0.482600
                    set y31[i] = 0.474280
                    set y32[i] = 0.446200
                    set y41[i] = 0.437880
                    set y42[i] = 0.409800
                    set y51[i] = 0.401480
                    set y52[i] = 0.373400
                    set y61[i] = 0.365080
                    set y62[i] = 0.337000
                endif

                // Reposition the hero button 0
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 0)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y01[i])
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y02[i])
                call BlzFrameSetScale(handle, 0.7)
                call BlzFrameSetScale(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON_INDICATOR, 0), 0.71)

                // Reposition the hero button 1
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 1)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y11[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y12[i])
                call BlzFrameSetScale(handle, 0.7)
                call BlzFrameSetScale(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON_INDICATOR, 1), 0.71)

                // Reposition the hero button 2
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 2)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y21[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y22[i])  
                call BlzFrameSetScale(handle, 0.7)
                call BlzFrameSetScale(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON_INDICATOR, 2), 0.71)

                // Reposition the hero button 3
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 3)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y31[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y32[i]) 
                call BlzFrameSetScale(handle, 0.7)
                call BlzFrameSetScale(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON_INDICATOR, 3), 0.71)

                // Reposition the hero button 4
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 4)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y41[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y42[i]) 
                call BlzFrameSetScale(handle, 0.7)
                call BlzFrameSetScale(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON_INDICATOR, 4), 0.71)

                // Reposition the hero button 5
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 5)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y51[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y52[i])  
                call BlzFrameSetScale(handle, 0.7)
                call BlzFrameSetScale(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON_INDICATOR, 5), 0.71)

                // Reposition the hero button 6
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 6)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y61[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y62[i]) 
                call BlzFrameSetScale(handle, 0.7)
                call BlzFrameSetScale(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON_INDICATOR, 6), 0.71)

                set handle = null
            else
                if GetLocalPlayer() == GetTriggerPlayer() then
                    set x1[i] = 999.0
                    set x2[i] = 999.0
                    set y01[i] = 999.0
                    set y02[i] = 999.0
                    set y11[i] = 999.0
                    set y12[i] = 999.0
                    set y21[i] = 999.0
                    set y22[i] = 999.0
                    set y31[i] = 999.0
                    set y32[i] = 999.0
                    set y41[i] = 999.0
                    set y42[i] = 999.0
                    set y51[i] = 999.0
                    set y52[i] = 999.0
                    set y61[i] = 999.0
                    set y62[i] = 999.0
                endif

                // Hides the hero button 0
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 0)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y01[i])
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y02[i])

                // Hides the hero button 1
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 1)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y11[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y12[i])

                // Hides the hero button 2
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 2)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y21[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y22[i])

                // Hides the hero button 3
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 3)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y31[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y32[i]) 

                // Hides the hero button 4
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 4)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y41[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y42[i]) 

                // Hides the hero button 5
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 5)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y51[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y52[i])

                // Hides the hero button 6
                set handle = BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 6)
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, x1[i], y61[i]) 
                call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, x2[i], y62[i])

                set handle = null
            endif
        endmethod

        private static method onGroupSelection takes nothing returns nothing
            // Reposistion the Group selection button 0
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 0), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.262600, 0.0776200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.285600, 0.0546200) 

            // Reposistion the Group selection button 1
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 1), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.295800, 0.0731200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.318800, 0.0501200)

            // Reposistion the Group selection button 2
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 2), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.328300, 0.0731200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.351300, 0.0501200)  

            // Reposistion the Group selection button 3
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 3), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.262600, 0.0414100) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.285600, 0.0184100)
            
            // Reposistion the Group selection button 4
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 4), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.295800, 0.0414000) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.318800, 0.0184000) 

            // Reposistion the Group selection button 5
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 5), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.329100, 0.0414000) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.352100, 0.0184000) 

            // Reposistion the Group selection button 6
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 6), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.449300, 0.0731200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.472300, 0.0501200) 

            // Reposistion the Group selection button 7
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 7), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.483500, 0.0731200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.506500, 0.0501200)  

            // Reposistion the Group selection button 8
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 8), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.516800, 0.0731200) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.539800, 0.0501200) 

            // Reposistion the Group selection button 9
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 9), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.450300, 0.0414000) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.473300, 0.0184000)

            // Reposistion the Group selection button 10
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 10), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.483500, 0.0414000) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.506500, 0.0184000)  

            // Reposistion the Group selection button 11
            set handle = BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetChild(BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0)), 5), 0), 11), 1)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.516800, 0.0414000) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.539800, 0.0184000)  

            set handle = null
        endmethod

        private static method onResources takes nothing returns nothing
            call BlzFrameSetText(Gold, "|cffffcc00" + I2S(GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_GOLD)) + "|r")
            call BlzFrameSetText(Lumber, "|cff00ff00" + I2S(GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_LUMBER)) + "|r")
        endmethod

        private static method onChat takes nothing returns nothing
            set handle = BlzGetOriginFrame(ORIGIN_FRAME_CHAT_MSG, 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.000212200, 0.302800) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.400212, 0.100300) 

            set handle = BlzGetOriginFrame(ORIGIN_FRAME_UNIT_MSG, 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, 0.000212200, 0.302800) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, 0.400212, 0.100300)  

            set handle = null
        endmethod

        private static method onMinimap takes nothing returns nothing
            local integer i = GetPlayerId(GetLocalPlayer())

            if BlzGetTriggerFrameEvent() == FRAMEEVENT_CHECKBOX_CHECKED then
                if BlzGetTriggerFrame() == CheckBL then
                    if GetLocalPlayer() == GetTriggerPlayer() then
                        set mapX1[i] = - 0.132100
                        set mapY1[i] = 0.0986970
                        set mapX2[i] = - 0.0351000
                        set mapY2[i] = 0.00169700
                        set frameX1[i] = - 0.133600
                        set frameY1[i] = 0.100939
                        set frameX2[i] = - 0.0338300
                        set frameY2[i] = 0.000438700
                        set checkL[i] = true
                    endif
                else
                    if GetLocalPlayer() == GetTriggerPlayer() then
                        set mapX1[i] = 0.835800
                        set mapY1[i] = 0.0999999
                        set mapX2[i] = 0.933610
                        set mapY2[i] = 0.000219400
                        set frameX1[i] = 0.833900
                        set frameY1[i] = 0.100939
                        set frameX2[i] = 0.933670
                        set frameY2[i] = 0.000438700
                        set checkR[i] = true
                    endif
                endif
            else
                if BlzGetTriggerFrame() == CheckBL then
                    if GetLocalPlayer() == GetTriggerPlayer() then
                        if checkR[i] then
                            set mapX1[i] = 0.835800
                            set mapY1[i] = 0.0999999
                            set mapX2[i] = 0.933610
                            set mapY2[i] = 0.000219400
                            set frameX1[i] = 0.833900
                            set frameY1[i] = 0.100939
                            set frameX2[i] = 0.933670
                            set frameY2[i] = 0.000438700
                        else
                            set mapX1[i] = 999.0
                            set mapY1[i] = 999.0
                            set mapX2[i] = 999.0
                            set mapY2[i] = 999.0
                            set frameX1[i] = 999.0
                            set frameY1[i] = 999.0
                            set frameX2[i] = 999.0
                            set frameY2[i] = 999.0
                        endif
                        set checkL[i] = false
                    endif
                else
                    if GetLocalPlayer() == GetTriggerPlayer() then
                        if checkL[i] then
                            set mapX1[i] = - 0.132100
                            set mapY1[i] = 0.0986970
                            set mapX2[i] = - 0.0351000
                            set mapY2[i] = 0.00169700
                            set frameX1[i] = - 0.133600
                            set frameY1[i] = 0.100939
                            set frameX2[i] = - 0.0338300
                            set frameY2[i] = 0.000438700
                        else
                            set mapX1[i] = 999.0
                            set mapY1[i] = 999.0
                            set mapX2[i] = 999.0
                            set mapY2[i] = 999.0
                            set frameX1[i] = 999.0
                            set frameY1[i] = 999.0
                            set frameX2[i] = 999.0
                            set frameY2[i] = 999.0
                        endif
                        set checkR[i] = false
                    endif
                endif
            endif

            set handle = BlzGetFrameByName("MiniMapFrame", 0)
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_TOPLEFT, mapX1[i], mapY1[i]) 
            call BlzFrameSetAbsPoint(handle, FRAMEPOINT_BOTTOMRIGHT, mapX2[i], mapY2[i])
            call BlzFrameSetAbsPoint(Minimap, FRAMEPOINT_TOPLEFT, frameX1[i], frameY1[i]) 
            call BlzFrameSetAbsPoint(Minimap, FRAMEPOINT_BOTTOMRIGHT, frameX2[i], frameY2[i]) 

            set handle = null
        endmethod

        private static method onMenu takes nothing returns nothing
            local integer i = GetPlayerId(GetLocalPlayer())

            if BlzGetTriggerFrameEvent() == FRAMEEVENT_CHECKBOX_CHECKED then
                if GetLocalPlayer() == GetTriggerPlayer() then
                    set checkMenu[i] = true
                endif
            else
                if GetLocalPlayer() == GetTriggerPlayer() then
                    set checkMenu[i] = false
                endif
            endif

            call BlzFrameSetVisible(BlzGetFrameByName("UpperButtonBarFrame", 0), checkMenu[i])
            call BlzFrameSetVisible(BlzGetFrameByName("ResourceBarFrame", 0), checkMenu[i])
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer i = 0
            local real newHP
            local real newMP
            local string newHptext
            local string newMptext
            local thistype this

            loop
                exitwhen i > key
                    set this = array[i]

                    if GetPlayerSlotState(player) != PLAYER_SLOT_STATE_LEFT then
                        set unit = GetMainSelectedUnitEx()

                        static if DISPLAY_SHOP then
                            if main[id] != unit then
                                set main[id] = unit
                                set shop[id] = (GetUnitAbilityLevel(unit, 'Aneu') > 0 or GetUnitAbilityLevel(unit, 'Ane2') > 0 or GetUnitAbilityLevel(unit, 'Apit') > 0) and not IsUnitEnemy(unit, player)
                                call onCommandButtons()
                            endif
                        endif

                        if not IsUnitVisible(unit, player) then
                            set unit = null
                        endif

                        set health = BlzFrameGetValue(HealthBar) 
                        set mana = BlzFrameGetValue(ManaBar)
                        set newHP = GetUnitLifePercent(unit)
                        set newMP = GetUnitManaPercent(unit)
                        set hp = BlzFrameGetText(HPText)
                        set mp = BlzFrameGetText(MPText)
                        set newHptext = I2S(R2I(GetWidgetLife(unit))) + " / " + I2S(BlzGetUnitMaxHP(unit))
                        set newMptext = I2S(R2I(GetUnitState(unit,  UNIT_STATE_MANA))) + " / " + I2S(BlzGetUnitMaxMana(unit))

                        if GetLocalPlayer() == player then
                            set health = newHP
                            set mana = newMP
                            set hp = newHptext
                            set mp = newMptext
                        endif

                        call BlzFrameSetValue(HealthBar, health)
                        call BlzFrameSetValue(ManaBar, mana)
                        call BlzFrameSetText(HPText, "|cffFFFFFF" + hp + "|r")
                        call BlzFrameSetText(MPText, "|cffFFFFFF" + mp + "|r")
                    else
                        set i = remove(i)
                    endif
                set i = i + 1
            endloop
        endmethod

        private static method onSelect takes nothing returns nothing
            local integer id = GetPlayerId(GetTriggerPlayer())
            local thistype this

            if struct[id] != 0 then
                set this = struct[id]
            else
                set this = thistype.allocate()
                set .id = id
                set player = GetTriggerPlayer()
                set health = 0
                set mana = 0
                set hp = "0 / 0"
                set mp = "0 / 0"
                set key = key + 1
                set array[key] = this
                set struct[id] = this
                
                if key == 0 then
                    call TimerStart(timer, 0.05, true, function thistype.onPeriod)
                endif
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            local integer i = 0

            call BlzFrameSetAlpha(BlzGetFrameByName("SimpleInventoryCover", 0), 0)
            call BlzFrameSetScale(BlzGetFrameByName("InventoryText", 0), 0.0001)
            call BlzFrameSetAbsPoint(BlzGetFrameByName("ConsoleUI", 0), FRAMEPOINT_TOPLEFT, 0.0, 0.633)
            call BlzFrameSetVisible(BlzGetFrameByName("ResourceBarFrame", 0), false)
            call BlzFrameSetVisible(BlzGetFrameByName("UpperButtonBarFrame", 0), false)
            call BlzFrameSetVisible(BlzFrameGetChild(BlzGetFrameByName("ConsoleUI", 0), 7), false)
            call BlzFrameSetVisible(BlzFrameGetChild(BlzFrameGetChild(BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 5),0), false)
            call BlzFrameSetParent(BlzGetFrameByName("MiniMapFrame", 0), BlzGetFrameByName("ConsoleUIBackdrop", 0))

            set UI = BlzCreateFrameByType("BACKDROP", "UI", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 1) 
            call BlzFrameSetAbsPoint(UI, FRAMEPOINT_TOPLEFT, 0.00000, 0.100000) 
            call BlzFrameSetAbsPoint(UI, FRAMEPOINT_BOTTOMRIGHT, 0.770000, 0.00000) 
            call BlzFrameSetTexture(UI, "UI.blp", 0, true) 

            set ShopSlots = BlzCreateFrameByType("BACKDROP", "ShopSlots", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 1) 
            call BlzFrameSetAbsPoint(ShopSlots, FRAMEPOINT_TOPLEFT, 0.330600, 0.216500) 
            call BlzFrameSetAbsPoint(ShopSlots, FRAMEPOINT_BOTTOMRIGHT, 0.478600, 0.100700) 
            call BlzFrameSetTexture(ShopSlots, "12Slot.blp", 0, true)

            set HealthBar = BlzCreateFrameByType("SIMPLESTATUSBAR", "", UI, "", 0) 
            call BlzFrameSetTexture(HealthBar, "replaceabletextures\\teamcolor\\teamcolor00", 0, true) 
            call BlzFrameSetAbsPoint(HealthBar, FRAMEPOINT_TOPLEFT, 0.0386400, 0.0778900) 
            call BlzFrameSetAbsPoint(HealthBar, FRAMEPOINT_BOTTOMRIGHT, 0.255140, 0.0535100) 
            call BlzFrameSetValue(HealthBar, 0) 

            set ManaBar = BlzCreateFrameByType("SIMPLESTATUSBAR", "", UI, "", 0) 
            call BlzFrameSetTexture(ManaBar, "replaceabletextures\\teamcolor\\teamcolor01", 0, true) 
            call BlzFrameSetAbsPoint(ManaBar, FRAMEPOINT_TOPLEFT, 0.551500, 0.0778000) 
            call BlzFrameSetAbsPoint(ManaBar, FRAMEPOINT_BOTTOMRIGHT, 0.768000, 0.0534200)
            call BlzFrameSetValue(ManaBar, 0)  

            set HeroCheck = BlzCreateFrame("QuestCheckBox", UI, 0, 0) 
            call BlzFrameSetAbsPoint(HeroCheck, FRAMEPOINT_TOPLEFT, -0.131300, 0.600240) 
            call BlzFrameSetAbsPoint(HeroCheck, FRAMEPOINT_BOTTOMRIGHT, -0.117260, 0.586200)

            set HPText = BlzCreateFrameByType("TEXT", "HPTEXT", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "", 0) 
            call BlzFrameSetAbsPoint(HPText, FRAMEPOINT_TOPLEFT, 0.108100, 0.0726300) 
            call BlzFrameSetAbsPoint(HPText, FRAMEPOINT_BOTTOMRIGHT, 0.184960, 0.0585900) 
            call BlzFrameSetText(HPText, "|cffFFFFFF|r") 
            call BlzFrameSetEnable(HPText, false) 
            call BlzFrameSetScale(HPText, 1.00) 
            call BlzFrameSetTextAlignment(HPText, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_MIDDLE) 

            set MPText = BlzCreateFrameByType("TEXT", "MPTEXT", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "", 0) 
            call BlzFrameSetAbsPoint(MPText, FRAMEPOINT_TOPLEFT, 0.622500, 0.0726000) 
            call BlzFrameSetAbsPoint(MPText, FRAMEPOINT_BOTTOMRIGHT, 0.699360, 0.0585600) 
            call BlzFrameSetText(MPText, "|cffFFFFFF|r") 
            call BlzFrameSetEnable(MPText, false) 
            call BlzFrameSetScale(MPText, 1.00) 
            call BlzFrameSetTextAlignment(MPText, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_MIDDLE) 

            set Lumber = BlzCreateFrameByType("TEXT", "GOLD", UI, "", 0) 
            call BlzFrameSetAbsPoint(Lumber, FRAMEPOINT_TOPLEFT, 0.291100, 0.0970200) 
            call BlzFrameSetAbsPoint(Lumber, FRAMEPOINT_BOTTOMRIGHT, 0.346530, 0.0815000) 
            call BlzFrameSetText(Lumber, "|cff00ff00|r") 
            call BlzFrameSetEnable(Lumber, false) 
            call BlzFrameSetScale(Lumber, 1.00) 
            call BlzFrameSetTextAlignment(Lumber, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_RIGHT) 

            set Gold = BlzCreateFrameByType("TEXT", "LUMBER", UI, "", 0) 
            call BlzFrameSetAbsPoint(Gold, FRAMEPOINT_TOPLEFT, 0.460600, 0.0972500) 
            call BlzFrameSetAbsPoint(Gold, FRAMEPOINT_BOTTOMRIGHT, 0.516030, 0.0817300) 
            call BlzFrameSetText(Gold, "|cffffcc00|r") 
            call BlzFrameSetEnable(Gold, false) 
            call BlzFrameSetScale(Gold, 1.00) 
            call BlzFrameSetTextAlignment(Gold, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_LEFT) 

            set CheckBL = BlzCreateFrame("QuestCheckBox", UI, 0, 0) 
            call BlzFrameSetAbsPoint(CheckBL, FRAMEPOINT_TOPLEFT, 0.269200, 0.102200) 
            call BlzFrameSetAbsPoint(CheckBL, FRAMEPOINT_BOTTOMRIGHT, 0.292850, 0.0778100) 

            set CheckBR = BlzCreateFrame("QuestCheckBox", UI, 0, 0) 
            call BlzFrameSetAbsPoint(CheckBR, FRAMEPOINT_TOPLEFT, 0.514800, 0.102200) 
            call BlzFrameSetAbsPoint(CheckBR, FRAMEPOINT_BOTTOMRIGHT, 0.538450, 0.0778100)

            set Minimap = BlzCreateFrameByType("BACKDROP", "Minimap", UI, "", 1) 
            call BlzFrameSetAbsPoint(Minimap, FRAMEPOINT_TOPLEFT, 999.0, 999.0) 
            call BlzFrameSetAbsPoint(Minimap, FRAMEPOINT_BOTTOMRIGHT, 999.0, 999.0) 
            call BlzFrameSetTexture(Minimap, "Minimap.blp", 0, true) 

            set MenuCheck = BlzCreateFrame("QuestCheckBox", UI, 0, 0) 
            call BlzFrameSetAbsPoint(MenuCheck, FRAMEPOINT_TOPLEFT, 0.918800, 0.601640) 
            call BlzFrameSetAbsPoint(MenuCheck, FRAMEPOINT_BOTTOMRIGHT, 0.932840, 0.587600) 

            set LumberIcon = BlzCreateFrameByType("BACKDROP", "LumberIcon", UI, "", 1) 
            call BlzFrameSetAbsPoint(LumberIcon, FRAMEPOINT_TOPLEFT, 0.347600, 0.0966800) 
            call BlzFrameSetAbsPoint(LumberIcon, FRAMEPOINT_BOTTOMRIGHT, 0.362600, 0.0816800)

            if LUMBER_ICON != "" then
                call BlzFrameSetTexture(LumberIcon, LUMBER_ICON, 0, true)
            else
                call BlzFrameSetVisible(LumberIcon, false)
            endif

            set GoldIcon = BlzCreateFrameByType("BACKDROP", "GoldIcon", UI, "", 1) 
            call BlzFrameSetAbsPoint(GoldIcon, FRAMEPOINT_TOPLEFT, 0.445900, 0.0966600) 
            call BlzFrameSetAbsPoint(GoldIcon, FRAMEPOINT_BOTTOMRIGHT, 0.460900, 0.0816600) 

            if GOLD_ICON != "" then
                call BlzFrameSetTexture(GoldIcon, GOLD_ICON, 0, true)
            else
                call BlzFrameSetVisible(GoldIcon, false)
            endif
            
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SELECTED, function thistype.onSelect)
            call BlzTriggerRegisterFrameEvent(herotrigger, HeroCheck, FRAMEEVENT_CHECKBOX_CHECKED) 
            call BlzTriggerRegisterFrameEvent(herotrigger, HeroCheck, FRAMEEVENT_CHECKBOX_UNCHECKED) 
            call TriggerAddAction(herotrigger, function thistype.onHeroCheck)
            call BlzTriggerRegisterFrameEvent(maptrigger, CheckBL, FRAMEEVENT_CHECKBOX_CHECKED) 
            call BlzTriggerRegisterFrameEvent(maptrigger, CheckBL, FRAMEEVENT_CHECKBOX_UNCHECKED)
            call BlzTriggerRegisterFrameEvent(maptrigger, CheckBR, FRAMEEVENT_CHECKBOX_CHECKED) 
            call BlzTriggerRegisterFrameEvent(maptrigger, CheckBR, FRAMEEVENT_CHECKBOX_UNCHECKED)
            call TriggerAddAction(maptrigger, function thistype.onMinimap)
            call BlzTriggerRegisterFrameEvent(menutrigger, MenuCheck, FRAMEEVENT_CHECKBOX_CHECKED) 
            call BlzTriggerRegisterFrameEvent(menutrigger, MenuCheck, FRAMEEVENT_CHECKBOX_UNCHECKED)
            call TriggerAddAction(menutrigger, function thistype.onMenu)
            call TimerStart(CreateTimer(), 0.2, true, function thistype.onResources) 

            loop
                exitwhen i > bj_MAX_PLAYER_SLOTS
                    set x1[i] = 999.0
                    set x2[i] = 999.0
                    set y01[i] = 999.0
                    set y02[i] = 999.0
                    set y11[i] = 999.0
                    set y12[i] = 999.0
                    set y21[i] = 999.0
                    set y22[i] = 999.0
                    set y31[i] = 999.0
                    set y32[i] = 999.0
                    set y41[i] = 999.0
                    set y42[i] = 999.0
                    set y51[i] = 999.0
                    set y52[i] = 999.0
                    set y61[i] = 999.0
                    set y62[i] = 999.0
                    set mapX1[i] = 999.0
                    set mapY1[i] = 999.0
                    set mapX2[i] = 999.0
                    set mapY2[i] = 999.0
                    set frameX1[i] = 999.0
                    set frameY1[i] = 999.0
                    set frameX2[i] = 999.0
                    set frameY2[i] = 999.0
                    set command0X1[i] = 999.0
                    set command0Y1[i] = 999.0
                    set command1X1[i] = 999.0
                    set command1Y1[i] = 999.0
                    set command2X1[i] = 999.0
                    set command2Y1[i] = 999.0
                    set command3X1[i] = 999.0
                    set command3Y1[i] = 999.0
                    set command4X1[i] = 999.0
                    set command4Y1[i] = 999.0
                    set command5X1[i] = 999.0
                    set command5Y1[i] = 999.0
                    set command6X1[i] = 999.0
                    set command6Y1[i] = 999.0
                    set command7X1[i] = 999.0
                    set command7Y1[i] = 999.0
                    set command8X1[i] = 999.0
                    set command8Y1[i] = 999.0
                    set command9X1[i] = 999.0
                    set command9Y1[i] = 999.0
                    set command10X1[i] = 999.0
                    set command10Y1[i] = 999.0
                    set command11X1[i] = 999.0
                    set command11Y1[i] = 999.0
                    set command0X2[i] = 999.0
                    set command0Y2[i] = 999.0
                    set command1X2[i] = 999.0
                    set command1Y2[i] = 999.0
                    set command2X2[i] = 999.0
                    set command2Y2[i] = 999.0
                    set command3X2[i] = 999.0
                    set command3Y2[i] = 999.0
                    set command4X2[i] = 999.0
                    set command4Y2[i] = 999.0
                    set command5X2[i] = 999.0
                    set command5Y2[i] = 999.0
                    set command6X2[i] = 999.0
                    set command6Y2[i] = 999.0
                    set command7X2[i] = 999.0
                    set command7Y2[i] = 999.0
                    set command8X2[i] = 999.0
                    set command8Y2[i] = 999.0
                    set command9X2[i] = 999.0
                    set command9Y2[i] = 999.0
                    set command10X2[i] = 999.0
                    set command10Y2[i] = 999.0
                    set command11X2[i] = 999.0
                    set command11Y2[i] = 999.0
                    set shop[i] = false
                    set main[i] = null
                    set checkL[i] = false
                    set checkR[i] = false
                    set checkMenu[i] = false
                set i = i + 1
            endloop

            call onCommandButtons()
            call onInventoryButtons()
            call onInfoPanel()
            call onPortrait()
            call onGroupSelection()
            call onChat()
        endmethod
    endstruct
endlibrary
library NewBonus requires optional DamageInterface, optional Evasion, optional CriticalStrike, optional SpellPower, optional LifeSteal, optional SpellVamp, optional CooldownReduction, optional Tenacity
    /* ---------------------------------------- NewBonus v2.4 --------------------------------------- */
    // Since ObjectMerger is broken and we still have no means to edit
    // bonus values (green values) i decided to create a light weight
    // Bonus library that works in the same way that the original Bonus Mod
    // by Earth Fury did. NewBonus requires patch 1.30+.
    // Credits to Earth Fury for the original Bonus idea

    // How to Import?
    // Importing bonus mod is really simple. Just copy the 9 abilities with the
    // prefix "NewBonus" from the Object Editor into your map and match their new raw
    // code to the bonus types in the global block below. Then create a trigger called
    // NewBonus, convert it to custom text and paste this code there. You done!
    /* ---------------------------------------- By Chopinski ---------------------------------------- */
    
    /* ---------------------------------------------------------------------------------------------- */
    /*                                          Configuration                                         */
    /* ---------------------------------------------------------------------------------------------- */
    globals
        // If true will use the extended version of the system.
        // Make sure you have the DamageInterface and Cooldown Reduction libraries
        public constant boolean EXTENDED                  = true
    
        // This is the maximum recursion limit allowed by the system.
        // It's value must be greater than or equal to 0. When equal to 0
        // no recursion is allowed. Values too big can cause screen freezes.
        private constant integer RECURSION_LIMIT          = 8
    
        //The bonus types
        constant integer BONUS_DAMAGE                     = 1
        constant integer BONUS_ARMOR                      = 2
        constant integer BONUS_AGILITY                    = 3
        constant integer BONUS_STRENGTH                   = 4
        constant integer BONUS_INTELLIGENCE               = 5
        constant integer BONUS_HEALTH                     = 6
        constant integer BONUS_MANA                       = 7
        constant integer BONUS_MOVEMENT_SPEED             = 8
        constant integer BONUS_SIGHT_RANGE                = 9
        constant integer BONUS_HEALTH_REGEN               = 10
        constant integer BONUS_MANA_REGEN                 = 11
        constant integer BONUS_ATTACK_SPEED               = 12
        constant integer BONUS_MAGIC_RESISTANCE           = 13
        constant integer BONUS_EVASION_CHANCE             = 14
        constant integer BONUS_CRITICAL_DAMAGE            = 15
        constant integer BONUS_CRITICAL_CHANCE            = 16
        constant integer BONUS_LIFE_STEAL                 = 17
        constant integer BONUS_MISS_CHANCE                = 18
        constant integer BONUS_SPELL_POWER_FLAT           = 19
        constant integer BONUS_SPELL_POWER_PERCENT        = 20
        constant integer BONUS_SPELL_VAMP                 = 21
        constant integer BONUS_COOLDOWN_REDUCTION         = 22
        constant integer BONUS_COOLDOWN_REDUCTION_FLAT    = 23
        constant integer BONUS_COOLDOWN_OFFSET            = 24
        constant integer BONUS_TENACITY                   = 25
        constant integer BONUS_TENACITY_FLAT              = 26
        constant integer BONUS_TENACITY_OFFSET            = 27
    
        //The abilities codes for each bonus
        //When pasting the abilities over to your map
        //their raw code should match the bonus here
        private constant integer DAMAGE_ABILITY           = 'Z001'
        private constant integer ARMOR_ABILITY            = 'Z002'
        private constant integer STATS_ABILITY            = 'Z003'
        private constant integer HEALTH_ABILITY           = 'Z004'
        private constant integer MANA_ABILITY             = 'Z005'
        private constant integer HEALTHREGEN_ABILITY      = 'Z006'
        private constant integer MANAREGEN_ABILITY        = 'Z007'
        private constant integer ATTACKSPEED_ABILITY      = 'Z008'
        private constant integer MOVEMENTSPEED_ABILITY    = 'Z009'
        private constant integer SIGHT_RANGE_ABILITY      = 'Z00A'
        private constant integer MAGIC_RESISTANCE_ABILITY = 'Z00B'
        private constant integer CRITICAL_STRIKE_ABILITY  = 'Z00C'
        private constant integer EVASION_ABILITY          = 'Z00D'
        private constant integer LIFE_STEAL_ABILITY       = 'Z00E'
    
        //The abilities fields that are modified. For the sake of readability
        private constant abilityintegerlevelfield DAMAGE_FIELD           = ABILITY_ILF_ATTACK_BONUS
        private constant abilityintegerlevelfield ARMOR_FIELD            = ABILITY_ILF_DEFENSE_BONUS_IDEF
        private constant abilityintegerlevelfield AGILITY_FIELD          = ABILITY_ILF_AGILITY_BONUS
        private constant abilityintegerlevelfield STRENGTH_FIELD         = ABILITY_ILF_STRENGTH_BONUS_ISTR
        private constant abilityintegerlevelfield INTELLIGENCE_FIELD     = ABILITY_ILF_INTELLIGENCE_BONUS
        private constant abilityintegerlevelfield HEALTH_FIELD           = ABILITY_ILF_MAX_LIFE_GAINED
        private constant abilityintegerlevelfield MANA_FIELD             = ABILITY_ILF_MAX_MANA_GAINED
        private constant abilityintegerlevelfield MOVEMENTSPEED_FIELD    = ABILITY_ILF_MOVEMENT_SPEED_BONUS
        private constant abilityintegerlevelfield SIGHT_RANGE_FIELD      = ABILITY_ILF_SIGHT_RANGE_BONUS
        private constant abilityreallevelfield    HEALTHREGEN_FIELD      = ABILITY_RLF_AMOUNT_OF_HIT_POINTS_REGENERATED
        private constant abilityreallevelfield    MANAREGEN_FIELD        = ABILITY_RLF_AMOUNT_REGENERATED
        private constant abilityreallevelfield    ATTACKSPEED_FIELD      = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1
        private constant abilityreallevelfield    MAGIC_RESISTANCE_FIELD = ABILITY_RLF_DAMAGE_REDUCTION_ISR2
        private constant abilityreallevelfield    CRITICAL_CHANCE_FIELD  = ABILITY_RLF_CHANCE_TO_CRITICAL_STRIKE
        private constant abilityreallevelfield    CRITICAL_DAMAGE_FIELD  = ABILITY_RLF_DAMAGE_MULTIPLIER_OCR2
        private constant abilityreallevelfield    EVASION_FIELD          = ABILITY_RLF_CHANCE_TO_EVADE_EEV1
        private constant abilityreallevelfield    LIFE_STEAL_FIELD       = ABILITY_RLF_LIFE_STOLEN_PER_ATTACK
    endglobals
    
    /* ---------------------------------------------------------------------------------------------- */
    /*                                            JASS API                                            */
    /* ---------------------------------------------------------------------------------------------- */
    function GetUnitBonus takes unit source, integer bonus returns real
        return NewBonus.get(source, bonus)
    endfunction

    function SetUnitBonus takes unit source, integer bonus, real amount returns real
        return NewBonus.Set(source, bonus, amount, false)
    endfunction
    
    function RemoveUnitBonus takes unit source, integer bonus returns nothing
        if bonus == BONUS_CRITICAL_DAMAGE then
            call NewBonus.Set(source, bonus, 1, false)
        else
            call NewBonus.Set(source, bonus, 0, false)
        endif
        
        if bonus == BONUS_LIFE_STEAL then
            call UnitRemoveAbility(source, LIFE_STEAL_ABILITY)
        endif
    endfunction
    
    function AddUnitBonus takes unit source, integer bonus, real amount returns real
        return NewBonus.add(source, bonus, amount)
    endfunction
    
    function RegisterBonusEvent takes code c returns nothing
        call NewBonus.register(c, 0)
    endfunction
    
    function RegisterBonusTypeEvent takes integer bonus, code c returns nothing
        call NewBonus.register(c, bonus)
    endfunction
    
    function GetBonusUnit takes nothing returns unit
        return NewBonus.unit[NewBonus.key]
    endfunction
    
    function GetBonusType takes nothing returns integer
        return NewBonus.type[NewBonus.key]
    endfunction
    
    function SetBonusType takes integer bonus returns nothing
        if bonus >= BONUS_DAMAGE and bonus <= NewBonus.last then
            set NewBonus.type[NewBonus.key] = bonus
        endif
    endfunction
    
    function GetBonusAmount takes nothing returns real
        return NewBonus.amount[NewBonus.key]
    endfunction
    
    function SetBonusAmount takes real amount returns nothing
        set NewBonus.amount[NewBonus.key] = amount
    endfunction

    /* ---------------------------------------------------------------------------------------------- */
    /*                                             System                                             */
    /* ---------------------------------------------------------------------------------------------- */
    struct NewBonus
        private static trigger trigger = CreateTrigger()
        readonly static integer key = -1
        private static trigger array event
        private static integer count = 0
        readonly static unit array unit
        readonly static integer last
        readonly static integer linkType
        static integer array type
        static real array amount
        
        private static method checkOverflow takes real current, real value returns real
            if value > 0 and current > 2147483647 - value then
                return 2147483647 - current
            elseif value < 0 and current < -2147483648 - value then
                return -2147483648 - current
            else
                return value
            endif
        endmethod

        private static method onEvent takes integer key returns nothing
            local integer i = 0
            local integer next = -1
            local integer prev = -2

            set count = count + 1
            
            if amount[key] != 0 and (count - last < RECURSION_LIMIT) then
                loop
                    exitwhen type[key] == next or (i - last > RECURSION_LIMIT)
                        set next = type[key]

                        if event[next] != null then
                            call TriggerEvaluate(event[next])
                        endif

                        if type[key] != next then
                            set i = i + 1
                        else
                            if next != prev then
                                call TriggerEvaluate(trigger)

                                if type[key] != next then
                                    set i = i + 1
                                    set prev = next
                                endif
                            endif
                        endif
                endloop
            endif
            
            set count = count - 1
            set .key = key
        endmethod
        
        private static method setAbilityI takes unit source, integer id, abilityintegerlevelfield field, real value, boolean adding returns real
            if GetUnitAbilityLevel(source, id) == 0 then
                call UnitAddAbility(source, id)
                call UnitMakeAbilityPermanent(source, true, id)
            endif
            
            if adding then
                if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(source, id), field, 0, BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, id), field, 0) + R2I(value)) then
                    call IncUnitAbilityLevel(source, id)
                    call DecUnitAbilityLevel(source, id)
                endif
            else
                if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(source, id), field, 0, R2I(value)) then
                    call IncUnitAbilityLevel(source, id)
                    call DecUnitAbilityLevel(source, id)
                endif
            endif
            
            set linkType = type[key]
            
            if key > -1 then
                set key = key - 1
            endif
        
            return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, id), field, 0))
        endmethod

        private static method setAbilityR takes unit source, integer id, abilityreallevelfield field, real value returns real
            if GetUnitAbilityLevel(source, id) == 0 then
                call UnitAddAbility(source, id)
                call UnitMakeAbilityPermanent(source, true, id)
            endif
        
            if BlzSetAbilityRealLevelField(BlzGetUnitAbility(source, id), field, 0, value) then
                call IncUnitAbilityLevel(source, id)
                call DecUnitAbilityLevel(source, id)
            endif
            
            set linkType = type[key]
            
            if key > -1 then
                set key = key - 1
            endif
        
            return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, id), field, 0)
        endmethod

        static method get takes unit source, integer bonus returns real
            if bonus == BONUS_DAMAGE then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, DAMAGE_ABILITY), DAMAGE_FIELD, 0))
            elseif bonus == BONUS_ARMOR then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, ARMOR_ABILITY), ARMOR_FIELD, 0))
            elseif bonus == BONUS_HEALTH then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, HEALTH_ABILITY), HEALTH_FIELD, 0))
            elseif bonus == BONUS_MANA then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, MANA_ABILITY), MANA_FIELD, 0))
            elseif bonus == BONUS_AGILITY then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, STATS_ABILITY), AGILITY_FIELD, 0))
            elseif bonus == BONUS_STRENGTH then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, STATS_ABILITY), STRENGTH_FIELD, 0))
            elseif bonus == BONUS_INTELLIGENCE then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, STATS_ABILITY), INTELLIGENCE_FIELD, 0))
            elseif bonus == BONUS_MOVEMENT_SPEED then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, MOVEMENTSPEED_ABILITY), MOVEMENTSPEED_FIELD, 0))
            elseif bonus == BONUS_SIGHT_RANGE then
                return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, SIGHT_RANGE_ABILITY), SIGHT_RANGE_FIELD, 0))
            elseif bonus == BONUS_HEALTH_REGEN then
                return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, HEALTHREGEN_ABILITY), HEALTHREGEN_FIELD, 0)
            elseif bonus == BONUS_MANA_REGEN then
                return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, MANAREGEN_ABILITY), MANAREGEN_FIELD, 0)
            elseif bonus == BONUS_ATTACK_SPEED then
                return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, ATTACKSPEED_ABILITY), ATTACKSPEED_FIELD, 0)
            elseif bonus == BONUS_MAGIC_RESISTANCE then
                return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, MAGIC_RESISTANCE_ABILITY), MAGIC_RESISTANCE_FIELD, 0)
            elseif bonus >= BONUS_EVASION_CHANCE and bonus <= last then
                static if EXTENDED and LIBRARY_DamageInterface and LIBRARY_Evasion and LIBRARY_CriticalStrike and LIBRARY_SpellPower and LIBRARY_LifeSteal and LIBRARY_SpellVamp and LIBRARY_Tenacity then
                    if bonus == BONUS_EVASION_CHANCE then
                        return GetUnitEvasionChance(source)
                    elseif bonus == BONUS_MISS_CHANCE then
                        return GetUnitMissChance(source)
                    elseif bonus == BONUS_CRITICAL_CHANCE then
                        return GetUnitCriticalChance(source)
                    elseif bonus == BONUS_CRITICAL_DAMAGE then
                        return GetUnitCriticalMultiplier(source)
                    elseif bonus == BONUS_SPELL_POWER_FLAT then
                        return GetUnitSpellPowerFlat(source)
                    elseif bonus == BONUS_SPELL_POWER_PERCENT then
                        return GetUnitSpellPowerPercent(source)
                    elseif bonus == BONUS_LIFE_STEAL then
                        return GetUnitLifeSteal(source)
                    elseif bonus == BONUS_SPELL_VAMP then
                        return GetUnitSpellVamp(source)
                    elseif bonus == BONUS_COOLDOWN_REDUCTION then
                        return GetUnitCooldownReduction(source)
                    elseif bonus == BONUS_COOLDOWN_REDUCTION_FLAT then
                        return GetUnitCooldownReductionFlat(source)
                    elseif bonus == BONUS_COOLDOWN_OFFSET then
                        return GetUnitCooldownOffset(source)
                    elseif bonus == BONUS_TENACITY then
                        return GetUnitTenacity(source)
                    elseif bonus == BONUS_TENACITY_FLAT then
                        return GetUnitTenacityFlat(source)
                    elseif bonus == BONUS_TENACITY_OFFSET then
                        return GetUnitTenacityOffset(source)
                    endif
                else
                    if bonus == BONUS_CRITICAL_CHANCE then
                        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, CRITICAL_STRIKE_ABILITY), CRITICAL_CHANCE_FIELD, 0)
                    elseif bonus == BONUS_CRITICAL_DAMAGE then
                        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, CRITICAL_STRIKE_ABILITY), CRITICAL_DAMAGE_FIELD, 0)
                    elseif bonus == BONUS_EVASION_CHANCE then
                        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, EVASION_ABILITY), EVASION_FIELD, 0)
                    elseif bonus == BONUS_LIFE_STEAL then
                        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, LIFE_STEAL_ABILITY), LIFE_STEAL_FIELD, 0)
                    endif
                endif
            else
                call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
            endif
            
            return -1.
        endmethod

        static method Set takes unit source, integer bonus, real value, boolean adding returns real
            local real p
            
            if not adding then
                set key = key + 1
                set unit[key] = source
                set type[key] = bonus
                set amount[key] = value
                
                call onEvent(key)
                
                if amount[key] != value then
                    set value = amount[key]
                endif
                
                if type[key] != bonus then
                    return Set(unit[key], type[key], amount[key], not adding)
                endif
            else
                set unit[key] = source
                set type[key] = bonus
                set amount[key] = value
            endif

            if bonus == BONUS_DAMAGE then
                return setAbilityI(source, DAMAGE_ABILITY, DAMAGE_FIELD, value, adding)
            elseif bonus == BONUS_ARMOR then
                return setAbilityI(source, ARMOR_ABILITY, ARMOR_FIELD, value, adding)
            elseif bonus == BONUS_HEALTH then
                set p = GetUnitLifePercent(source)

                if value == 0 and not adding then
                    call BlzSetUnitMaxHP(source, R2I(BlzGetUnitMaxHP(source) - get(source, bonus)))
                else
                    call BlzSetUnitMaxHP(source, R2I(BlzGetUnitMaxHP(source) + value))
                endif

                call setAbilityI(source, HEALTH_ABILITY, HEALTH_FIELD, value, adding)
                call SetUnitLifePercentBJ(source, p)

                return value
            elseif bonus == BONUS_MANA then
                set p = GetUnitManaPercent(source)

                if value == 0 and not adding then
                    call BlzSetUnitMaxMana(source, R2I(BlzGetUnitMaxMana(source) - get(source, bonus)))
                else
                    call BlzSetUnitMaxMana(source, R2I(BlzGetUnitMaxMana(source) + value))
                endif

                call setAbilityI(source, MANA_ABILITY, MANA_FIELD, value, adding)
                call SetUnitManaPercentBJ(source, p)

                return value
            elseif bonus == BONUS_AGILITY then
                return setAbilityI(source, STATS_ABILITY, AGILITY_FIELD, value, adding)
            elseif bonus == BONUS_STRENGTH then
                return setAbilityI(source, STATS_ABILITY, STRENGTH_FIELD, value, adding)
            elseif bonus == BONUS_INTELLIGENCE then
                return setAbilityI(source, STATS_ABILITY, INTELLIGENCE_FIELD, value, adding)
            elseif bonus == BONUS_MOVEMENT_SPEED then
                return setAbilityI(source, MOVEMENTSPEED_ABILITY, MOVEMENTSPEED_FIELD, value, adding)
            elseif bonus == BONUS_SIGHT_RANGE then
                if value == 0 and not adding then
                    call BlzSetUnitRealField(source, UNIT_RF_SIGHT_RADIUS, (BlzGetUnitRealField(source, UNIT_RF_SIGHT_RADIUS) - get(source, bonus)))
                else
                    call BlzSetUnitRealField(source, UNIT_RF_SIGHT_RADIUS, (BlzGetUnitRealField(source, UNIT_RF_SIGHT_RADIUS) + value))
                endif

                call setAbilityI(source, SIGHT_RANGE_ABILITY, SIGHT_RANGE_FIELD, value, adding)

                return value
            elseif bonus == BONUS_HEALTH_REGEN then
                return setAbilityR(source, HEALTHREGEN_ABILITY, HEALTHREGEN_FIELD, value)
            elseif bonus == BONUS_MANA_REGEN then
                return setAbilityR(source, MANAREGEN_ABILITY, MANAREGEN_FIELD, value)
            elseif bonus == BONUS_ATTACK_SPEED then
                return setAbilityR(source, ATTACKSPEED_ABILITY, ATTACKSPEED_FIELD, value)
            elseif bonus == BONUS_MAGIC_RESISTANCE then
                return setAbilityR(source, MAGIC_RESISTANCE_ABILITY, MAGIC_RESISTANCE_FIELD, value)
            elseif bonus >= BONUS_EVASION_CHANCE and bonus <= last then
                static if EXTENDED and LIBRARY_DamageInterface and LIBRARY_Evasion and LIBRARY_CriticalStrike and LIBRARY_SpellPower and LIBRARY_LifeSteal and LIBRARY_SpellVamp and LIBRARY_Tenacity then
                    if bonus == BONUS_EVASION_CHANCE then
                        call SetUnitEvasionChance(source, value)
                    elseif bonus == BONUS_MISS_CHANCE then
                        call SetUnitMissChance(source, value)
                    elseif bonus == BONUS_CRITICAL_CHANCE then
                        call SetUnitCriticalChance(source, value)
                    elseif bonus == BONUS_CRITICAL_DAMAGE then
                        call SetUnitCriticalMultiplier(source, value)
                    elseif bonus == BONUS_SPELL_POWER_FLAT then
                        call SetUnitSpellPowerFlat(source, value)
                    elseif bonus == BONUS_SPELL_POWER_PERCENT then
                        call SetUnitSpellPowerPercent(source, value)
                    elseif bonus == BONUS_LIFE_STEAL then
                        call SetUnitLifeSteal(source, value)
                    elseif bonus == BONUS_SPELL_VAMP then
                        call SetUnitSpellVamp(source, value)
                    elseif bonus == BONUS_COOLDOWN_REDUCTION then
                        if adding then
                            call UnitAddCooldownReduction(source, value)
                        else
                            call SetUnitCooldownReduction(source, value)
                        endif
                    elseif bonus == BONUS_COOLDOWN_REDUCTION_FLAT then
                        call SetUnitCooldownReductionFlat(source, value)
                    elseif bonus == BONUS_COOLDOWN_OFFSET then
                        call SetUnitCooldownOffset(source, value)
                    elseif bonus == BONUS_TENACITY then
                        if adding then
                            call UnitAddTenacity(source, value)
                        else
                            call SetUnitTenacity(source, value)
                        endif
                    elseif bonus == BONUS_TENACITY_FLAT then
                        call SetUnitTenacityFlat(source, value)
                    elseif bonus == BONUS_TENACITY_OFFSET then
                        call SetUnitTenacityOffset(source, value)
                    endif
                    
                    set linkType = bonus
                    
                    if key > -1 then
                        set key = key - 1
                    endif
                    
                    return value
                else
                    if bonus == BONUS_CRITICAL_CHANCE then
                        return setAbilityR(source, CRITICAL_STRIKE_ABILITY, CRITICAL_CHANCE_FIELD, value)
                    elseif bonus == BONUS_CRITICAL_DAMAGE then
                        return setAbilityR(source, CRITICAL_STRIKE_ABILITY, CRITICAL_DAMAGE_FIELD, value)
                    elseif bonus == BONUS_EVASION_CHANCE then
                        return setAbilityR(source, EVASION_ABILITY, EVASION_FIELD, value)
                    elseif bonus == BONUS_LIFE_STEAL then
                        return setAbilityR(source, LIFE_STEAL_ABILITY, LIFE_STEAL_FIELD, value)
                    endif
                endif
            else
                call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
            endif

            return -1.
        endmethod

        static method add takes unit source, integer bonus, real value returns real
            if value != 0 then
                set key = key + 1
                set unit[key] = source
                set type[key] = bonus
                set amount[key] = value
                
                if bonus <= BONUS_SIGHT_RANGE then
                    set amount[key] = checkOverflow(get(source, bonus), R2I(value))
                endif
                
                call onEvent(key)
                set value = amount[key] 
                
                if type[key] <= BONUS_SIGHT_RANGE then
                    call Set(unit[key], type[key], checkOverflow(get(unit[key], type[key]), R2I(amount[key])), true)
                else
                    static if EXTENDED and LIBRARY_DamageInterface and LIBRARY_Evasion and LIBRARY_CriticalStrike and LIBRARY_SpellPower and LIBRARY_LifeSteal and LIBRARY_SpellVamp and LIBRARY_Tenacity then
                        if type[key] == BONUS_COOLDOWN_REDUCTION or type[key] == BONUS_TENACITY then
                            call Set(unit[key], type[key], amount[key], true)
                        else
                            call Set(unit[key], type[key], get(unit[key], type[key]) + amount[key], true)
                        endif
                    else
                        call Set(unit[key], type[key], get(unit[key], type[key]) + amount[key], true)
                    endif
                endif
                
                return value
            endif
            
            return 0.
        endmethod
        
        static method register takes code c, integer bonus returns nothing
            if bonus >= BONUS_DAMAGE and bonus <= last then
                if event[bonus] == null then
                    set event[bonus] = CreateTrigger()
                endif
                call TriggerAddCondition(event[bonus], Filter(c))
            else
                call TriggerAddCondition(trigger, Filter(c))
            endif
        endmethod
        
        private static method onInit takes nothing returns nothing
            static if EXTENDED and LIBRARY_DamageInterface and LIBRARY_Evasion and LIBRARY_CriticalStrike and LIBRARY_SpellPower and LIBRARY_LifeSteal and LIBRARY_SpellVamp and LIBRARY_Tenacity then
                set last = BONUS_TENACITY_OFFSET
            else
                set last = BONUS_LIFE_STEAL
            endif
        endmethod
    endstruct
endlibrary
library NewBonusUtils requires NewBonus, RegisterPlayerUnitEvent
    /* ------------------------------------- NewBonusUtils v2.4 ------------------------------------- */
    // Required Library: RegisterPlayerUnitEvent -> www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/

    // API:
    // function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
    //     -> Add the specified amount for the specified bonus type for unit for a duration
    //     -> Example: call AddUnitBonusTimed(GetTriggerUnit(), BONUS_ARMOR, 13, 10.5)

    // function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
    //     -> Links the bonus amount specified to a buff or ability. As long as the unit has the buff or
    //     -> the ability represented by the parameter buffId the bonus is not removed.
    //     -> Example: call LinkBonusToBuff(GetTriggerUnit(), BONUS_ARMOR, 10, 'B000')

    // function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
    //     -> Links the bonus amount specified to an item. As long as the unit has that item the bonus is not removed.
    //     -> Note that it will work for items with the same id, because it takes as parameter the item object.
    //     -> Example: call LinkBonusToItem(GetManipulatingUnit(), BONUS_ARMOR, 10, GetManipulatedItem())

    // function UnitCopyBonuses takes unit source, unit target returns nothing
    //     -> Copy the source unit bonuses using the Add functionality to the target unit
    //     -> Example: call UnitCopyBonuses(GetTriggerUnit(), GetSummonedUnit())

    // function UnitMirrorBonuses takes unit source, unit target returns nothing
    //     -> Copy the source unit bonuses using the Set functionality to the target unit
    //     -> Example: call UnitMirrorBonuses(GetTriggerUnit(), GetSummonedUnit())
    /* ---------------------------------------- By Chopinski ---------------------------------------- */
    
    /* ---------------------------------------------------------------------------------------------- */
    /*                                            JASS API                                            */
    /* ---------------------------------------------------------------------------------------------- */
    function AddUnitBonusTimed takes unit source, integer bonus, real amount, real duration returns nothing
        call NewBonusUtils.linkTimed(source, bonus, amount, duration, true)
    endfunction

    function LinkBonusToBuff takes unit source, integer bonus, real amount, integer id returns nothing
        call NewBonusUtils.linkBuff(source, bonus, amount, id, false)
    endfunction

    function LinkBonusToItem takes unit source, integer bonus, real amount, item i returns nothing
        call NewBonusUtils.linkItem(source, bonus, amount, i)
    endfunction

    function UnitCopyBonuses takes unit source, unit target returns nothing
        call NewBonusUtils.copy(source, target)
    endfunction

    function UnitMirrorBonuses takes unit source, unit target returns nothing
        call NewBonusUtils.mirror(source, target)
    endfunction

    /* ---------------------------------------------------------------------------------------------- */
    /*                                             System                                             */
    /* ---------------------------------------------------------------------------------------------- */
    struct NewBonusUtils extends NewBonus
        static constant real period = 0.03125000
        static timer timer = CreateTimer()
        static integer index = -1
        static thistype array array
        static integer k = -1
        static thistype array items

        unit source
        item item
        real duration
        integer bonus_t
        integer buff
        real value
        boolean link

        method remove takes integer i, boolean isItem returns integer
            static if NewBonus_EXTENDED and LIBRARY_DamageInterface and LIBRARY_Evasion and LIBRARY_CriticalStrike and LIBRARY_SpellPower and LIBRARY_LifeSteal and LIBRARY_SpellVamp and LIBRARY_Tenacity then
                if bonus_t == BONUS_COOLDOWN_REDUCTION then
                    call UnitRemoveCooldownReduction(source, value)
                elseif bonus_t == BONUS_TENACITY then
                    call UnitRemoveTenacity(source, value)
                else
                    call AddUnitBonus(source, bonus_t, -value)
                endif
            else
                call AddUnitBonus(source, bonus_t, -value)
            endif

            if isItem then
                set items[i] = items[k]
                set k = k - 1
            else
                set array[i] = array[index]
                set index = index - 1

                if index == -1 then
                    call PauseTimer(timer)
                endif
            endif

            set source = null
            set item = null
            
            call deallocate()

            return i - 1
        endmethod

        static method onDrop takes nothing returns nothing
            local item itm = GetManipulatedItem()
            local integer i = 0
            local thistype this
            
            loop
                exitwhen i > k
                    set this = items[i]
                    
                    if item == itm then
                        set i = remove(i, true)
                    endif
                set i = i + 1
            endloop
        endmethod
     
        static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > index
                    set this = array[i]

                    if link then
                        if duration <= 0 then
                            set i = remove(i, false)
                        endif
                        set duration = duration - period
                    else
                        if GetUnitAbilityLevel(source, buff) == 0 then
                            set i = remove(i, false)
                        endif
                    endif
                set i = i + 1
            endloop
        endmethod

        static method linkTimed takes unit source, integer bonus, real amount, real duration, boolean link returns nothing
            local thistype this = thistype.allocate()

            set this.source = source
            set this.duration = duration
            set this.link = link
            set this.value = AddUnitBonus(source, bonus, amount)
            set this.bonus_t = linkType
            set index = index + 1
            set array[index] = this
         
            if index == 0 then
                call TimerStart(timer, period, true, function thistype.onPeriod)
            endif
        endmethod

        static method linkBuff takes unit source, integer bonus, real amount, integer id, boolean link returns nothing
            local thistype this = thistype.allocate()

            set this.source = source
            set this.buff = id
            set this.link = link
            set this.value = AddUnitBonus(source, bonus, amount)
            set this.bonus_t = linkType
            set index = index + 1
            set array[index] = this
         
            if index == 0 then
                call TimerStart(timer, period, true, function thistype.onPeriod)
            endif
        endmethod

        static method linkItem takes unit source, integer bonus, real amount, item i returns nothing
            local thistype this = thistype.allocate()

            set this.source = source
            set this.item = i
            set this.value = AddUnitBonus(source, bonus, amount)
            set this.bonus_t = linkType
            set k = k + 1
            set items[k] = this
        endmethod

        static method copy takes unit source, unit target returns nothing
            local integer i = 1

            loop
                exitwhen i > last
                    if GetUnitBonus(source, i) != 0 then
                        call AddUnitBonus(target, i, GetUnitBonus(source, i))
                    endif
                set i = i + 1
            endloop
        endmethod

        static method mirror takes unit source, unit target returns nothing
            local integer i = 1
            
            loop
                exitwhen i > last
                    call SetUnitBonus(target, i, GetUnitBonus(source, i))
                set i = i + 1
            endloop
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
        endmethod
    endstruct
endlibrary
library CrowdControl requires Utilities, WorldBounds, Indexer, TimerUtils, RegisterPlayerUnitEvent, optional Tenacity
    /* ------------------------------------- Crowd Control v1.0 ------------------------------------- */
    // How to Import:
    // 1 - Copy the Utilities library over to your map and follow its install instructions
    // 2 - Copy the WorldBounds library over to your map and follow its install instructions
    // 3 - Copy the Indexer library over to your map and follow its install instructions
    // 4 - Copy the TimerUtils library over to your map and follow its install instructions
    // 5 - Copy the RegisterPlayerUnitEvent library over to your map and follow its install instructions
    // 6 - Copy the Tenacity library over to your map and follow its install instructions
    // 7 - Copy this library into your map
    // 8 - Copy the 14 buffs and 15 abilities with the CC prefix and match their raw code below.
    /* ---------------------------------------- By Chopinski ---------------------------------------- */

    /* ---------------------------------------------------------------------------------------------- */
    /*                                          Configuration                                         */
    /* ---------------------------------------------------------------------------------------------- */
    globals
        // The raw code of the silence ability
        private constant integer SILENCE            = 'U000'
        // The raw code of the stun ability
        private constant integer STUN               = 'U001'
        // The raw code of the attack slow ability
        private constant integer ATTACK_SLOW        = 'U002'
        // The raw code of the movement slow ability
        private constant integer MOVEMENT_SLOW      = 'U003'
        // The raw code of the banish ability
        private constant integer BANISH             = 'U004'
        // The raw code of the ensnare ability
        private constant integer ENSNARE            = 'U005'
        // The raw code of the purge ability
        private constant integer PURGE              = 'U006'
        // The raw code of the hex ability
        private constant integer HEX                = 'U007'
        // The raw code of the sleep ability
        private constant integer SLEEP              = 'U008'
        // The raw code of the cyclone ability
        private constant integer CYCLONE            = 'U009'
        // The raw code of the entangle ability
        private constant integer ENTANGLE           = 'U010'
        // The raw code of the disarm ability
        private constant integer DISARM             = 'U011'
        // The raw code of the fear ability
        private constant integer FEAR               = 'U012'
        // The raw code of the taunt ability
        private constant integer TAUNT              = 'U013'
        // The raw code of the true sight ability
        private constant integer TRUE_SIGHT         = 'U014'
        // The raw code of the silence buff
        private constant integer SILENCE_BUFF       = 'BU00'
        // The raw code of the stun buff
        private constant integer STUN_BUFF          = 'BU01'
        // The raw code of the attack slow buff
        private constant integer ATTACK_SLOW_BUFF   = 'BU02'
        // The raw code of the movement slow buff
        private constant integer MOVEMENT_SLOW_BUFF = 'BU03'
        // The raw code of the banish buff
        private constant integer BANISH_BUFF        = 'BU04'
        // The raw code of the ensnare buff
        private constant integer ENSNARE_BUFF       = 'BU05'
        // The raw code of the purge buff
        private constant integer PURGE_BUFF         = 'BU06'
        // The raw code of the hex buff
        private constant integer HEX_BUFF           = 'BU07'
        // The raw code of the sleep buff
        private constant integer SLEEP_BUFF         = 'BU08'
        // The raw code of the cyclone buff
        private constant integer CYCLONE_BUFF       = 'BU09'
        // The raw code of the entangle buff
        private constant integer ENTANGLE_BUFF      = 'BU10'
        // The raw code of the disarm buff
        private constant integer DISARM_BUFF        = 'BU11'
        // The raw code of the fear buff
        private constant integer FEAR_BUFF          = 'BU12'
        // The raw code of the taunt buff
        private constant integer TAUNT_BUFF         = 'BU13'

        // This is the maximum recursion limit allowed by the system.
        // Its value must be greater than or equal to 0. When equal to 0
        // no recursion is allowed. Values too big can cause screen freezes.
        private constant integer RECURSION_LIMIT    = 8

        // The Crowd Control types
        constant integer CROWD_CONTROL_SILENCE      = 0
        constant integer CROWD_CONTROL_STUN         = 1
        constant integer CROWD_CONTROL_SLOW         = 2
        constant integer CROWD_CONTROL_SLOW_ATTACK  = 3
        constant integer CROWD_CONTROL_BANISH       = 4
        constant integer CROWD_CONTROL_ENSNARE      = 5
        constant integer CROWD_CONTROL_PURGE        = 6
        constant integer CROWD_CONTROL_HEX          = 7
        constant integer CROWD_CONTROL_SLEEP        = 8
        constant integer CROWD_CONTROL_CYCLONE      = 9
        constant integer CROWD_CONTROL_ENTANGLE     = 10
        constant integer CROWD_CONTROL_DISARM       = 11
        constant integer CROWD_CONTROL_FEAR         = 12
        constant integer CROWD_CONTROL_TAUNT        = 13
        constant integer CROWD_CONTROL_KNOCKBACK    = 14
        constant integer CROWD_CONTROL_KNOCKUP      = 15
    endglobals

    /* ---------------------------------------------------------------------------------------------- */
    /*                                            JASS API                                            */
    /* ---------------------------------------------------------------------------------------------- */
    /* ------------------------------------------- Disarm ------------------------------------------- */
    function DisarmUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.disarm(target, duration, model, point, stack)
    endfunction

    function IsUnitDisarmed takes unit target returns boolean
        return CrowdControl.disarmed(target)
    endfunction

    /* -------------------------------------------- Fear -------------------------------------------- */
    function FearUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.fear(target, duration, model, point, stack)
    endfunction

    function IsUnitFeared takes unit target returns boolean
        return CrowdControl.feared(target)
    endfunction 

    /* -------------------------------------------- Taunt ------------------------------------------- */
    function TauntUnit takes unit source, unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.taunt(source, target, duration, model, point, stack)
    endfunction

    function IsUnitTaunted takes unit target returns boolean
        return CrowdControl.taunted(target)
    endfunction 

    /* ------------------------------------------ Knockback ----------------------------------------- */
    function KnockbackUnit takes unit target, real angle, real distance, real duration, string model, string point, boolean onCliff, boolean onDestructable, boolean onUnit, boolean stack returns nothing
        call CrowdControl.knockback(target, angle, distance, duration, model, point, onCliff, onDestructable, onUnit, stack)
    endfunction
    
    function IsUnitKnockedBack takes unit target returns boolean
        return CrowdControl.knockedback(target)
    endfunction

    /* ------------------------------------------- Knockup ------------------------------------------ */
    function KnockupUnit takes unit target, real maxHeight, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.knockup(target, maxHeight, duration, model, point, stack)
    endfunction

    function IsUnitKnockedUp takes unit target returns boolean
        return CrowdControl.knockedup(target)
    endfunction

    /* ------------------------------------------- Silence ------------------------------------------ */
    function SilenceUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.silence(target, duration, model, point, stack)
    endfunction

    function IsUnitSilenced takes unit target returns boolean
        return CrowdControl.silenced(target)
    endfunction

    /* -------------------------------------------- Stun -------------------------------------------- */
    function StunUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.stun(target, duration, model, point, stack)
    endfunction

    function IsUnitStunned takes unit target returns boolean
        return CrowdControl.stunned(target)
    endfunction

    /* ---------------------------------------- Movement Slow --------------------------------------- */
    function SlowUnit takes unit target, real amount, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.slow(target, amount, duration, model, point, stack)
    endfunction

    function IsUnitSlowed takes unit target returns boolean
        return CrowdControl.slowed(target)
    endfunction

    /* ----------------------------------------- Attack Slow ---------------------------------------- */
    function SlowUnitAttack takes unit target, real amount, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.slowAttack(target, amount, duration, model, point, stack)
    endfunction

    function IsUnitAttackSlowed takes unit target returns boolean
        return CrowdControl.attackSlowed(target)
    endfunction

    /* ------------------------------------------- Banish ------------------------------------------- */
    function BanishUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.banish(target, duration, model, point, stack)
    endfunction

    function IsUnitBanished takes unit target returns boolean
        return CrowdControl.banished(target)
    endfunction

    /* ------------------------------------------- Ensnare ------------------------------------------ */
    function EnsnareUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.ensnare(target, duration, model, point, stack)
    endfunction

    function IsUnitEnsnared takes unit target returns boolean
        return CrowdControl.ensnared(target)
    endfunction

    /* -------------------------------------------- Purge ------------------------------------------- */
    function PurgeUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.purge(target, duration, model, point, stack)
    endfunction

    function IsUnitPurged takes unit target returns boolean
        return CrowdControl.purged(target)
    endfunction

    /* --------------------------------------------- Hex -------------------------------------------- */
    function HexUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.hex(target, duration, model, point, stack)
    endfunction

    function IsUnitHexed takes unit target returns boolean
        return CrowdControl.hexed(target)
    endfunction

    /* -------------------------------------------- Sleep ------------------------------------------- */
    function SleepUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.sleep(target, duration, model, point, stack)
    endfunction

    function IsUnitSleeping takes unit target returns boolean
        return CrowdControl.sleeping(target)
    endfunction

    /* ------------------------------------------- Cyclone ------------------------------------------ */
    function CycloneUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.cyclone(target, duration, model, point, stack)
    endfunction

    function IsUnitCycloned takes unit target returns boolean
        return CrowdControl.cycloned(target)
    endfunction

    /* ------------------------------------------ Entangle ------------------------------------------ */
    function EntangleUnit takes unit target, real duration, string model, string point, boolean stack returns nothing
        call CrowdControl.entangle(target, duration, model, point, stack)
    endfunction

    function IsUnitEntangled takes unit target returns boolean
        return CrowdControl.entangled(target)
    endfunction

    /* ------------------------------------------- Dispel ------------------------------------------- */
    function UnitDispelCrowdControl takes unit target, integer id returns nothing
        call CrowdControl.dispel(target, id)
    endfunction

    function UnitDispelAllCrowdControl takes unit target returns nothing
        call CrowdControl.dispelAll(target)
    endfunction

    /* ------------------------------------------- Events ------------------------------------------- */
    function RegisterCrowdControlEvent takes integer id, code c returns nothing
        call CrowdControl.register(id, c)
    endfunction

    function RegisterAnyCrowdControlEvent takes code c returns nothing
        call CrowdControl.register(-1, c)
    endfunction

    function GetCrowdControlUnit takes nothing returns unit
        return CrowdControl.unit[CrowdControl.key]
    endfunction

    function GetCrowdControlType takes nothing returns integer
        return CrowdControl.type[CrowdControl.key]
    endfunction

    function GetCrowdControlDuration takes nothing returns real
        return CrowdControl.duration[CrowdControl.key]
    endfunction

    function GetCrowdControlAmount takes nothing returns real
        return CrowdControl.amount[CrowdControl.key]
    endfunction

    function GetCrowdControlModel takes nothing returns string
        return CrowdControl.model[CrowdControl.key]
    endfunction

    function GetCrowdControlBone takes nothing returns string
        return CrowdControl.point[CrowdControl.key]
    endfunction

    function GetCrowdControlStack takes nothing returns boolean
        return CrowdControl.stack[CrowdControl.key]
    endfunction

    function GetCrowdControlRemaining takes unit target, integer id returns real
        return CrowdControl.remaining(target, id)
    endfunction

    function GetTauntSource takes nothing returns unit
        return CrowdControl.source[CrowdControl.key]
    endfunction

    function GetKnockbackAngle takes nothing returns real
        return CrowdControl.angle[CrowdControl.key]
    endfunction

    function GetKnockbackDistance takes nothing returns real
        return CrowdControl.distance[CrowdControl.key]
    endfunction

    function GetKnockupHeight takes nothing returns real
        return CrowdControl.height[CrowdControl.key]
    endfunction

    function GetKnockbackOnCliff takes nothing returns boolean
        return CrowdControl.cliff[CrowdControl.key]
    endfunction

    function GetKnockbackOnDestructable takes nothing returns boolean
        return CrowdControl.destructable[CrowdControl.key]
    endfunction

    function GetKnockbackOnUnit takes nothing returns boolean
        return CrowdControl.agent[CrowdControl.key]
    endfunction

    function SetCrowdControlUnit takes unit u returns nothing
        set CrowdControl.unit[CrowdControl.key] = u
    endfunction

    function SetCrowdControlType takes integer id returns nothing
        if id >= CROWD_CONTROL_SILENCE and id <= CROWD_CONTROL_KNOCKUP then
            set CrowdControl.type[CrowdControl.key] = id
        endif
    endfunction

    function SetCrowdControlDuration takes real duration returns nothing
        set CrowdControl.duration[CrowdControl.key] = duration
    endfunction

    function SetCrowdControlAmount takes real amount returns nothing
        set CrowdControl.amount[CrowdControl.key] = amount
    endfunction

    function SetCrowdControlModel takes string model returns nothing
        set CrowdControl.model[CrowdControl.key] = model
    endfunction

    function SetCrowdControlBone takes string point returns nothing
        set CrowdControl.point[CrowdControl.key] = point
    endfunction

    function SetCrowdControlStack takes boolean stack returns nothing
        set CrowdControl.stack[CrowdControl.key] = stack
    endfunction

    function SetTauntSource takes unit u returns nothing
        set CrowdControl.source[CrowdControl.key] = u
    endfunction

    function SetKnockbackAngle takes real angle returns nothing
        set CrowdControl.angle[CrowdControl.key] = angle
    endfunction

    function SetKnockbackDistance takes real distance returns nothing
        set CrowdControl.distance[CrowdControl.key] = distance
    endfunction

    function SetKnockupHeight takes real height returns nothing
        set CrowdControl.height[CrowdControl.key] = height
    endfunction

    function SetKnockbackOnCliff takes boolean onCliff returns nothing
        set CrowdControl.cliff[CrowdControl.key] = onCliff
    endfunction

    function SetKnockbackOnDestructable takes boolean onDestructable returns nothing
        set CrowdControl.destructable[CrowdControl.key] = onDestructable
    endfunction

    function SetKnockbackOnUnit takes boolean onUnit returns nothing
        set CrowdControl.agent[CrowdControl.key] = onUnit
    endfunction

    /* ---------------------------------------------------------------------------------------------- */
    /*                                             Systems                                            */
    /* ---------------------------------------------------------------------------------------------- */
    /* ------------------------------------------ Knockback ----------------------------------------- */
    struct Knockback
        private static timer timer = CreateTimer()
        private static rect rect = Rect(0., 0., 0., 0.)
        private static constant real period = 0.03125
        private static thistype array array
        private static integer array struct
        private static integer key = -1
        private static thistype temp
    
        private unit unit
        private group group
        private real angle
        private real offset
        private real distance
        private real duration
        private real collision
        private integer id
        private effect effect
        private boolean onCliff
        private boolean onDest
        private boolean onUnit
        
        private method remove takes integer i returns integer
            call DestroyGroup(group)
            call DestroyEffect(effect)
            call BlzPauseUnitEx(unit, false)

            set unit = null
            set group = null
            set effect = null
            set struct[id] = 0
            set array[i] = array[key]
            set key = key - 1

            call deallocate()

            if key == -1 then
                call PauseTimer(timer)
            endif

            return i - 1
        endmethod

        private static method onDestructable takes nothing returns nothing
            local thistype this = temp

            if GetDestructableLife(GetEnumDestructable()) > 0 then
                set duration = 0
                return
            endif
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this
            local real x
            local real y
            local unit u

            loop
                exitwhen i > key
                    set this = array[i]

                    if duration > 0 and UnitAlive(unit) then
                        set duration = duration - period
                        set x = GetUnitX(unit) + offset*Cos(angle)
                        set y = GetUnitY(unit) + offset*Sin(angle)
                        
                        if onUnit and collision > 0 then
                            call GroupEnumUnitsInRange(group, x, y, collision, null)
                            call GroupRemoveUnit(group, unit)

                            loop
                                set u = FirstOfGroup(group)
                                exitwhen u == null
                                    if UnitAlive(u) then
                                        set duration = 0
                                        set u = null
                                        exitwhen true
                                    endif
                                call GroupRemoveUnit(group, u)
                            endloop
                        endif

                        if onDest and duration > 0 and collision > 0 then
                            set temp = this
                            call SetRect(rect, x - collision, y - collision, x + collision, y + collision)
                            call EnumDestructablesInRect(rect, null, function thistype.onDestructable)
                        endif

                        if onCliff and duration > 0 then
                            if GetTerrainCliffLevel(GetUnitX(unit), GetUnitY(unit)) < GetTerrainCliffLevel(x, y) and GetUnitZ(unit) < (GetTerrainCliffLevel(x, y) - GetTerrainCliffLevel(WorldBounds.maxX, WorldBounds.maxY))*bj_CLIFFHEIGHT then
                                set duration = 0
                            endif
                        endif

                        if duration > 0 then
                            call SetUnitX(unit, x)
                            call SetUnitY(unit, y)
                        endif
                    else
                        set i = remove(i)
                    endif
                set i = i + 1
            endloop
        endmethod

        static method knocked takes unit u returns boolean
            return struct[GetUnitUserData(u)] != 0
        endmethod
        
        static method apply takes unit target, real angle, real distance, real duration, string model, string point, boolean onCliff, boolean onDestructable, boolean onUnit returns nothing
            local integer id = GetUnitUserData(target)
            local thistype this

            if duration > 0 and UnitAlive(target) then
                if struct[id] != 0 then
                    set this = struct[id]
                else
                    set this = thistype.allocate()
                    set .id = id
                    set .unit = target
                    set .collision = 2*BlzGetUnitCollisionSize(target)
                    set .group = CreateGroup()
                    set key = key + 1
                    set array[key] = this
                    set struct[id] = this

                    call BlzPauseUnitEx(target, true)

                    if model != null and point != null then
                        set effect = AddSpecialEffectTarget(model, target, point)
                    endif

                    if key == 0 then
                        call TimerStart(timer, period, true, function thistype.onPeriod)
                    endif
                endif

                set .angle = angle
                set .distance = distance
                set .duration = duration
                set .onCliff = onCliff
                set .onDest = onDestructable
                set .onUnit = onUnit
                set .offset = RMaxBJ(0.00000001, distance*period/RMaxBJ(0.00000001, duration))
            endif
        endmethod
    endstruct

    /* ------------------------------------------- Knockup ------------------------------------------ */
    struct Knockup
        private static integer array knocked

        timer timer 
        unit unit
        effect effect
        integer key
        boolean up
        real rate
        real airTime

        static method isUnitKnocked takes unit u returns boolean
            return knocked[GetUnitUserData(u)] > 0
        endmethod

        private static method onPeriod takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            if up then
                set up = false
                call SetUnitFlyHeight(unit, GetUnitDefaultFlyHeight(unit), rate)
                call TimerStart(timer, airTime/2, false, function thistype.onPeriod)
            else
                call DestroyEffect(effect)
                call ReleaseTimer(timer)
                call deallocate()

                set knocked[key] = knocked[key] - 1

                if knocked[key] == 0 then
                    call BlzPauseUnitEx(unit, false)
                endif

                set timer = null
                set unit = null
                set effect = null
            endif
        endmethod

        static method apply takes unit whichUnit, real airTime, real maxHeight, string model, string point returns nothing
            local thistype this

            if airTime > 0 then
                set this = thistype.allocate()
                set timer = NewTimerEx(this)
                set unit = whichUnit
                set rate = maxHeight/airTime
                set .airTime = airTime
                set up = true
                set key = GetUnitUserData(unit)
                set knocked[key] = knocked[key] + 1

                if model != null and point != null then
                    set effect = AddSpecialEffectTarget(model, unit, point)
                endif

                if knocked[key] == 1 then
                    call BlzPauseUnitEx(whichUnit, true)
                endif

                call UnitAddAbility(unit, 'Amrf')
                call UnitRemoveAbility(unit, 'Amrf')
                call SetUnitFlyHeight(unit, (GetUnitDefaultFlyHeight(unit) + maxHeight), rate)
                call TimerStart(timer, airTime/2, false, function thistype.onPeriod)
            endif
        endmethod
    endstruct

    /* -------------------------------------------- Fear -------------------------------------------- */
    struct Fear
        private static timer timer = CreateTimer()
        private static constant integer DIRECTION_CHANGE = 5 
        private static constant real MAX_CHANGE = 200.
        private static constant real PERIOD = 1./5.
        private static integer key = -1
        private static thistype array array
        private static integer array struct
        private static boolean array flag
        private static real array x
        private static real array y
        private static ability ability
        private static unit dummy

        unit unit
        effect effect
        integer id
        integer change

        static method feared takes unit target returns boolean
            return GetUnitAbilityLevel(target, FEAR_BUFF) > 0
        endmethod

        method remove takes integer i returns integer
            set flag[id] = true
            call IssueImmediateOrder(unit, "stop")
            call DestroyEffect(effect)

            set struct[id] = 0
            set unit = null
            set effect = null
            set array[i] = array[key]
            set key = key - 1

            call deallocate()

            if key == -1 then
                call PauseTimer(timer)
            endif

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > key
                    set this = array[i]

                    if GetUnitAbilityLevel(unit, FEAR_BUFF) > 0 then
                        set change = change + 1

                        if change >= DIRECTION_CHANGE then
                            set change = 0
                            set flag[id] = true
                            set x[id] = GetRandomReal(GetUnitX(unit) - MAX_CHANGE, GetUnitX(unit) + MAX_CHANGE)
                            set y[id] = GetRandomReal(GetUnitY(unit) - MAX_CHANGE, GetUnitY(unit) + MAX_CHANGE)
                            call IssuePointOrder(unit, "move", x[id], y[id])
                        endif
                    else
                        set i = remove(i)
                    endif
                set i = i + 1
            endloop
        endmethod

        static method apply takes unit whichUnit, real duration, string model, string point returns nothing
            local integer id = GetUnitUserData(whichUnit)
            local thistype this

            if duration > 0 then
                call BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_NORMAL, 0, duration)
                call BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_HERO, 0, duration)
                call IncUnitAbilityLevel(dummy, FEAR)
                call DecUnitAbilityLevel(dummy, FEAR)

                if IssueTargetOrder(dummy, "drunkenhaze", whichUnit) then
                    if struct[id] != 0 then
                        set this = struct[id]
                    else
                        set this = thistype.allocate()
                        set .id = id
                        set unit = whichUnit
                        set change = 0
                        set key = key + 1
                        set array[key] = this
                        set struct[id] = this
    
                        if model != null and point != null then
                            set effect = AddSpecialEffectTarget(model, whichUnit, point)
                        endif
    
                        if key == 0 then
                            call TimerStart(timer, PERIOD, true, function thistype.onPeriod)
                        endif
                    endif
    
                    set flag[id] = true
                    set x[id] = GetRandomReal(GetUnitX(whichUnit) - MAX_CHANGE, GetUnitX(whichUnit) + MAX_CHANGE)
                    set y[id] = GetRandomReal(GetUnitY(whichUnit) - MAX_CHANGE, GetUnitY(whichUnit) + MAX_CHANGE)
                    call IssuePointOrder(whichUnit, "move", x[id], y[id])
                endif
            endif
        endmethod

        private static method onOrder takes nothing returns nothing
            local unit source = GetOrderedUnit()
            local integer id

            if feared(source) and GetIssuedOrderId() != 851973 then
                set id = GetUnitUserData(source)

                if not flag[id] then
                    set flag[id] = true
                    call IssuePointOrder(source, "move", x[id], y[id])
                else
                    set flag[id] = false
                endif
            endif

            set source = null
        endmethod

        private static method onInit takes nothing returns nothing
            set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetRectCenterX(GetWorldBounds()), GetRectCenterY(GetWorldBounds()), 0, 0)

            call UnitAddAbility(dummy, TRUE_SIGHT)
            call UnitAddAbility(dummy, FEAR)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, function thistype.onOrder)

            set ability = BlzGetUnitAbility(dummy, FEAR)
        endmethod
    endstruct

    /* -------------------------------------------- Taunt ------------------------------------------- */
    struct Taunt
        private static constant real PERIOD = 0.2
        private static unit dummy
        private static integer key = -1
        private static thistype array array
        private static integer array struct
        private static timer timer = CreateTimer()
        private static ability ability

        readonly static unit array source

        unit unit
        effect effect
        integer id
        boolean selected

        static method taunted takes unit target returns boolean
            return GetUnitAbilityLevel(target, TAUNT_BUFF) > 0
        endmethod

        method remove takes integer i returns integer
            call UnitDispelCrowdControl(unit, CROWD_CONTROL_TAUNT)
            call IssueImmediateOrder(unit, "stop")
            call DestroyEffect(effect)

            if selected and UnitAlive(unit) then
                call SelectUnitAddForPlayer(unit, GetOwningPlayer(unit))
            endif

            set struct[id] = 0
            set source[id] = null
            set unit = null
            set effect = null
            set array[i] = array[key]
            set key = key - 1

            call deallocate()

            if key == -1 then
                call PauseTimer(timer)
            endif

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > key
                    set this = array[i]

                    if GetUnitAbilityLevel(unit, TAUNT_BUFF) > 0 and UnitAlive(source[id]) and UnitAlive(unit) then
                        if IsUnitVisible(source[id],  GetOwningPlayer(unit)) then
                            call IssueTargetOrderById(unit, 851983, source[id])
                        else
                            call IssuePointOrderById(unit, 851986, GetUnitX(source[id]), GetUnitY(source[id]))
                        endif
                    else
                        set i = remove(i)
                    endif
                set i = i + 1
            endloop
        endmethod

        static method apply takes unit source, unit target, real duration, string model, string point returns nothing
            local integer id = GetUnitUserData(target)
            local thistype this

            if duration > 0 and UnitAlive(source) and UnitAlive(target) then
                call BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_NORMAL, 0, duration)
                call BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_HERO, 0, duration)
                call IncUnitAbilityLevel(dummy, TAUNT)
                call DecUnitAbilityLevel(dummy, TAUNT)

                if IssueTargetOrder(dummy, "drunkenhaze", target) then
                    if struct[id] != 0 then
                        set this = struct[id]
                    else
                        set this = thistype.allocate()
                        set .id = id
                        set unit = target
                        set selected = IsUnitSelected(target, GetOwningPlayer(target))
                        set key = key + 1
                        set array[key] = this
                        set struct[id] = this
    
                        if selected then
                            call SelectUnit(target, false)
                        endif

                        if model != null and point != null then
                            set effect = AddSpecialEffectTarget(model, target, point)
                        endif
    
                        if key == 0 then
                            call TimerStart(timer, PERIOD, true, function thistype.onPeriod)
                        endif
                    endif

                    set .source[id] = source
                    
                    if IsUnitVisible(source, GetOwningPlayer(target)) then
                        call IssueTargetOrderById(target, 851983, source)
                    else
                        call IssuePointOrderById(target, 851986, GetUnitX(source), GetUnitY(source))
                    endif
                endif
            endif
        endmethod

        private static method onOrder takes nothing returns nothing
            local unit target = GetOrderedUnit()
            local integer order = GetIssuedOrderId()
            local integer id
            
            if taunted(target) and order != 851973 then
                set id = GetUnitUserData(target)

                if order != 851983 and order != 851986 then
                    if IsUnitVisible(source[id],  GetOwningPlayer(target)) then
                        call IssueTargetOrderById(target, 851983, source[id])
                    else 
                        call IssuePointOrderById(target, 851986, GetUnitX(source[id]), GetUnitY(source[id]))
                    endif
                else
                    if GetOrderTargetUnit() != source[id] and GetOrderTargetUnit() != null then
                        if IsUnitVisible(source[id],  GetOwningPlayer(target)) then
                            call IssueTargetOrderById(target, 851983, source[id])
                        else 
                            call IssuePointOrderById(target, 851986, GetUnitX(source[id]), GetUnitY(source[id]))
                        endif
                    endif
                endif
            endif

            set target = null
        endmethod

        private static method onSelect takes nothing returns nothing
            local unit target = GetTriggerUnit()
            
            if taunted(target) then
                if IsUnitSelected(target, GetOwningPlayer(target)) then
                    call SelectUnit(target, false)
                endif
            endif
            
            set target = null
        endmethod

        private static method onInit takes nothing returns nothing
            set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetRectCenterX(GetWorldBounds()), GetRectCenterY(GetWorldBounds()), 0, 0)

            call UnitAddAbility(dummy, TRUE_SIGHT)
            call UnitAddAbility(dummy, TAUNT)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SELECTED, function thistype.onSelect)

            set ability = BlzGetUnitAbility(dummy, TAUNT)
        endmethod
    endstruct

    /* ---------------------------------------- Crowd Control --------------------------------------- */
    struct CrowdControl extends array
        private static trigger trigger = CreateTrigger()
        private static hashtable timer = InitHashtable()
        private static trigger array event
        private static integer array ability
        private static integer array buff
        private static string array order
        private static integer count = 0
        private static unit dummy

        readonly static integer key = -1
        
        static unit array unit
        static unit array source
        static real array amount
        static real array duration
        static real array angle
        static real array distance
        static real array height
        static string array model
        static string array point
        static boolean array stack 
        static boolean array cliff 
        static boolean array destructable 
        static boolean array agent 
        static integer array type

        private static method onInit takes nothing returns nothing
            set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetRectCenterX(GetWorldBounds()), GetRectCenterY(GetWorldBounds()), 0, 0)   
            
            call UnitAddAbility(dummy, SILENCE)
            call UnitAddAbility(dummy, STUN)
            call UnitAddAbility(dummy, ATTACK_SLOW)
            call UnitAddAbility(dummy, MOVEMENT_SLOW)
            call UnitAddAbility(dummy, BANISH)
            call UnitAddAbility(dummy, ENSNARE)
            call UnitAddAbility(dummy, PURGE)
            call UnitAddAbility(dummy, HEX)
            call UnitAddAbility(dummy, SLEEP)
            call UnitAddAbility(dummy, CYCLONE)
            call UnitAddAbility(dummy, ENTANGLE)
            call UnitAddAbility(dummy, DISARM)
            call UnitAddAbility(dummy, TRUE_SIGHT)

            call BlzUnitDisableAbility(dummy, SILENCE, true, true)
            call BlzUnitDisableAbility(dummy, STUN, true, true)
            call BlzUnitDisableAbility(dummy, ATTACK_SLOW, true, true)
            call BlzUnitDisableAbility(dummy, MOVEMENT_SLOW, true, true)
            call BlzUnitDisableAbility(dummy, BANISH, true, true)
            call BlzUnitDisableAbility(dummy, ENSNARE, true, true)
            call BlzUnitDisableAbility(dummy, PURGE, true, true)
            call BlzUnitDisableAbility(dummy, HEX, true, true)
            call BlzUnitDisableAbility(dummy, SLEEP, true, true)
            call BlzUnitDisableAbility(dummy, CYCLONE, true, true)
            call BlzUnitDisableAbility(dummy, ENTANGLE, true, true)
            call BlzUnitDisableAbility(dummy, DISARM, true, true)

            set ability[CROWD_CONTROL_SILENCE] = SILENCE
            set ability[CROWD_CONTROL_STUN] = STUN
            set ability[CROWD_CONTROL_SLOW] = MOVEMENT_SLOW
            set ability[CROWD_CONTROL_SLOW_ATTACK] = ATTACK_SLOW
            set ability[CROWD_CONTROL_BANISH] = BANISH
            set ability[CROWD_CONTROL_ENSNARE] = ENSNARE
            set ability[CROWD_CONTROL_PURGE] = PURGE
            set ability[CROWD_CONTROL_HEX] = HEX
            set ability[CROWD_CONTROL_SLEEP] = SLEEP
            set ability[CROWD_CONTROL_CYCLONE] = CYCLONE
            set ability[CROWD_CONTROL_ENTANGLE] = ENTANGLE
            set ability[CROWD_CONTROL_DISARM] = DISARM
            set ability[CROWD_CONTROL_FEAR] = FEAR
            set ability[CROWD_CONTROL_TAUNT] = TAUNT

            set buff[CROWD_CONTROL_SILENCE] = SILENCE_BUFF
            set buff[CROWD_CONTROL_STUN] = STUN_BUFF
            set buff[CROWD_CONTROL_SLOW] = MOVEMENT_SLOW_BUFF
            set buff[CROWD_CONTROL_SLOW_ATTACK] = ATTACK_SLOW_BUFF
            set buff[CROWD_CONTROL_BANISH] = BANISH_BUFF
            set buff[CROWD_CONTROL_ENSNARE] = ENSNARE_BUFF
            set buff[CROWD_CONTROL_PURGE] = PURGE_BUFF
            set buff[CROWD_CONTROL_HEX] = HEX_BUFF
            set buff[CROWD_CONTROL_SLEEP] = SLEEP_BUFF
            set buff[CROWD_CONTROL_CYCLONE] = CYCLONE_BUFF
            set buff[CROWD_CONTROL_ENTANGLE] = ENTANGLE_BUFF
            set buff[CROWD_CONTROL_DISARM] = DISARM_BUFF
            set buff[CROWD_CONTROL_FEAR] = FEAR_BUFF
            set buff[CROWD_CONTROL_TAUNT] = TAUNT_BUFF

            set order[CROWD_CONTROL_SILENCE] = "drunkenhaze"
            set order[CROWD_CONTROL_STUN] = "thunderbolt"
            set order[CROWD_CONTROL_SLOW] = "cripple"
            set order[CROWD_CONTROL_SLOW_ATTACK] = "cripple"
            set order[CROWD_CONTROL_BANISH] = "banish"
            set order[CROWD_CONTROL_ENSNARE] = "ensnare"
            set order[CROWD_CONTROL_PURGE] = "purge"
            set order[CROWD_CONTROL_HEX] = "hex"
            set order[CROWD_CONTROL_SLEEP] = "sleep"
            set order[CROWD_CONTROL_CYCLONE] = "cyclone"
            set order[CROWD_CONTROL_ENTANGLE] = "entanglingroots"
            set order[CROWD_CONTROL_DISARM] = "drunkenhaze"
        endmethod

        private static method onExpire takes nothing returns nothing
            local timer t = GetExpiredTimer()

            call RemoveSavedHandle(timer, GetHandleId(LoadUnitHandle(timer, GetHandleId(t), 0)), LoadInteger(timer, GetHandleId(t), 1))
            call FlushChildHashtable(timer, GetHandleId(t))
            call DestroyTimer(t)

            set t = null
        endmethod

        private static method onEvent takes integer key returns nothing
            local integer i = 0
            local integer next = -1
            local integer prev = -2

            set count = count + 1

            if count - CROWD_CONTROL_KNOCKUP < RECURSION_LIMIT then
                loop
                    exitwhen type[key] == next or (i - CROWD_CONTROL_KNOCKUP > RECURSION_LIMIT)
                        set next = type[key]
    
                        if event[next] != null then
                            call TriggerEvaluate(event[next])
                        endif

                        if type[key] != next then
                            set i = i + 1
                        else
                            if next != prev then
                                call TriggerEvaluate(trigger)

                                if type[key] != next then
                                    set i = i + 1
                                    set prev = next
                                endif
                            endif
                        endif
                endloop
            endif
            
            set count = count - 1
            set .key = key
        endmethod

        private static method cast takes unit source, unit target, real amount, real angle, real distance, real height, real duration, string model, string point, boolean stack, boolean onCliff, boolean onDestructable, boolean onUnit, integer id returns nothing
            local ability spell
            local timer t
    
            if not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) and UnitAlive(target) and duration > 0 then
                set key = key + 1
                set .unit[key] = target
                set .source[key] = source
                set .amount[key] = amount
                set .angle[key] = angle
                set .distance[key] = distance
                set .height[key] = height
                set .duration[key] = duration
                set .model[key] = model
                set .point[key] = point
                set .stack[key] = stack
                set .cliff[key] = onCliff
                set .destructable[key] = onDestructable
                set .agent[key] = onUnit
                set .type[key] = id

                call onEvent(key)
        
                static if LIBRARY_Tenacity then
                    set .duration[key] = GetTenacityDuration(unit[key], .duration[key])
                endif

                if .duration[key] > 0 and UnitAlive(unit[key]) then
                    if not HaveSavedHandle(timer, GetHandleId(unit[key]), type[key]) then
                        set t = CreateTimer()
                        call SaveTimerHandle(timer, GetHandleId(unit[key]), type[key], t)
                        call SaveUnitHandle(timer, GetHandleId(t), 0, unit[key])
                        call SaveInteger(timer, GetHandleId(t), 1, type[key])
                    endif

                    if .stack[key] then
                        if type[key] != CROWD_CONTROL_TAUNT then
                            set .duration[key] = .duration[key] + TimerGetRemaining(LoadTimerHandle(timer, GetHandleId(unit[key]), type[key]))
                        else
                            if Taunt.source[GetUnitUserData(unit[key])] == .source[key] then
                                set .duration[key] = .duration[key] + TimerGetRemaining(LoadTimerHandle(timer, GetHandleId(unit[key]), type[key]))
                            endif
                        endif
                    endif

                    if type[key] != CROWD_CONTROL_FEAR and type[key] != CROWD_CONTROL_TAUNT and type[key] != CROWD_CONTROL_KNOCKBACK and type[key] != CROWD_CONTROL_KNOCKUP then
                        set spell = BlzGetUnitAbility(dummy, ability[type[key]])

                        call BlzUnitDisableAbility(dummy, ability[type[key]], false, false)
                        call BlzSetAbilityRealLevelField(spell, ABILITY_RLF_DURATION_NORMAL, 0, .duration[key])
                        call BlzSetAbilityRealLevelField(spell, ABILITY_RLF_DURATION_HERO, 0, .duration[key])

                        if type[key] == CROWD_CONTROL_SLOW then
                            call BlzSetAbilityRealLevelField(spell, ABILITY_RLF_MOVEMENT_SPEED_REDUCTION_PERCENT_CRI1, 0, .amount[key])
                        elseif type[key] == CROWD_CONTROL_SLOW_ATTACK then
                            call BlzSetAbilityRealLevelField(spell, ABILITY_RLF_ATTACK_SPEED_REDUCTION_PERCENT_CRI2, 0, .amount[key])
                        endif

                        call IncUnitAbilityLevel(dummy, ability[type[key]])
                        call DecUnitAbilityLevel(dummy, ability[type[key]])

                        if IssueTargetOrder(dummy, order[type[key]], unit[key]) then
                            call UnitRemoveAbility(unit[key], buff[type[key]])
                            call IssueTargetOrder(dummy, order[type[key]], unit[key])
                            call TimerStart(LoadTimerHandle(timer, GetHandleId(unit[key]), type[key]), .duration[key], false, function thistype.onExpire)

                            if .model[key] != null and .model[key] != "" then
                                if .point[key] != null and .point[key] != "" then
                                    call LinkEffectToBuff(unit[key], buff[type[key]], .model[key], .point[key])
                                else
                                    call DestroyEffect(AddSpecialEffect(.model[key], GetUnitX(unit[key]), GetUnitY(unit[key])))
                                endif
                            endif
                        else
                            call RemoveSavedHandle(timer, GetHandleId(unit[key]), type[key])
                            call FlushChildHashtable(timer, GetHandleId(t))
                            call DestroyTimer(t)
                        endif

                        call BlzUnitDisableAbility(dummy, ability[type[key]], true, true)
                    else
                        if type[key] == CROWD_CONTROL_FEAR then
                            call Fear.apply(unit[key], .duration[key], .model[key], .point[key])
                        elseif type[key] == CROWD_CONTROL_TAUNT then
                            call Taunt.apply(.source[key], unit[key], .duration[key], .model[key], .point[key])
                        elseif type[key] == CROWD_CONTROL_KNOCKBACK then
                            call Knockback.apply(unit[key], .angle[key], .distance[key], .duration[key], .model[key], .point[key], .cliff[key], .destructable[key], .agent[key])
                        elseif type[key] == CROWD_CONTROL_KNOCKUP then
                            call Knockup.apply(unit[key], .duration[key], .height[key], .model[key], .point[key])
                        endif

                        call TimerStart(LoadTimerHandle(timer, GetHandleId(unit[key]), type[key]), .duration[key], false, function thistype.onExpire)
                    endif
                endif
    
                if key > -1 then
                    set key = key - 1
                endif
            endif
    
            set t = null
            set spell = null
        endmethod

        static method silence takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_SILENCE)
        endmethod
    
        static method silenced takes unit target returns boolean
            return GetUnitAbilityLevel(target, SILENCE_BUFF) > 0
        endmethod

        static method stun takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_STUN)
        endmethod
    
        static method stunned takes unit target returns boolean
            return GetUnitAbilityLevel(target, STUN_BUFF) > 0
        endmethod

        static method slow takes unit target, real amount, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, amount, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_SLOW)
        endmethod
    
        static method slowed takes unit target returns boolean
            return GetUnitAbilityLevel(target, MOVEMENT_SLOW_BUFF) > 0
        endmethod

        static method slowAttack takes unit target, real amount, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, amount, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_SLOW_ATTACK)
        endmethod
    
        static method attackSlowed takes unit target returns boolean
            return GetUnitAbilityLevel(target, ATTACK_SLOW_BUFF) > 0
        endmethod

        static method banish takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_BANISH)
        endmethod
    
        static method banished takes unit target returns boolean
            return GetUnitAbilityLevel(target, BANISH_BUFF) > 0
        endmethod

        static method ensnare takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_ENSNARE)
        endmethod
    
        static method ensnared takes unit target returns boolean
            return GetUnitAbilityLevel(target, ENSNARE_BUFF) > 0
        endmethod

        static method purge takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_PURGE)
        endmethod
    
        static method purged takes unit target returns boolean
            return GetUnitAbilityLevel(target, PURGE_BUFF) > 0
        endmethod

        static method hex takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_HEX)
        endmethod
    
        static method hexed takes unit target returns boolean
            return GetUnitAbilityLevel(target, HEX_BUFF) > 0
        endmethod

        static method sleep takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_SLEEP)
        endmethod
    
        static method sleeping takes unit target returns boolean
            return GetUnitAbilityLevel(target, SLEEP_BUFF) > 0
        endmethod

        static method cyclone takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_CYCLONE)
        endmethod
    
        static method cycloned takes unit target returns boolean
            return GetUnitAbilityLevel(target, CYCLONE_BUFF) > 0
        endmethod

        static method entangle takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_ENTANGLE)
        endmethod
    
        static method entangled takes unit target returns boolean
            return GetUnitAbilityLevel(target, ENTANGLE_BUFF) > 0
        endmethod
        
        static method knockback takes unit target, real angle, real distance, real duration, string model, string point, boolean onCliff, boolean onDestructable, boolean onUnit, boolean stack returns nothing
            call cast(null, target, 0, angle, distance, 0, duration, model, point, stack, onCliff, onDestructable, onUnit, CROWD_CONTROL_KNOCKBACK)
        endmethod
    
        static method knockedback takes unit target returns boolean
            return Knockback.knocked(target)
        endmethod

        static method knockup takes unit target, real maxHeight, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, maxHeight, duration, model, point, stack, false, false, false, CROWD_CONTROL_KNOCKUP)
        endmethod
    
        static method knockedup takes unit target returns boolean
            return Knockup.isUnitKnocked(target)
        endmethod

        static method fear takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_FEAR)
        endmethod
    
        static method feared takes unit target returns boolean
            return Fear.feared(target)
        endmethod

        static method disarm takes unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(null, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_DISARM)
        endmethod
    
        static method disarmed takes unit target returns boolean
            return GetUnitAbilityLevel(target, DISARM_BUFF) > 0
        endmethod

        static method taunt takes unit source, unit target, real duration, string model, string point, boolean stack returns nothing
            call cast(source, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_TAUNT)
        endmethod
    
        static method taunted takes unit target returns boolean
            return Taunt.taunted(target)
        endmethod

        static method dispel takes unit target, integer id returns nothing
            local timer t

            if buff[id] != 0 then
                call UnitRemoveAbility(target, buff[id])

                if HaveSavedHandle(timer, GetHandleId(target), id) then
                    set t = LoadTimerHandle(timer, GetHandleId(target), id)
                    call RemoveSavedHandle(timer, GetHandleId(target), id)
                    call FlushChildHashtable(timer, GetHandleId(t))
                    call DestroyTimer(t)
                endif
            endif

            set t = null
        endmethod

        static method dispelAll takes unit target returns nothing
            call dispel(target, CROWD_CONTROL_SILENCE)
            call dispel(target, CROWD_CONTROL_STUN)
            call dispel(target, CROWD_CONTROL_SLOW)
            call dispel(target, CROWD_CONTROL_SLOW_ATTACK)
            call dispel(target, CROWD_CONTROL_BANISH)
            call dispel(target, CROWD_CONTROL_ENSNARE)
            call dispel(target, CROWD_CONTROL_PURGE)
            call dispel(target, CROWD_CONTROL_HEX)
            call dispel(target, CROWD_CONTROL_SLEEP)
            call dispel(target, CROWD_CONTROL_CYCLONE)
            call dispel(target, CROWD_CONTROL_ENTANGLE)
            call dispel(target, CROWD_CONTROL_DISARM)
            call dispel(target, CROWD_CONTROL_FEAR)
            call dispel(target, CROWD_CONTROL_TAUNT)
        endmethod

        static method remaining takes unit target, integer id returns real
            return TimerGetRemaining(LoadTimerHandle(timer, GetHandleId(target), id))
        endmethod

        static method register takes integer id, code c returns nothing
            if id >= CROWD_CONTROL_SILENCE and id <= CROWD_CONTROL_KNOCKUP then
                if event[id] == null then
                    set event[id] = CreateTrigger()
                endif
                call TriggerAddCondition(event[id], Filter(c))
            else
                call TriggerAddCondition(trigger, Filter(c))
            endif
        endmethod
    endstruct
endlibrary
library DamageInterface requires optional Table
    /* --------------------- DamageInterface v2.4 by Chopinski --------------------- */
    // Allows for easy registration of specific damage type events like on attack
    // damage or on spell damage, etc...
    /* ----------------------------------- END ---------------------------------- */
    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // This constant is used to define if the system will cache
        // extra information from a Damage Event, like the unit
        // Custom value (UnitUserData), a unit Handle Id, and more
        // Additionaly you can see the CacheResponse method below
        // to have an idea and comment the members you want cached or not
        private constant boolean CACHE_EXTRA = true
        // Since Table can be a bit slow due to using only one hastable
        // for everything, you can choose to use it or not with this
        // system in particular.
        private constant boolean USE_TABLE   = false
    endglobals

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct Unit
        unit    unit
        player  player
        integer handle
        boolean isHero
        boolean isMelee
        boolean isRanged
        boolean isStructure
        boolean isMagicImmune
        integer id
        real    x
        real    y
        real    z
        
        static method create takes nothing returns thistype
            return thistype.allocate()
        endmethod
    endstruct

    struct Damage extends array
        static if LIBRARY_Table and USE_TABLE then
            private static HashTable after
            private static HashTable before
        else
            private static hashtable after = InitHashtable()
            private static hashtable before = InitHashtable()
        endif

        static trigger damaged = CreateTrigger()
        static trigger damaging = CreateTrigger()
        readonly static location location = Location(0, 0)
        
        readonly static damagetype damagetype
        readonly static attacktype attacktype
        readonly static Unit       source
        readonly static Unit       target
        readonly static boolean    isSpell
        readonly static boolean    isAttack
        readonly static boolean    isEnemy
        readonly static boolean    isAlly

        private static method GetUnitZ takes unit u returns real
            call MoveLocation(location, GetUnitX(u), GetUnitY(u))
            return GetUnitFlyHeight(u) + GetLocationZ(location)
        endmethod

        private static method cache takes unit src, unit tgt, damagetype dmg_type, attacktype atk_type returns nothing
            set damagetype  = dmg_type
            set attacktype  = atk_type
            set source.unit = src
            set target.unit = tgt
            set isAttack    = damagetype == DAMAGE_TYPE_NORMAL
            set isSpell     = attacktype == ATTACK_TYPE_NORMAL

            // You can comment the members you dont want to be cached
            // or set CACHE_EXTRA = false to not save them at all
            if CACHE_EXTRA then
                set source.player        = GetOwningPlayer(src)
                set target.player        = GetOwningPlayer(tgt)
                set isEnemy              = IsUnitEnemy(tgt, source.player)
                set isAlly               = IsUnitAlly(tgt, source.player)
                set source.isMelee       = IsUnitType(src, UNIT_TYPE_MELEE_ATTACKER)
                set source.isRanged      = IsUnitType(src, UNIT_TYPE_RANGED_ATTACKER)
                set target.isMelee       = IsUnitType(tgt, UNIT_TYPE_MELEE_ATTACKER)
                set target.isRanged      = IsUnitType(tgt, UNIT_TYPE_RANGED_ATTACKER)
                set source.isHero        = IsUnitType(src, UNIT_TYPE_HERO)
                set target.isHero        = IsUnitType(tgt, UNIT_TYPE_HERO)
                set source.isStructure   = IsUnitType(src, UNIT_TYPE_STRUCTURE)
                set target.isStructure   = IsUnitType(tgt, UNIT_TYPE_STRUCTURE)
                set source.isMagicImmune = IsUnitType(src, UNIT_TYPE_MAGIC_IMMUNE)
                set target.isMagicImmune = IsUnitType(tgt, UNIT_TYPE_MAGIC_IMMUNE)
                set source.x             = GetUnitX(src)
                set source.y             = GetUnitY(src)
                set source.z             = GetUnitZ(src)
                set target.x             = GetUnitX(tgt)
                set target.y             = GetUnitY(tgt)
                set target.z             = GetUnitZ(tgt)
                set source.id            = GetUnitUserData(src)
                set target.id            = GetUnitUserData(tgt)
                set source.handle        = GetHandleId(src)
                set target.handle        = GetHandleId(tgt)
            endif
        endmethod

        private static method onDamaging takes nothing returns nothing
            local integer i
            local integer j
            
            call cache(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())

            if damagetype != DAMAGE_TYPE_UNKNOWN then
                set i = GetHandleId(attacktype)
                set j = GetHandleId(damagetype)

                static if LIBRARY_Table and USE_TABLE then
                    if before[i].trigger[0] != null then
                        call TriggerEvaluate(before[i].trigger[0])
                    endif

                    if before[0].trigger[j] != null then
                        call TriggerEvaluate(before[0].trigger[j])
                    endif
                    
                    if before[i].trigger[j] != null then
                        call TriggerEvaluate(before[i].trigger[j])
                    endif
                else
                    if HaveSavedHandle(before, i, 0) then
                        call TriggerEvaluate(LoadTriggerHandle(before, i, 0))
                    endif

                    if HaveSavedHandle(before, 0, j) then
                        call TriggerEvaluate(LoadTriggerHandle(before, 0, j))
                    endif
                    
                    if HaveSavedHandle(before, i, j) then
                        call TriggerEvaluate(LoadTriggerHandle(before, i, j))
                    endif
                endif
            endif
        endmethod

        private static method onDamage takes nothing returns nothing
            local integer i
            local integer j
            
            call cache(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())

            if damagetype != DAMAGE_TYPE_UNKNOWN then
                set i = GetHandleId(attacktype)
                set j = GetHandleId(damagetype)
    
                static if LIBRARY_Table and USE_TABLE then
                    if after[i].trigger[0] != null then
                        call TriggerEvaluate(after[i].trigger[0])
                    endif

                    if after[0].trigger[j] != null then
                        static if LIBRARY_Evasion then
                            if isAttack then
                                if not Evasion.evade then
                                    call TriggerEvaluate(after[0].trigger[j])
                                endif
                            else
                                call TriggerEvaluate(after[0].trigger[j])
                            endif
                        else
                            call TriggerEvaluate(after[0].trigger[j])
                        endif
                    endif
                    
                    if after[i].trigger[j] != null then
                        call TriggerEvaluate(after[i].trigger[j])
                    endif
                else
                    if HaveSavedHandle(after, i, 0) then
                        call TriggerEvaluate(LoadTriggerHandle(after, i, 0))
                    endif

                    if HaveSavedHandle(after, 0, j) then
                        static if LIBRARY_Evasion then
                            if isAttack then
                                if not Evasion.evade then
                                    call TriggerEvaluate(LoadTriggerHandle(after, 0, j))
                                endif
                            else
                                call TriggerEvaluate(LoadTriggerHandle(after, 0, j))
                            endif
                        else
                            call TriggerEvaluate(LoadTriggerHandle(after, 0, j))
                        endif
                    endif
                    
                    if HaveSavedHandle(after, i, j) then
                        call TriggerEvaluate(LoadTriggerHandle(after, i, j))
                    endif
                endif
            endif
        endmethod

        static method register takes attacktype attack, damagetype damage, code c, boolean onDamaged returns nothing
            local integer i = GetHandleId(attack)
            local integer j = GetHandleId(damage)

            if onDamaged then
                static if LIBRARY_Table and USE_TABLE then
                    if after[i].trigger[j] == null then
                        set after[i].trigger[j] = CreateTrigger()
                    endif
                    call TriggerAddCondition(after[i].trigger[j], Filter(c))
                else
                    if not HaveSavedHandle(after, i, j) then
                        call SaveTriggerHandle(after, i, j, CreateTrigger())
                    endif
                    call TriggerAddCondition(LoadTriggerHandle(after, i, j), Filter(c))
                endif
            else
                static if LIBRARY_Table and USE_TABLE then
                    if before[i].trigger[j] == null then
                        set before[i].trigger[j] = CreateTrigger()
                    endif
                    call TriggerAddCondition(before[i].trigger[j], Filter(c))
                else
                    if not HaveSavedHandle(before, i, j) then
                        call SaveTriggerHandle(before, i, j, CreateTrigger())
                    endif
                    call TriggerAddCondition(LoadTriggerHandle(before, i, j), Filter(c))
                endif
            endif
        endmethod
    
        static method onInit takes nothing returns nothing
            static if LIBRARY_Table and USE_TABLE then
                set after = HashTable.create()
                set before = HashTable.create()
            endif
            set source = Unit.create()
            set target = Unit.create()

            call TriggerRegisterAnyUnitEventBJ(damaged, EVENT_PLAYER_UNIT_DAMAGED)
            call TriggerAddCondition(damaged, Condition(function thistype.onDamage))

            call TriggerRegisterAnyUnitEventBJ(damaging, EVENT_PLAYER_UNIT_DAMAGING)
            call TriggerAddCondition(damaging, Condition(function thistype.onDamaging))
        endmethod
    endstruct
    
    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function RegisterAttackDamageEvent takes code c returns nothing
        call Damage.register(null, DAMAGE_TYPE_NORMAL, c, true)
    endfunction
    
    function RegisterSpellDamageEvent takes code c returns nothing
        call Damage.register(ATTACK_TYPE_NORMAL, null, c, true)
    endfunction

    function RegisterDamageEvent takes attacktype attack, damagetype damage, code c returns nothing
        call Damage.register(attack, damage, c, true)
    endfunction

    function RegisterAnyDamageEvent takes code c returns nothing
        call TriggerAddCondition(Damage.damaged, Filter(c))
    endfunction

    function RegisterAttackDamagingEvent takes code c returns nothing
        call Damage.register(null, DAMAGE_TYPE_NORMAL, c, false)
    endfunction
    
    function RegisterSpellDamagingEvent takes code c returns nothing
        call Damage.register(ATTACK_TYPE_NORMAL, null, c, false)
    endfunction

    function RegisterDamagingEvent takes attacktype attack, damagetype damage, code c returns nothing
        call Damage.register(attack, damage, c, false)
    endfunction 

    function RegisterAnyDamagingEvent takes code c returns nothing
        call TriggerAddCondition(Damage.damaging, Filter(c))
    endfunction
endlibrary
library Evasion requires DamageInterface
    /* ------------------------ Evasion v2.4 by Chopinski ----------------------- */
    // Evasion implements an easy way to register and detect a custom evasion event.

    // It works by monitoring custom evasion and missing values given to units,
    // and nulling damage when the odds given occurs.

    // It will only detect custom evasion, so all evasion or miss values given to a
    // unit must be done so using the public API provided by this system.

    // *Evasion requires DamageInterface. Do not use TriggerSleepAction() with Evasion.

    // The API:
    //     function RegisterEvasionEvent(function YourFunction)
    //         -> YourFunction will run when a unit evades an attack.

    //     function GetMissingUnit takes nothing returns unit
    //         -> Returns the unit missing the attack

    //     function GetEvadingUnit takes nothing returns unit
    //         -> Returns the unit evading the attack

    //     function GetEvadedDamage takes nothing returns real
    //         -> Returns the amount of evaded damage

    //     function GetUnitEvasionChance takes unit u returns real
    //         -> Returns this system amount of evasion chance given to a unit

    //     function GetUnitMissChance takes unit u returns real
    //         -> Returns this system amount of miss chance given to a unit

    //     function SetUnitEvasionChance takes unit u, real chance returns nothing
    //         -> Sets unit evasion chance to specified amount

    //     function SetUnitMissChance takes unit u, real chance returns nothing
    //         -> Sets unit miss chance to specified amount

    //     function UnitAddEvasionChance takes unit u, real chance returns nothing
    //         -> Add to a unit Evasion chance the specified amount

    //     function UnitAddMissChance takes unit u, real chance returns nothing
    //         -> Add to a unit Miss chance the specified amount

    //     function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
    //         -> Will make a unit never miss attacks no matter the evasion chance of the attacked unit

    //     function DoUnitNeverMiss takes unit u returns boolean
    //         -> Returns true if the unit will never miss an attack
    /* ----------------------------------- END ---------------------------------- */
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct Evasion
        static Unit             source
        static Unit             target
        static real             damage
        static real    array    evasion
        static real    array    miss
        static integer array    neverMiss
        readonly static boolean evade     = false
        readonly static trigger trigger   = CreateTrigger()
        static constant real    TEXT_SIZE = 0.016

        static method getEvasionChance takes unit u returns real
            return evasion[GetUnitUserData(u)]
        endmethod

        static method getMissChance takes unit u returns real
            return miss[GetUnitUserData(u)]
        endmethod

        static method setEvasionChance takes unit u, real value returns nothing
            set evasion[GetUnitUserData(u)] = value
        endmethod

        static method setMissChance takes unit u, real value returns nothing
            set miss[GetUnitUserData(u)] = value
        endmethod

        static method text takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
            local texttag tx = CreateTextTag()
            
            call SetTextTagText(tx, text, TEXT_SIZE)
            call SetTextTagPosUnit(tx, whichUnit, 0)
            call SetTextTagColor(tx, red, green, blue, alpha)
            call SetTextTagLifespan(tx, duration)
            call SetTextTagVelocity(tx, 0.0, 0.0355)
            call SetTextTagPermanent(tx, false)
            
            set tx = null
        endmethod

        static method onDamage takes nothing returns nothing
            local real amount = GetEventDamage()
        
            set evade = false
            if amount > 0 and not (neverMiss[Damage.source.id] > 0) then
                set evade = GetRandomReal(0, 100) <= evasion[Damage.target.id] or GetRandomReal(0, 100) <= miss[Damage.source.id]
                if evade then
                    set source = Damage.source
                    set target = Damage.target
                    set damage = amount

                    call TriggerEvaluate(trigger)
                    call BlzSetEventDamage(0)
                    call BlzSetEventWeaponType(WEAPON_TYPE_WHOKNOWS)
                    call text(source.unit, "miss", 1.5, 255, 0, 0, 255)

                    set damage = 0
                    set source = 0
                    set target = 0
                endif
            endif
        endmethod

        static method register takes code c returns nothing
            call TriggerAddCondition(trigger, Filter(c))
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterAttackDamagingEvent(function thistype.onDamage)
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function RegisterEvasionEvent takes code c returns nothing
        call Evasion.register(c)
    endfunction

    function GetMissingUnit takes nothing returns unit
        return Evasion.source.unit
    endfunction

    function GetEvadingUnit takes nothing returns unit
        return Evasion.target.unit
    endfunction

    function GetEvadedDamage takes nothing returns real
        return Evasion.damage
    endfunction

    function GetUnitEvasionChance takes unit u returns real
        return Evasion.getEvasionChance(u)
    endfunction

    function GetUnitMissChance takes unit u returns real
        return Evasion.getMissChance(u)
    endfunction

    function SetUnitEvasionChance takes unit u, real chance returns nothing
        call Evasion.setEvasionChance(u, chance)
    endfunction

    function SetUnitMissChance takes unit u, real chance returns nothing
        call Evasion.setMissChance(u, chance)
    endfunction

    function UnitAddEvasionChance takes unit u, real chance returns nothing
        call Evasion.setEvasionChance(u, Evasion.getEvasionChance(u) + chance)
    endfunction

    function UnitAddMissChance takes unit u, real chance returns nothing
        call Evasion.setMissChance(u, Evasion.getMissChance(u) + chance)
    endfunction

    function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
        if flag then
            set Evasion.neverMiss[GetUnitUserData(u)] = Evasion.neverMiss[GetUnitUserData(u)] + 1
        else
            set Evasion.neverMiss[GetUnitUserData(u)] = Evasion.neverMiss[GetUnitUserData(u)] - 1
        endif
    endfunction

    function DoUnitNeverMiss takes unit u returns boolean
        return Evasion.neverMiss[GetUnitUserData(u)] > 0
    endfunction
endlibrary
library CriticalStrike requires DamageInterface optional Evasion
    /* ------------------------ CriticalStrike v2.4 by Chopinski ----------------------- */
    // CriticalStrike implements an easy way to register and detect a custom critical event.
    // allows the manipulation of a unit critical strike chance and multiplier, as well as
    // manipulating the critical damage dealt.

    // It works by monitoring custom critical strike chance and multiplier values given to units.

    // It will only detect custom critical strikes, so all critical chance given to a
    // unit must be done so using the public API provided by this system.

    // *CriticalStrike requires DamageInterface. Do not use TriggerSleepAction() with Evasion.
    // It also requires optional Evasion so that this library is written after the Evasion
    // library, so both custom events will not fire at the same time.

    // The API:
    //     function RegisterCriticalStrikeEvent(function YourFunction)
    //         -> YourFunction will run when a unit hits a critical strike.

    //     function GetCriticalSource takes nothing returns unit
    //         -> Returns the unit hitting a critical strike.
    
    //     function GetCriticalTarget takes nothing returns unit
    //         -> Returns the unit being hit by a critical strike.
    
    //     function GetCriticalDamage takes nothing returns real
    //         -> Returns the critical strike damage amount.
    
    //     function GetUnitCriticalChance takes unit u returns real
    //         -> Returns the chance to hit a critical strike to specified unit.
    
    //     function GetUnitCriticalMultiplier takes unit u returns real
    //         -> Returns the chance to hit a critical strike to specified unit.
    
    //     function SetUnitCriticalChance takes unit u, real value returns nothing
    //         -> Set's the unit chance to hit a critical strike to specified value.
    //         -> 15.0 = 15%
    
    //     function SetUnitCriticalMultiplier takes unit u, real value returns nothing
    //         -> Set's the unit multiplier of damage when hitting a critical to value
    //         -> 1.0 = increases the multiplier by 1. all units have a multiplier of 1.0
    //             by default, so by adding 1.0, for example, the critical damage will be 
    //             2x the normal damage

    //     function SetCriticalEventDamage takes real newValue returns nothing
    //         -> Modify the critical damage dealt to the specified value.
    
    //     function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
    //         -> Adds the specified values of chance and multiplier to a unit
    /* ----------------------------------- END ---------------------------------- */
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct Critical
        static constant real    TEXT_SIZE = 0.016
        readonly static trigger trigger = CreateTrigger()
        static Unit             source
        static Unit             target
        static real             damage
        static real array       chance
        static real array       multiplier

        static method getChance takes unit u returns real
            return chance[GetUnitUserData(u)]
        endmethod

        static method getMultiplier takes unit u returns real
            return multiplier[GetUnitUserData(u)]
        endmethod

        static method setChance takes unit u, real value returns nothing
            set chance[GetUnitUserData(u)] = value
        endmethod

        static method setMultiplier takes unit u, real value returns nothing
            set multiplier[GetUnitUserData(u)] = value
        endmethod

        static method add takes unit u, real chance, real multuplier returns nothing
            call setChance(u, getChance(u) + chance)
            call setMultiplier(u, getMultiplier(u) + multuplier)
        endmethod

        static method text takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
            local texttag tx = CreateTextTag()
            
            call SetTextTagText(tx, text, TEXT_SIZE)
            call SetTextTagPosUnit(tx, whichUnit, 0)
            call SetTextTagColor(tx, red, green, blue, alpha)
            call SetTextTagLifespan(tx, duration)
            call SetTextTagVelocity(tx, 0.0, 0.0355)
            call SetTextTagPermanent(tx, false)
            
            set tx = null
        endmethod

        static method onDamage takes nothing returns nothing
            local real amount = GetEventDamage()
        
            if amount > 0 and GetRandomReal(0, 100) <= chance[Damage.source.id] and Damage.isEnemy and not Damage.target.isStructure and multiplier[Damage.source.id] > 0 then
                set source = Damage.source
                set target = Damage.target
                set damage = amount*(1 + multiplier[Damage.source.id])

                call TriggerEvaluate(trigger)
                call BlzSetEventDamage(damage)
                if damage > 0 then
                    call text(target.unit, (I2S(R2I(damage)) + "!"), 1.5, 255, 0, 0, 255)
                endif

                set damage = 0
                set source = 0
                set target = 0
            endif
        endmethod

        static method register takes code c returns nothing
            call TriggerAddCondition(trigger, Filter(c))
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterAttackDamageEvent(function thistype.onDamage)
        endmethod
    endstruct
    
    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function RegisterCriticalStrikeEvent takes code c returns nothing
        call Critical.register(c)
    endfunction

    function GetCriticalSource takes nothing returns unit
        return Critical.source.unit
    endfunction

    function GetCriticalTarget takes nothing returns unit
        return Critical.target.unit
    endfunction

    function GetCriticalDamage takes nothing returns real
        return Critical.damage
    endfunction

    function GetUnitCriticalChance takes unit u returns real
        return Critical.getChance(u)
    endfunction

    function GetUnitCriticalMultiplier takes unit u returns real
        return Critical.getMultiplier(u)
    endfunction

    function SetUnitCriticalChance takes unit u, real value returns nothing
        call Critical.setChance(u, value)
    endfunction

    function SetUnitCriticalMultiplier takes unit u, real value returns nothing
        call Critical.setMultiplier(u, value)
    endfunction

    function SetCriticalEventDamage takes real newValue returns nothing
        set Critical.damage = newValue
    endfunction

    function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
        call Critical.add(u, chance, multiplier)
    endfunction
endlibrary
library SpellPower requires DamageInterface
    /* ------------------------ SpellPower v2.4 by Chopinski ----------------------- */
    // SpellPower intends to simulate a system similiar to Ability Power from LoL or
    // Spell Amplification from Dota 2.

    // Whenever a units deals Spell damage, that dealt damage will be amplified by a flat
    // and percentile amount that represents a unit spell power bonus.

    // The formula for amplification is: 
    // final damage = (initial damage + flat bonus) * (1 + percent bonus)
    // for percent bonus: 0.1 = 10% bonus

    // *SpellPower requires DamageInterface. Do not use TriggerSleepAction() within triggers.

    // The API:
    //     function GetUnitSpellPowerFlat takes unit u returns real
    //         -> Returns the Flat bonus of spell power of a unit

    //     function GetUnitSpellPowerPercent takes unit u returns real
    //         -> Returns the Percent bonus of spell power of a unit

    //     function SetUnitSpellPowerFlat takes unit u, real value returns nothing
    //         -> Set's the Flat amount of Spell Power of a unit

    //     function SetUnitSpellPowerPercent takes unit u, real value returns nothing
    //         -> Set's the Flat amount of Spell Power of a unit

    //     function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
    //         -> Add to the Flat amount of Spell Power of a unit

    //     function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
    //         -> Add to the Percent amount of Spell Power of a unit

    //     function GetSpellDamage takes real amount, unit u returns real
    //         -> Returns the amount of spell damage that would be dealt given an initial damage
    /* ----------------------------------- END ---------------------------------- */
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct SpellPower
        static real array flat
        static real array percent

        static method getFlat takes unit u returns real
            return flat[GetUnitUserData(u)]
        endmethod

        static method getPercent takes unit u returns real
            return percent[GetUnitUserData(u)]
        endmethod

        static method setFlat takes unit u, real value returns nothing
            set flat[GetUnitUserData(u)] = value
        endmethod

        static method setPercent takes unit u, real value returns nothing
            set percent[GetUnitUserData(u)] = value
        endmethod

        static method onDamage takes nothing returns nothing
            local real damage = GetEventDamage()
        
            if damage > 0 then
                call BlzSetEventDamage((damage + flat[Damage.source.id])*(1 + percent[Damage.source.id]))
            endif
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellDamageEvent(function thistype.onDamage)
        endmethod
    endstruct
    
    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function GetUnitSpellPowerFlat takes unit u returns real
        return SpellPower.getFlat(u)
    endfunction

    function GetUnitSpellPowerPercent takes unit u returns real
        return SpellPower.getPercent(u)
    endfunction

    function SetUnitSpellPowerFlat takes unit u, real value returns nothing
        call SpellPower.setFlat(u, value)
    endfunction

    function SetUnitSpellPowerPercent takes unit u, real value returns nothing
        call SpellPower.setPercent(u, value)
    endfunction

    function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
        call SpellPower.setFlat(u, SpellPower.getFlat(u) + amount)
    endfunction

    function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
        call SpellPower.setPercent(u, SpellPower.getPercent(u) + amount)
    endfunction

    function GetSpellDamage takes real amount, unit u returns real
        return ((amount + SpellPower.getFlat(u))*(1 + SpellPower.getPercent(u)))
    endfunction
endlibrary
Creating a Life Steal system is easy using the functions provided by the DamageEvents Library.
library LifeSteal requires DamageInterface
    /* ------------------------ LifeSteal v2.4 by Chopinski ----------------------- */
    // LifeSteal intends to simulate the Life Steal system in warcraft, and allow you
    // to easily change the life steal amount of any unit.

    // Whenever a unit deals Physical damage, and it has a value of life steal given by
    // this system, it will heal based of this value and the damage amount.

    // The formula for life steal is:
    // heal = damage * life steal
    // fror life steal: 0.1 = 10%

    // *LifeSteal requires DamageInterface. Do not use TriggerSleepAction() within triggers.

    // The API:
    //     function SetUnitLifeSteal takes unit u, real amount returns nothing
    //         -> Set the Life Steal amount for a unit

    //     function GetUnitLifeSteal takes unit u returns real
    //         -> Returns the Life Steal amount of a unit

    //     function UnitAddLifeSteal takes unit u, real amount returns nothing
    //         -> Add to the Life Steal amount of a unit the given amount
    /* ----------------------------------- END ---------------------------------- */
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct LifeSteal
        static string     effect = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
        static real array amount

        static method Get takes unit u returns real
            return amount[GetUnitUserData(u)]
        endmethod

        static method Set takes unit u, real value returns nothing
            set amount[GetUnitUserData(u)] = value
        endmethod

        static method onDamage takes nothing returns nothing
            local real damage = GetEventDamage()
            
            if damage > 0 and amount[Damage.source.id] > 0 and not Damage.target.isStructure then
                call SetWidgetLife(Damage.source.unit, (GetWidgetLife(Damage.source.unit) + (damage * amount[Damage.source.id])))
                call DestroyEffect(AddSpecialEffectTarget(effect, Damage.source.unit, "origin"))
            endif
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterAttackDamageEvent(function thistype.onDamage)
        endmethod 
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function SetUnitLifeSteal takes unit u, real amount returns nothing
        call LifeSteal.Set(u, amount)
    endfunction

    function GetUnitLifeSteal takes unit u returns real
        return LifeSteal.Get(u)
    endfunction

    function UnitAddLifeSteal takes unit u, real amount returns nothing
        call LifeSteal.Set(u, LifeSteal.Get(u) + amount)
    endfunction
endlibrary
library SpellVamp requires DamageInterface
    /* ------------------------ SpellVamp v2.4 by Chopinski ----------------------- */
    // SpellVamp intends to introduce to warcraft a healing based on Spell damage, like
    // in LoL or Dota 2.

    // Whenever a unit deals Spell damage, and it has a value of spell vamp given by
    // this system, it will heal based of this value and the damage amount.

    // The formula for spell vamp is:
    // heal = damage * lspell vamp
    // fror spell vamp: 0.1 = 10%

    // *SpellVamp requires DamageInterface. Do not use TriggerSleepAction() within triggers.

    // The API:
    //     function SetUnitSpellVamp takes unit u, real amount returns nothing
    //         -> Set the Spell Vamp amount for a unit
    
    //     function GetUnitSpellVamp takes unit u returns real
    //         -> Returns the Spell Vamp amount of a unit
    
    //     function UnitAddSpellVamp takes unit u, real amount returns nothing
    //         -> Add to the Spell Vamp amount of a unit the given amount
    /* ----------------------------------- END ---------------------------------- */
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct SpellVamp
        static real array amount

        static method Get takes unit u returns real
            return amount[GetUnitUserData(u)]
        endmethod

        static method Set takes unit u, real value returns nothing
            set amount[GetUnitUserData(u)] = value
        endmethod  

        static method onDamage takes nothing returns nothing
            local real damage = GetEventDamage()
        
            if damage > 0 and amount[Damage.source.id] > 0 and not Damage.target.isStructure then
                call SetWidgetLife(Damage.source.unit, (GetWidgetLife(Damage.source.unit) + (damage * amount[Damage.source.id])))
            endif
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellDamageEvent(function thistype.onDamage)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function SetUnitSpellVamp takes unit u, real amount returns nothing
        call SpellVamp.Set(u, amount)
    endfunction

    function GetUnitSpellVamp takes unit u returns real
        return SpellVamp.Get(u)
    endfunction

    function UnitAddSpellVamp takes unit u, real amount returns nothing
        call SpellVamp.Set(u, SpellVamp.Get(u) + amount)
    endfunction
endlibrary
library DamageInterfaceUtils requires Evasion, CriticalStrike, LifeSteal, SpellPower, SpellVamp
    /* ------- Utility Library for all the Damage Interface Custom Events ------- */
    /* ---------------------------- v2.4 by Chopinski --------------------------- */
    // JASS API:
    //     Evasion System:
    //         function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to a unit Evasion chance the specified amount for a given period
    
    //         function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to a unit Miss chance the specified amount for a given period

    //     Critical Strike System:
    //         function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
    //             -> Adds the specified values of chance and multiplier to a unit for a given period
        
    //         function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
    //             -> Adds the specified values of critical chance to a unit for a given period
        
    //         function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
    //             -> Adds the specified values of critical multiplier to a unit for a given period

    //     Spell Power System:
    //         function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to the Flat amount of Spell Power of a unit for a given period

    //         function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to the Percent amount of Spell Power of a unit for a given period

    //         function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
    //             -> Given an ability field, will return a string that represents the damage that would be dealt
    //             taking into consideration the spell power bonusses of a unit.

    //         function AbilitySpellDamageEx takes real amount, unit u returns string
    //             -> Similar to GetSpellDamage will return the damage that would be dealt but as a string

    //     Life Steal System:
    //         function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to the Life Steal amount of a unit the given amount for a given period

    //     Spell Vamp System:
    //         function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to the Spell Vamp amount of a unit the given amount for a given period
    /* ----------------------------------- END ---------------------------------- */
    
    /* -------------------------------------------------------------------------- */
    /*                                Evasion utils                               */
    /* -------------------------------------------------------------------------- */
    private struct EvasionUtils extends Evasion
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit    unit
        real    amount
        real    ticks
        boolean type

        method remove takes integer i returns integer
            if type then
                call setEvasionChance(unit, getEvasionChance(unit) - amount)
            else
                call setMissChance(unit, getMissChance(unit) - amount)
            endif

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
            
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration, boolean evasion returns nothing 
            local thistype this = thistype.allocate()

            set .unit      = u
            set .amount    = amount
            set .ticks     = duration/0.03125000
            set .type      = evasion
            set didx       = didx + 1
            set data[didx] = this

            if type then
                call setEvasionChance(u, getEvasionChance(u) + amount)
            else
                call setMissChance(u, getMissChance(u) + amount)
            endif
            
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                            Critical Strike Utils                           */
    /* -------------------------------------------------------------------------- */
    //region Critical Strike
    private struct CriticalUtils extends Critical
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit    unit
        real    crit
        real    multi
        real    ticks
        integer type

        method remove takes integer i returns integer
            if type == 0 then
                call add(unit, -crit, -multi)
            elseif type == 1 then
                call setChance(unit, getChance(unit) - crit)
            else
                call setMultiplier(unit, getMultiplier(unit) - multi)
            endif

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
            
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real chance, real multiplier, real duration, integer types returns nothing 
            local thistype this = thistype.allocate()

            set .unit      = u
            set .crit      = chance
            set .multi     = multiplier
            set .ticks     = duration/0.03125000
            set .type      = types
            set didx       = didx + 1
            set data[didx] = this

            if types == 0 then
                call add(u, crit, multi)
            elseif types == 1 then
                call setChance(u, getChance(u) + crit)
            else
                call setMultiplier(u, getMultiplier(u) + multi)
            endif
            
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                              Spell Power Utils                             */
    /* -------------------------------------------------------------------------- */
    //region Spell Power
    private struct SpellPowerUtils extends SpellPower
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit    unit
        real    amount
        real    ticks
        boolean isFlat

        method remove takes integer i returns integer
            if isFlat then
                call setFlat(unit, getFlat(unit) - amount)
            else
                call setPercent(unit, getPercent(unit) - amount)
            endif

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
            
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration, boolean isFlat returns nothing 
            local thistype this = thistype.allocate()

            set .unit      = u
            set .amount    = amount
            set .ticks     = duration/0.03125000
            set .isFlat    = isFlat
            set didx       = didx + 1
            set data[didx] = this

            if isFlat then
                call setFlat(u, getFlat(u) + amount)
            else
                call setPercent(u, getPercent(u) + amount)
            endif
            
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                              Life Steal Utils                              */
    /* -------------------------------------------------------------------------- */
    //region Life Steal
    private struct LifeStealUtils extends LifeSteal
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit unit
        real lifeSteal
        real ticks

        method remove takes integer i returns integer
            call Set(unit, Get(unit) - lifeSteal)

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
            
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration returns nothing
            local thistype this = thistype.allocate()

            set .unit      = u
            set .lifeSteal = amount
            set .ticks     = duration/0.03125000
            set didx       = didx + 1
            set data[didx] = this

            call Set(u, Get(u) + lifeSteal)
            
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                              Spell Vamp Utils                              */
    /* -------------------------------------------------------------------------- */
    //region Spell Vamp
    private struct SpellVampUtils extends SpellVamp
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit unit
        real spellVamp
        real ticks

        method remove takes integer i returns integer
            call Set(unit, Get(unit) - spellVamp)

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
           
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration returns nothing
            local thistype this = thistype.allocate()

            set .unit      = u
            set .spellVamp = amount
            set .ticks     = duration/0.03125000
            set didx       = didx + 1
            set data[didx] = this

            call Set(u, Get(u) + spellVamp)
           
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
        call EvasionUtils.addTimed(u, amount, duration, true)
    endfunction

    function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
        call EvasionUtils.addTimed(u, amount, duration, false)
    endfunction

    function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
        call CriticalUtils.addTimed(u, chance, multiplier, duration, 0)
    endfunction

    function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
        call CriticalUtils.addTimed(u, chance, 0, duration, 1)
    endfunction

    function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
        call CriticalUtils.addTimed(u, 0, multiplier, duration, 2)
    endfunction

    function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
        call SpellPowerUtils.addTimed(u, amount, duration, true)
    endfunction

    function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
        call SpellPowerUtils.addTimed(u, amount, duration, false)
    endfunction

    function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
        return I2S(R2I((BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, GetUnitAbilityLevel(u, abilityId) - 1) + SpellPower.getFlat(u)) * (1 + SpellPower.getPercent(u))))
    endfunction

    function AbilitySpellDamageEx takes real amount, unit u returns string
        return I2S(R2I((amount + SpellPower.getFlat(u)) * (1 + SpellPower.getPercent(u))))
    endfunction

    function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
        call LifeStealUtils.addTimed(u, amount, duration)
    endfunction

    function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
        call SpellVampUtils.addTimed(u, amount, duration)
    endfunction
endlibrary
library CooldownReduction requires RegisterPlayerUnitEvent, Table, Alloc, Indexer
/* ------------------ Cooldown Reduction v1.9 by Chopinski ------------------ */
    // Intro
    //     This library intension in to introduce to warcraft an easy way to 
    //     manipulate abilities cooldowns based on a cooldown reduction value that
    //     is unique for each unit.

    // How it Works?
    //     When casting an ability, its "new" cooldown is calculated based on the 
    //     amount of cooldown reduction of the casting unit. the formula for 
    //     calculation is:
    //         Cooldown = (Default Cooldown - Cooldown Offset) * [(1 - source1)*(1 - source2)*...] * (1 - Cooldown Reduction Flat)

    //     The system also allow negative values for CDR, resulting in increased
    //     ability cooldown.

    //     It does not acumulate because the abilities are registered automatically
    //     on the first cast, saving its base cooldown (Object Editor values) and 
    //     always using this base value for calculation, so you can still edit 
    //     the ability via the editor and the system takes care of the rest.

    // How to Import
    //     simply copy the CooldownReduction folder over to your map, and start 
    //     use the API functions

    // Requirements
    //     CooldownReduction requires RegisterPlayerUnitEvent, Alloc and a Unit Indexer.
    //     Credits to Magtheridon96 for RegisterPlayerUnitEvent and to Bribe for
    //     the UnitIndexer. It also requires patch 1.31+.

    //     RegisterPlayerUnitEvent: www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/
    //     UnitIndexer: www.hiveworkshop.com/threads/gui-unit-indexer-1-4-0-0.197329/#resource-45899
    //     Alloc: www.hiveworkshop.com/threads/snippet-alloc.192348/
    /* ----------------------------------- END ---------------------------------- */
    
    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    // Use this function to filter out units you dont want to have abilities registered.
    // By default dummy units do not trigger the system.
    private function UnitFilter takes unit source returns boolean
        return GetUnitAbilityLevel(source, 'Aloc') == 0
    endfunction
    
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private module List
        readonly thistype next
        readonly thistype prev

        method init takes nothing returns thistype
            set next = this
            set prev = this

            return this
        endmethod

        method push takes thistype node returns thistype
            set node.prev = prev
            set node.next = this
            set prev.next = node
            set prev = node

            return node
        endmethod

        method pop takes nothing returns nothing
            set prev.next = next
            set next.prev = prev
        endmethod
    endmodule
    
    private struct AbilityList extends array
        implement Alloc
        implement List
        
        unit unit
        ability ability
        Table defaults
        integer id
        integer levels
        
        method destroy takes nothing returns nothing
            local thistype node = this.next
        
            loop
                exitwhen node == this
                    set node.ability = null
                    call node.defaults.destroy()
                    call node.pop()
                    call node.deallocate()
                set node = node.next
            endloop
            call deallocate()

            set unit = null
        endmethod
        
        method insert takes integer id returns thistype
            local thistype node = push(allocate())
            local integer i = 0

            set node.id = id
            set node.ability = BlzGetUnitAbility(unit, id)
            set node.levels = BlzGetAbilityIntegerField(node.ability, ABILITY_IF_LEVELS)
            set node.defaults = Table.create()
            
            loop
                exitwhen i >= node.levels
                    set node.defaults.real[i] = BlzGetAbilityRealLevelField(node.ability, ABILITY_RLF_COOLDOWN, i)
                set i = i + 1
            endloop

            return node
        endmethod
        
        method update takes integer count, real normal, real flat, real offset returns nothing
            local thistype node = this.next
            local real cooldown
            local integer i
        
            loop
                exitwhen node == this
                    set i = 0
                    loop
                        exitwhen i >= node.levels
                            if count > 0 then
                                set cooldown = ((node.defaults.real[i] - offset) * normal * (1 - flat))
                            else
                                set cooldown = ((node.defaults.real[i] - offset) * (1 - flat))
                            endif
                            call BlzSetAbilityRealLevelField(node.ability, ABILITY_RLF_COOLDOWN, i, cooldown)
                            call IncUnitAbilityLevel(unit, node.id)
                            call DecUnitAbilityLevel(unit, node.id)
                        set i = i + 1
                    endloop
                set node = node.next
            endloop
        endmethod
        
        method calculate takes integer id, integer level, real cooldown, integer count, real normal, real flat, real offset returns nothing
            if count > 0 then
                call BlzSetAbilityRealLevelField(BlzGetUnitAbility(unit, id), ABILITY_RLF_COOLDOWN, level, ((cooldown - offset) * normal * (1 - flat)))
            else
                call BlzSetAbilityRealLevelField(BlzGetUnitAbility(unit, id), ABILITY_RLF_COOLDOWN, level, ((cooldown - offset) * (1 - flat)))
            endif
            call IncUnitAbilityLevel(unit, id)
            call DecUnitAbilityLevel(unit, id)
        endmethod
        
        method simulate takes real cooldown, integer count, real normal, real flat, real offset returns real
            local real cd
            
            if count > 0 then
                set cd = ((cooldown - offset) * normal * (1 - flat))
            else
                set cd = ((cooldown - offset) * (1 - flat))
            endif
            
            return cd
        endmethod
        
        static method create takes unit source returns thistype
            local thistype this = thistype(allocate()).init()
            
            set unit = source
            
            return this
        endmethod
    endstruct
    
    struct CDR
        readonly static hashtable hashtable = InitHashtable()
        private static AbilityList array n
        private  static integer array count
        readonly static real array normal
        readonly static real array flat
        readonly static real array offset
    
        private static method getInstance takes unit source returns AbilityList
            local integer i = GetUnitUserData(source)
            
            if n[i] == 0 then
                set n[i] = AbilityList.create(source)
            endif

            return n[i]
        endmethod
    
        private static method update takes unit u returns nothing
            local integer id = GetUnitUserData(u)
            local AbilityList list = getInstance(u)

            call list.update(count[id], normal[id], flat[id], offset[id])
        endmethod
        
        private static method calculate takes unit u returns real
            local integer idx = GetUnitUserData(u)
            local integer id = GetHandleId(u)
            local integer i = 0
            local real cdr = 0
            local real aux

            loop
                exitwhen i > count[idx]
                    set aux = LoadReal(hashtable, id, i) 
                    
                    if i > 0 then
                        set cdr = cdr * (1 - aux)
                    else
                        set cdr = 1 - aux
                    endif
                set i = i + 1
            endloop

            return cdr
        endmethod
    
        static method get takes unit u, integer types returns real
            if types == 0 then
                return normal[GetUnitUserData(u)]
            elseif types == 1 then
                return flat[GetUnitUserData(u)]
            else
                return offset[GetUnitUserData(u)]
            endif
        endmethod

        static method Set takes unit u, real value, integer types returns nothing
            if types == 0 then
                set normal[GetUnitUserData(u)] = value
            elseif types == 1 then
                set flat[GetUnitUserData(u)] = value
            else
                set offset[GetUnitUserData(u)] = value
            endif

            call update(u)
        endmethod

        static method add takes unit u, real amount returns nothing
            local integer i = GetUnitUserData(u)

            if amount != 0 then
                call SaveReal(hashtable, GetHandleId(u), count[i], amount)
                set normal[i] = calculate(u)
                set count[i] = count[i] + 1
                call update(u)
            endif
        endmethod
        
        static method remove takes unit u, real amount returns nothing
            local integer idx = GetUnitUserData(u)
            local integer id = GetHandleId(u)
            local integer i = 0
            local real aux

            if amount == 0 then
                return
            endif

            loop
                exitwhen i > count[idx] - 1
                    set aux = LoadReal(hashtable, id, i)
                    
                    if aux == amount then
                        call RemoveSavedReal(hashtable, id, i)
                        if i != count[idx] - 1 then
                            set aux = LoadReal(hashtable, id, count[idx] - 1)
                            call SaveReal(hashtable, id, i, aux)
                            call RemoveSavedReal(hashtable, id, count[idx] - 1)
                        endif
                        set count[idx] = count[idx] - 1
                        set normal[idx] = calculate(u)
                        set i = count[idx] + 1
                        call update(u)
                    else
                        set i = i + 1
                    endif
            endloop
        endmethod
    
        static method calculateCooldown takes unit u, integer id, integer level, real cooldown returns nothing
            local integer i = GetUnitUserData(u)
            local AbilityList list = getInstance(u)

            call list.calculate(id, level - 1, cooldown, count[i], normal[i], flat[i], offset[i])
        endmethod
        
        static method simulateCooldown takes unit u, real cooldown returns real
            local integer i = GetUnitUserData(u)
            local AbilityList list = getInstance(u)

            return list.simulate(cooldown, count[i], normal[i], flat[i], offset[i])
        endmethod
    
        static method register takes unit u, integer id returns nothing
            local AbilityList list
            local integer i
            
            if UnitFilter(u) then
                set list = getInstance(u)
                set i = GetUnitUserData(u)
                
                if not LoadBoolean(hashtable, list, id) then
                    call list.insert(id)
                    call SaveBoolean(hashtable, list, id, true)
                    
                    if count[i] > 0 or normal[i] != 0 or flat[i] != 0 or offset[i] != 0 then
                        call update(u)
                    endif
                endif
            endif
        endmethod
    
        private static method onCast takes nothing returns nothing
            call register(GetTriggerUnit(), GetSpellAbilityId())
        endmethod
        
        private static method onLevel takes nothing returns nothing
            call register(GetTriggerUnit(), GetLearnedSkill())
        endmethod
        
        private static method onDeindex takes nothing returns nothing
            local unit source = GetIndexUnit()
            local integer i = GetUnitUserData(source)
            local AbilityList list = getInstance(source)
            
            set n[i] = 0
            set normal[i] = 0
            set flat[i] = 0
            set offset[i] = 0
            set count[i] = 0

            if list != 0 then
                call list.destroy()
                call FlushChildHashtable(hashtable, list)
            endif
            call FlushChildHashtable(hashtable, GetHandleId(source))
        endmethod
    
        private static method onInit takes nothing returns nothing
            call RegisterUnitDeindexEvent(function thistype.onDeindex)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_SKILL, function thistype.onLevel)
        endmethod
    endstruct
    
    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function GetUnitCooldownReduction takes unit u returns real
        return 1 - CDR.get(u, 0)
    endfunction

    function GetUnitCooldownReductionFlat takes unit u returns real
        return CDR.get(u, 1)
    endfunction

    function GetUnitCooldownOffset takes unit u returns real
        return CDR.get(u, 2)
    endfunction

    function SetUnitCooldownReduction takes unit u, real value returns nothing
        call CDR.Set(u, value, 0)
    endfunction

    function SetUnitCooldownReductionFlat takes unit u, real value returns nothing
        call CDR.Set(u, value, 1)
    endfunction

    function SetUnitCooldownOffset takes unit u, real value returns nothing
        call CDR.Set(u, value, 2)
    endfunction

    function UnitAddCooldownReduction takes unit u, real value returns nothing
        call CDR.add(u, value)
    endfunction

    function UnitAddCooldownReductionFlat takes unit u, real value returns nothing
        call CDR.Set(u, CDR.get(u, 1) + value, 1)
    endfunction

    function UnitAddCooldownOffset takes unit u, real value returns nothing
        call CDR.Set(u, CDR.get(u, 2) + value, 2)
    endfunction

    function UnitRemoveCooldownReduction takes unit u, real value returns nothing
        call CDR.remove(u, value)
    endfunction

    function CalculateAbilityCooldown takes unit u, integer id, integer level, real cooldown returns nothing
        call CDR.calculateCooldown(u, id, level, cooldown)
    endfunction 
    
    function SimulateAbilityCooldown takes unit u, real cooldown returns real
        return CDR.simulateCooldown(u, cooldown)
    endfunction 
    
    function RegisterAbility takes unit u, integer id returns nothing
        call CDR.register(u, id)
    endfunction
endlibrary
library CooldownReductionUtils requires CooldownReduction
    /* --------------- Cooldown Reduction Utils v1.9 by Chopinski --------------- */
    // Intro
    //     Utility Library that include a few extra functions to deal with 
    //     Cooldown Reduction

    // JASS API
    //     function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
    //         -> Add to the amount of cdr of a unit for a given duration. Accepts positive and negative values. 
    //         -> It handles removing the bonus automatically
    
    //     function UnitAddCooldownReductionFlatTimed takes unit u, real value, real duration returns nothing
    //         -> Add to the amount of cdr flat of a unit for a given period. Accepts positive and negative values.
    //         -> It handles removing the bonus automatically
    
    //     function UnitAddCooldownOffsetTimed takes unit u, real value, real duration returns nothing
    //         -> Add to the amount of cdr offset of a unit for a given period. Accepts positive and negative values.
    //         -> It handles removing the bonus automatically
        
    //     function GetUnitCooldownReductionEx takes unit u returns string
    //         -> Returns the amount of cdr a unit has as a string factored by 100
    //         -> example of return: 10.50 -> 0.105 internally.
    
    //     function GetUnitCooldownReductionFlatEx takes unit u returns string
    //         -> Returns the amount of cdr flat a unit has as a string factored by 100
    //         -> example of return: 10.50 -> 0.105 internally.
    
    //     function GetUnitCooldownOffsetEx takes unit u returns string
    //         -> Returns the amount of cdr offset a unit has as a string
    
    //     function GetAbilityTable takes nothing returns hashtable
    //         -> Returns the hashtable that holds the units default cooldown reduction values. 
    //         -> Use with caution! you might break stuff
    /* ----------------------------------- END ---------------------------------- */
    
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private struct CDRUtils extends CDR
        static timer t = CreateTimer()
        //----------------------------------------------
        static integer didx = -1
        static thistype array data
        //----------------------------------------------

        unit    u
        real    ticks
        real    amount
        integer tipo

        method destroy takes nothing returns nothing
            if didx == -1 then
                call PauseTimer(t)
            endif

            set .u     = null
            set .ticks = 0
            call .deallocate()
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
            
            loop
                exitwhen i > didx
                    set this   = data[i]
                    set .ticks = .ticks - 1

                    if .ticks <= 0 then
                        if .tipo == 0 then
                            call remove(.u, .amount)
                        elseif .tipo == 1 then
                            call Set(.u, get(.u, 1) - .amount, 1)
                        else
                            call Set(.u, get(.u, 2) - .amount, 2)
                        endif

                        set  data[i] = data[didx]
                        set  didx    = didx - 1
                        set  i 		 = i - 1
                        call .destroy()
                    endif
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration, integer tipo returns nothing
            local thistype this = thistype.allocate()

            set .u          = u
            set .amount     = amount
            set .tipo       = tipo
            set .ticks      = duration/0.03125000
            set didx        = didx + 1
            set data[didx]  = this

            if tipo == 0 then
                call add(u, amount)
            elseif tipo == 1 then
                call Set(u, get(u, 1) + amount, 1)
            else
                call Set(u, get(u, 2) + amount, 2)
            endif
            
            if didx == 0 then
                call TimerStart(t, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* -------------------------------------------------------------------------- */
    function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
        call CDRUtils.addTimed(u, value, duration, 0)
    endfunction

    function UnitAddCooldownReductionFlatTimed takes unit u, real value, real duration returns nothing
        call CDRUtils.addTimed(u, value, duration, 1)
    endfunction

    function UnitAddCooldownOffsetTimed takes unit u, real value, real duration returns nothing
        call CDRUtils.addTimed(u, value, duration, 2)
    endfunction

    function GetUnitCooldownReductionEx takes unit u returns string
        return R2SW(CDRUtils.get(u, 0)*100, 1, 2)
    endfunction

    function GetUnitCooldownReductionFlatEx takes unit u returns string
        return R2SW(CDRUtils.get(u, 1)*100, 1, 2)
    endfunction

    function GetUnitCooldownOffsetEx takes unit u returns string
        return R2SW(CDRUtils.get(u, 2), 1, 2)
    endfunction

    function GetAbilityTable takes nothing returns hashtable
        return CDR.hashtable
    endfunction
endlibrary
library WaterElemental requires SpellEffectEvent, PluginSpellEffect, NewBonus, Indexer
    /* -------------------- Water Elemental v1.1 by Chopinski ------------------- */
    // Credits:
    //     Blizzard        - Icon
    //     Bribe           - SpellEffectEvent
    /* ----------------------------------- END ---------------------------------- */

    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the ability
        private constant integer ABILITY   = 'A000'
        // The raw code of the Water Elemental unit
        public  constant integer ELEMENTAL = 'h001'
        // The summon effect
        private constant string MODEL      = "WaterBurst.mdl"
        // The summon effect scale
        private constant real SCALE        = 1.
    endglobals

    // The unit damage
    private function GetDamage takes unit source, integer level returns integer
        return 30 + R2I((BlzGetUnitBaseDamage(source, 0) + GetUnitBonus(source, BONUS_DAMAGE))*0.3)
    endfunction

    // The unit health
    private function GetHealth takes unit source, integer level returns integer
        return 600 + R2I(BlzGetUnitMaxHP(source)*0.4)
    endfunction

    // The unit armor
    private function GetArmor takes unit source, integer level returns real
        return 1. + GetUnitBonus(source, BONUS_ARMOR)
    endfunction

    // The unit duration
    private function GetDuration takes unit source, integer level returns real
        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, ABILITY), ABILITY_RLF_DURATION_HERO, level - 1)
    endfunction

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct Elemental
        private static thistype array struct

        private unit unit
        private group group

        method operator size takes nothing returns integer
            return BlzGroupGetSize(group)
        endmethod

        method destroy takes nothing returns nothing
            local integer i = 0

            loop
                exitwhen i == size
                    set struct[GetUnitUserData(BlzGroupUnitAt(group, i))] = 0
                set i = i + 1
            endloop

            call DestroyGroup(group)
            call deallocate()

            set unit = null
            set group = null
        endmethod

        method add takes unit u returns nothing
            set struct[GetUnitUserData(u)] = this
            call GroupAddUnit(group, u)
        endmethod

        method command takes unit target, real x, real y, string order returns nothing
            local integer i = 0
            
            if target == null then
                if order == "stop" or order == "holdposition" then
                    call GroupImmediateOrder(group, order)
                elseif order == "attackground" or order == "smart" or order == "move" or order == "attack" then
                    loop
                        exitwhen i == size
                            call IssuePointOrder(BlzGroupUnitAt(group, i), order, x + 300*Cos(i*2*bj_PI/size), y + 300*Sin(i*2*bj_PI/size))
                        set i = i + 1
                    endloop
                endif
            else
                if order == "smart" or order == "move" or order == "attack" then
                    call GroupTargetOrder(group, order, target)
                endif
            endif
        endmethod

        static method owner takes integer id returns unit
            local thistype this

            if struct[id] != 0 then
                set this = struct[id]
                return unit
            else
                return null
            endif
        endmethod

        private static method onOrder takes nothing returns nothing
            local unit source = GetOrderedUnit()
            local integer id
            local thistype this

            if GetUnitTypeId(source) == ELEMENTAL then
                set id = GetUnitUserData(source)

                if struct[id] != 0 then
                    set this = struct[id]
                    
                    if GetOrderTargetUnit() == unit then
                        if not IsUnitInGroup(source, group) then
                            call GroupAddUnit(group, source)
                        endif
                    else
                        if IsUnitSelected(source, GetOwningPlayer(source)) and IsUnitInGroup(source, group) then
                            call GroupRemoveUnit(group, source)
                        endif
                    endif
                endif
            endif

            set source = null
        endmethod

        private static method onDeath takes nothing returns nothing
            local unit source = GetTriggerUnit()
            local integer id
            local thistype this

            if GetUnitTypeId(source) == ELEMENTAL then
                set id = GetUnitUserData(source)

                if struct[id] != 0 then
                    set this = struct[id]
                    call GroupRemoveUnit(group, source)
                    set struct[id] = 0
                endif
            endif
        endmethod

        static method create takes unit source returns thistype
            local thistype this = thistype.allocate()

            set unit = source
            set group = CreateGroup()

            return this
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
        endmethod
    endstruct
    
    private struct Master
        private static thistype array struct

        Elemental elementals

        method destroy takes nothing returns nothing
            call elementals.destroy()
            call deallocate()
        endmethod

        private static method onDeindex takes nothing returns nothing
            local integer id = GetUnitUserData(GetIndexUnit())
            local thistype this

            if struct[id] != 0 then
                set this = struct[id]
                call destroy()
                set struct[id] = 0
            endif
        endmethod

        private static method onOrder takes nothing returns nothing
            local unit source = GetOrderedUnit()
            local integer id = GetUnitUserData(source)
            local thistype this

            if GetUnitAbilityLevel(source, ABILITY) > 0 and struct[id] != 0 then
                set this = struct[id]
                call elementals.command(GetOrderTargetUnit(), GetOrderPointX(), GetOrderPointY(), OrderId2String(GetIssuedOrderId()))
            endif

            set source = null
        endmethod

        private static method onCast takes nothing returns nothing
            local thistype this
            local real angle = GetUnitFacing(Spell.source.unit)
            local unit u = CreateUnit(Spell.source.player, ELEMENTAL, Spell.source.x + 250*Cos(Deg2Rad(angle)), Spell.source.y + 250*Sin(Deg2Rad(angle)), angle)

            call DestroyEffect(AddSpecialEffectEx(MODEL, GetUnitX(u), GetUnitY(u), 0, SCALE))

            if struct[Spell.source.id] != 0 then
                set this = struct[Spell.source.id]
            else
                set this = thistype.allocate()
                set struct[Spell.source.id] = this
                set elementals = Elemental.create(Spell.source.unit)
            endif

            call BlzSetUnitBaseDamage(u, GetDamage(Spell.source.unit, Spell.level), 0)
            call BlzSetUnitMaxHP(u, GetHealth(Spell.source.unit, Spell.level))
            call SetUnitLifePercentBJ(u, 100)
            call BlzSetUnitArmor(u, GetArmor(Spell.source.unit, Spell.level))
            call SetUnitAnimation(u, "Birth")
            call UnitApplyTimedLife(u, 'BTLF', GetDuration(Spell.source.unit, Spell.level))
            call elementals.add(u)

            set u = null
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ABILITY, function thistype.onCast)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
            call RegisterUnitDeindexEvent(function thistype.onDeindex)
        endmethod
    endstruct
endlibrary
library CrushingWave requires SpellEffectEvent, PluginSpellEffect, Missiles, Utilities, CrowdControl optional WaterElemental
    /* --------------------- Crushing Wave v1.1 by Chopinski -------------------- */
    // Credits:
    //     Blizzard        - Icon
    //     Bribe           - SpellEffectEvent
    /* ----------------------------------- END ---------------------------------- */

    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the ability
        private constant integer ABILITY   = 'A001'
        // The Wave model
        private constant string MODEL      = "LivingTide.mdl"
        // The Wave speed 
        private constant real SPEED        = 1250
        // The wave model scale
        private constant real SCALE        = 0.8
    endglobals

    // The wave damage
    private function GetDamage takes unit source, integer level returns real
        return 50. + 50.*level
    endfunction

    // The Wave collision
    private function GetCollision takes unit source, integer level returns real
        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, ABILITY), ABILITY_RLF_AREA_OF_EFFECT, level - 1)
    endfunction

    // The water elemental search range
    private function GetAoE takes unit source, integer level returns real
        return 1000. + 0.*level
    endfunction

    // The wave travel distance
    private function GetTravelDistance takes unit source, integer level returns real
        return 1000. + 0.*level
    endfunction

    // The Wave slow amount
    private function GetSlowAmount takes unit source, integer level returns real
        return 0.1 + 0.1*level
    endfunction

    // The Slow duration
    private function GetSlowDuration takes unit source, integer level returns real
        return 1. + 0.25*level
    endfunction

    // The damae filter
    private function UnitFilter takes player owner, unit target returns boolean
        return UnitAlive(target) and IsUnitEnemy(target, owner) and not IsUnitType(target, UNIT_TYPE_STRUCTURE)
    endfunction

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private struct Wave extends Missiles
        unit unit
        real slow
        real timeout

        method onPeriod takes nothing returns boolean
            if unit != null then
                call SetUnitX(unit, x)
                call SetUnitY(unit, y)
            endif

            return false
        endmethod

        method onHit takes unit u returns boolean
            if UnitFilter(owner, u) then
                if UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) then
                    call SlowUnit(u, slow, timeout, null, null, false)
                endif
            endif

            return false
        endmethod

        method onRemove takes nothing returns nothing
            if unit != null then
                call PauseUnit(unit, false)
                call SetUnitAnimation(unit, "Stand")
                call SetUnitInvulnerable(unit, false)
            endif

            set unit = null
        endmethod
    endstruct

    private struct CrushingWave extends array
        private static method launch takes real x, real y, real z, real tx, real ty, real tz, unit source, player owner, integer level, unit elemental returns nothing
            local Wave wave = Wave.create(x, y, z, tx, ty, tz)
            
            set wave.model = MODEL
            set wave.scale = SCALE
            set wave.speed = SPEED
            set wave.source = source
            set wave.owner = owner
            set wave.unit = elemental
            set wave.damage = GetDamage(source, level)
            set wave.collision = GetCollision(source, level)
            set wave.slow = GetSlowAmount(source, level)
            set wave.timeout = GetSlowDuration(source, level)

            if elemental != null then
                call BlzSetUnitFacingEx(elemental, AngleBetweenCoordinates(x, y, tx, ty)*bj_RADTODEG)
                call PauseUnit(elemental, true)
                call SetUnitAnimationByIndex(elemental, 8)
                call SetUnitInvulnerable(elemental, true)
            endif

            call wave.launch()
        endmethod

        private static method onCast takes nothing returns nothing
            local real angle = AngleBetweenCoordinates(Spell.source.x, Spell.source.y, Spell.x, Spell.y)
            local real offset = GetTravelDistance(Spell.source.unit, Spell.level)
            local group g
            local unit u

            call launch(Spell.source.x, Spell.source.y, 0, Spell.source.x + offset*Cos(angle), Spell.source.y + offset*Sin(angle), 0, Spell.source.unit, Spell.source.player, Spell.level, null)
            
            static if LIBRARY_WaterElemental then
                set g = CreateGroup()
                
                call GroupEnumUnitsInRange(g, Spell.source.x, Spell.source.y, GetAoE(Spell.source.unit, Spell.level), null)
                loop
                    set u = FirstOfGroup(g)
                    exitwhen u == null
                        if UnitAlive(u) and GetUnitTypeId(u) == WaterElemental_ELEMENTAL then
                            if Elemental.owner(GetUnitUserData(u)) == Spell.source.unit then
                                call launch(GetUnitX(u), GetUnitY(u), 0, Spell.x, Spell.y, 0, Spell.source.unit, Spell.source.player, Spell.level, u)
                            endif
                        endif
                    call GroupRemoveUnit(g, u)
                endloop
                call DestroyGroup(g)
            endif

            set u = null
            set g = null
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ABILITY, function thistype.onCast)
        endmethod
    endstruct
endlibrary
library WaterShield requires RegisterPlayerUnitEvent, SpellEffectEvent, PluginSpellEffect, DamageInterface, Missiles, Utilities, TimerUtils
    /* --------------------- Water Shield v1.0 by Chopinski --------------------- */
    // Credits:
    //     Darkfang        - Icon
    //     Bribe           - SpellEffectEvent
    //     Vexorian        - TimerUtils
    //     Magtheridon96   - RegisterPlayerUnitEvent
    /* ----------------------------------- END ---------------------------------- */

    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the ability
        private constant integer ABILITY                = 'A002'
        // The shield model
        private constant string  MODEL                  = "WaterShield.mdl"
        // The shield attachement point
        private constant string  ATTACH                 = "origin"
        // The explosion model
        private constant string  EXPLOSION_MODEL        = "LivingTide.mdl"
        // The scale of the explosion model
        private constant real    EXPLOSION_SCALE        = 0.6
        // The water bolt model
        private constant string  BOLT_MODEL             = "Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl"
        // The water bolt scal
        private constant real    BOLT_SCALE             = 1.
        // The water bolt speed
        private constant real    BOLT_SPEED             = 1000.
    endglobals

    // The shield amount
    private function GetAmount takes unit source, integer level returns real
        return 100.*level + (0.5 + 0.5*level)*GetHeroInt(source, true)
    endfunction

    // The water bolt damage
    private function GetBoltDamage takes unit source, integer level returns real
        return 25. + 25.*level
    endfunction

    // The range at which units can be selected by water bolt
    private function GetAoE takes unit source, integer level returns real
        return 400. + 0.*level
    endfunction

    // The aoe of the explosion when there is a remaining shield amount
    private function GetExplosionAoE takes unit source, integer level returns real
        return 400. + 0.*level
    endfunction

    // The angle in degrees at which units can be selected
    private function GetAngle takes unit source, integer level returns real
        return 90. + 0.*level
    endfunction

    // The Shield duration
    private function GetDuration takes unit source, integer level returns real
        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, ABILITY), ABILITY_RLF_DURATION_HERO, level - 1)
    endfunction

    // The unit filter
    private function UnitFilter takes player owner, unit target returns boolean
        return IsUnitEnemy(target, owner) and UnitAlive(target) and not IsUnitType(target, UNIT_TYPE_STRUCTURE)
    endfunction

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private struct WaterBolt extends Missiles
        method onFinish takes nothing returns boolean
            if UnitAlive(target) then
                call UnitDamageTarget(source, target, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
            endif

            return true
        endmethod
    endstruct
    
    private struct WaterShield
        static thistype array defense
        static thistype array offense
        static effect array effect

        timer timer
        unit source
        unit target
        player player
        group group
        integer id
        integer level
        real amount
        real angle
        real aoe
        boolean defensive

        method destroy takes nothing returns nothing
            if defensive then
                set defense[id] = 0

                if offense[id] == 0 then
                    call DestroyEffect(effect[id])
                    set effect[id] = null
                endif
            else
                set offense[id] = 0

                if defense[id] == 0  then   
                    call DestroyEffect(effect[id])
                    set effect[id] = null
                endif
            endif

            call DestroyGroup(group)
            call ReleaseTimer(timer)
            call deallocate()

            set timer = null
            set group = null
            set source = null
            set target = null
            set player = null
        endmethod

        static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local unit u

            if defensive and amount > 0 then
                call GroupEnumUnitsInRange(group, GetUnitX(target), GetUnitY(target), aoe, null)
                loop
                    set u = FirstOfGroup(group)
                    exitwhen u == null
                        if UnitFilter(player, u) then
                            call UnitDamageTarget(source, u, amount, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
                        endif
                    call GroupRemoveUnit(group, u)
                endloop
                call DestroyEffect(AddSpecialEffectEx(EXPLOSION_MODEL, GetUnitX(target), GetUnitY(target), 0, EXPLOSION_SCALE))
            endif

            call destroy()
        endmethod

        static method onDamage takes nothing returns nothing
            local real damage = GetEventDamage()
            local thistype this

            if damage > 0 and defense[Damage.target.id] != 0 then
                set this = defense[Damage.target.id]

                if damage <= amount then
                    set amount = amount - damage
                    call BlzSetEventDamage(0)
                else
                    set damage = damage - amount
                    set amount = 0

                    call BlzSetEventDamage(damage)
                    call destroy()
                endif
            endif
        endmethod

        private static method onAttack takes nothing returns nothing
            local thistype this
            local WaterBolt bolt
            local unit u

            if Damage.isEnemy and offense[Damage.target.id] != 0 then
                set this = offense[Damage.target.id]

                call GroupEnumUnitsInRange(group, Damage.target.x, Damage.target.y, aoe, null)
                call GroupRemoveUnit(group, Damage.target.unit)
                loop
                    set u = FirstOfGroup(group)
                    exitwhen u == null
                        if UnitFilter(player, u) and IsUnitInCone(u, Damage.target.x, Damage.target.y, aoe, AngleBetweenCoordinates(Damage.source.x, Damage.source.y, Damage.target.x, Damage.target.y)*bj_RADTODEG, angle) then
                            set bolt = WaterBolt.create(Damage.target.x, Damage.target.y, 50, GetUnitX(u), GetUnitY(u), 50)
                            
                            set bolt.model = BOLT_MODEL
                            set bolt.speed = BOLT_SPEED
                            set bolt.scale = BOLT_SCALE
                            set bolt.source = source
                            set bolt.target = u
                            set bolt.damage = amount

                            call bolt.launch()
                        endif
                    call GroupRemoveUnit(group, u)
                endloop
            endif
        endmethod

        private static method onDeath takes nothing returns nothing
            local integer id = GetUnitUserData(GetTriggerUnit())
            local thistype this

            if defense[id] != 0 then
                set this = defense[id]
                call destroy()
            endif

            if offense[id] != 0 then
                set this = offense[id]
                call destroy()
            endif
        endmethod

        static method onCast takes nothing returns nothing
            local thistype this
            
            if effect[Spell.target.id] == null then
                set effect[Spell.target.id] = AddSpecialEffectTarget(MODEL, Spell.target.unit, ATTACH)
            endif

            if IsUnitEnemy(Spell.target.unit, Spell.source.player) then
                if offense[Spell.target.id] != 0 then
                    set this = offense[Spell.target.id]
                else
                    set this = thistype.allocate()
                    set timer = NewTimerEx(this)
                    set id = Spell.target.id
                    set target = Spell.target.unit
                    set group = CreateGroup()
                    set defensive = false
                    set offense[id] = this
                endif

                set source = Spell.source.unit
                set player = Spell.source.player
                set level = Spell.level
                set amount = GetBoltDamage(source, level)
                set angle = GetAngle(source, level)
                set aoe = GetAoE(source, level)
            else
                if defense[Spell.target.id] != 0 then
                    set this = defense[Spell.target.id]
                else
                    set this = thistype.allocate()
                    set timer = NewTimerEx(this)
                    set id = Spell.target.id
                    set target = Spell.target.unit
                    set group = CreateGroup()
                    set defensive = true
                    set amount = 0
                    set defense[id] = this
                endif

                set source = Spell.source.unit
                set player = Spell.source.player
                set level = Spell.level
                set amount = amount + GetAmount(source, level)
                set aoe = GetExplosionAoE(source, level)
            endif

            call TimerStart(timer, GetDuration(source, level), false, function thistype.onExpire)
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ABILITY, function thistype.onCast)
            call RegisterAnyDamageEvent(function thistype.onDamage)
            call RegisterAttackDamageEvent(function thistype.onAttack)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
        endmethod
    endstruct
endlibrary
library BrillanceAura requires RegisterPlayerUnitEvent, TimerUtils
    /* -------------------- Brilliance Aure v1.0 by Chopinski ------------------- */
    // Credits
    //      Vexorian         - TimerUtils
    //      Magtheridon96    - RegisterPlayerUnitEvent
    /* ----------------------------------- END ---------------------------------- */

    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the ability
        private constant integer ABILITY    = 'A003'
        // If true the bonus regen will stack with each cast
        private constant boolean STACK      = false
    endglobals

    // The bonus mana regen when an ability is cast
    private function GetBonusManaRegen takes unit source, integer level returns real
        return 1.5 * level
    endfunction

    // The duration of the bonus regen
    private function GetDuration takes unit source, integer level returns real
        return 2.5 * level
    endfunction

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private struct BrillianceAura
        static thistype array struct

        timer timer
        unit unit
        ability ability
        integer id
        integer levels
        real bonus
        abilityreallevelfield field

        static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local integer i = 0

            loop
                exitwhen i == levels
                    call BlzSetAbilityRealLevelField(ability, field, i, BlzGetAbilityRealLevelField(ability, field, i) - bonus)
                    call IncUnitAbilityLevel(unit, ABILITY)
                    call DecUnitAbilityLevel(unit, ABILITY)
                set i = i + 1
            endloop
            
            call ReleaseTimer(timer)
            call deallocate()

            set struct[id] = 0
            set bonus = 0
            set field = null
            set timer = null
            set unit = null
            set ability = null
        endmethod

        static method onCast takes nothing returns nothing
            local unit source = GetTriggerUnit()
            local integer level = GetUnitAbilityLevel(source, ABILITY)
            local integer i = 0
            local thistype this

            if level > 0 then
                static if STACK then
                    set this = thistype.allocate()
                    set timer = NewTimerEx(this)
                    set id = GetUnitUserData(source)
                    set unit = source
                    set field = ABILITY_RLF_MANA_REGENERATION_INCREASE
                    set ability = BlzGetUnitAbility(source, ABILITY)
                    set levels = BlzGetAbilityIntegerField(ability, ABILITY_IF_LEVELS)
                    set bonus = GetBonusManaRegen(source, level)

                    loop
                        exitwhen i == levels
                            call BlzSetAbilityRealLevelField(ability, field, i, BlzGetAbilityRealLevelField(ability, field, i) + bonus)
                            call IncUnitAbilityLevel(source, ABILITY)
                            call DecUnitAbilityLevel(source, ABILITY)
                        set i = i + 1
                    endloop
                else
                    if struct[GetUnitUserData(source)] != 0 then
                        set this = struct[GetUnitUserData(source)]
                    else
                        set this = thistype.allocate()
                        set timer = NewTimerEx(this)
                        set id = GetUnitUserData(source)
                        set unit = source
                        set field = ABILITY_RLF_MANA_REGENERATION_INCREASE
                        set ability = BlzGetUnitAbility(source, ABILITY)
                        set levels = BlzGetAbilityIntegerField(ability, ABILITY_IF_LEVELS)
                        set bonus = 0
                        set struct[id] = this
                    endif
                    
                    if bonus == 0 then
                        set bonus = GetBonusManaRegen(source, level)

                        loop
                            exitwhen i == levels
                                call BlzSetAbilityRealLevelField(ability, field, i, BlzGetAbilityRealLevelField(ability, field, i) + bonus)
                                call IncUnitAbilityLevel(source, ABILITY)
                                call DecUnitAbilityLevel(source, ABILITY)
                            set i = i + 1
                        endloop
                    endif
                endif

                call TimerStart(timer, GetDuration(source, level), false, function thistype.onExpire)
            endif

            set source = null
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
        endmethod
    endstruct
endlibrary
library WaterOrb requires RegisterPlayerUnitEvent, Utilities, NewBonus, DamageInterface
    /* ----------------------- Water Orb v1.0 by Chopinski ---------------------- */
    // Credits:
    //     Darkfang             - Icon
    //     Magtheridon96        - RegisterPlayerUnitEvent
    //     General Frank        - Model
    /* ----------------------------------- END ---------------------------------- */
    
    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the level 1 buff
        private constant integer BUFF_1         = 'B002'
        // The raw code of the level 2 buff
        private constant integer BUFF_2         = 'B003'
        // The raw code of the level 3 buff
        private constant integer BUFF_3         = 'B004'
        // The raw code of the level 4 buff
        private constant integer BUFF_4         = 'B005'
        // The orb model
        private constant string  MODEL          = "OrbWaterX.mdl"
        // The orb model scale
        private constant real    SCALE          = 1.
        // The pickup effect model
        private constant string  EFFECT         = "Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl"
        // The pickup effect model attach point
        private constant string  ATTACH         = "origin"
        // The update period
        private constant real    PERIOD         = 0.25
    endglobals

    // The orb duration
    private function GetDuratoin takes integer buffid returns real
        if buffid == BUFF_1 then
            return 20.
        elseif buffid == BUFF_2 then
            return 20.
        elseif buffid == BUFF_3 then
            return 20.
        else
            return 20.
        endif
    endfunction

    // The max mana bonus
    private function GetManaBonus takes integer buffid returns real
        if buffid == BUFF_1 then
            return 20.
        elseif buffid == BUFF_2 then
            return 30.
        elseif buffid == BUFF_3 then
            return 40.
        else
            return 50.
        endif
    endfunction

    // The chance to drop an orb
    private function GetDropChance takes unit target, integer buffid returns integer
        if IsUnitType(target, UNIT_TYPE_HERO) then
            return 100
        else
            if buffid == BUFF_1 then
                return 20
            elseif buffid == BUFF_2 then
                return 20
            elseif buffid == BUFF_3 then
                return 20
            else
                return 20
            endif
        endif
    endfunction

    //
    private function GetPickupRange takes integer buffid returns real
        if buffid == BUFF_1 then
            return 100.
        elseif buffid == BUFF_2 then
            return 100.
        elseif buffid == BUFF_3 then
            return 100.
        else
            return 100.
        endif
    endfunction

    // The unit drop filter
    private function UnitDropFilter takes unit target returns boolean
        return not IsUnitType(target, UNIT_TYPE_STRUCTURE)
    endfunction

    // The unit pickup filter
    private function UnitPickupFilter takes player owner, unit target returns boolean
        return UnitAlive(target) and IsUnitType(target, UNIT_TYPE_HERO) and IsUnitEnemy(target, owner)
    endfunction

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private struct WaterOrb
        static timer timer = CreateTimer()
        static integer key = -1
        static thistype array array
        static integer array flag

        group group
        player player
        effect effect
        real duration
        real bonus
        real range
        real x
        real y

        private method remove takes integer i returns integer
            call DestroyGroup(group)
            call DestroyEffect(effect)
            
            set array[i] = array[key]
            set key = key - 1
            set group = null
            set player = null
            set effect = null

            if key == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this
            local unit u

            loop
                exitwhen i > key
                    set this = array[i]

                    if duration > 0 then
                        set duration = duration - PERIOD

                        call GroupEnumUnitsInRange(group, x, y, range, null)
                        loop
                            set u = FirstOfGroup(group)
                            exitwhen u == null
                                if UnitPickupFilter(player, u) then
                                    call AddUnitBonus(u, BONUS_MANA, bonus)
                                    call DestroyEffect(AddSpecialEffectTarget(EFFECT, u, ATTACH))
                                    set i = remove(i)
                                    exitwhen true
                                endif
                            call GroupRemoveUnit(group, u)
                        endloop
                    else
                        set i = remove(i)
                    endif
                set i = i + 1
            endloop

            set u = null
        endmethod

        private static method onDamage takes nothing returns nothing
            local real damage = GetEventDamage()

            if damage > 0 then
                set flag[Damage.target.id] = 0
                
                if damage >= GetWidgetLife(Damage.target.unit) and UnitDropFilter(Damage.target.unit) then
                    if GetUnitAbilityLevel(Damage.target.unit, BUFF_4) > 0 then
                        if GetRandomInt(0, 100) <= GetDropChance(Damage.target.unit, BUFF_4) then
                            set flag[Damage.target.id] = BUFF_4
                        endif
                    elseif GetUnitAbilityLevel(Damage.target.unit, BUFF_3) > 0 then
                        if GetRandomInt(0, 100) <= GetDropChance(Damage.target.unit, BUFF_3) then
                            set flag[Damage.target.id] = BUFF_3
                        endif
                    elseif GetUnitAbilityLevel(Damage.target.unit, BUFF_2) > 0 then
                        if GetRandomInt(0, 100) <= GetDropChance(Damage.target.unit, BUFF_2) then
                            set flag[Damage.target.id] = BUFF_2
                        endif
                    elseif GetUnitAbilityLevel(Damage.target.unit, BUFF_1) > 0 then
                        if GetRandomInt(0, 100) <= GetDropChance(Damage.target.unit, BUFF_1) then
                            set flag[Damage.target.id] = BUFF_1
                        endif
                    endif
                endif
            endif
        endmethod

        private static method onDeath takes nothing returns nothing
            local unit source = GetTriggerUnit()
            local integer id = GetUnitUserData(source)
            local thistype this
            
            if flag[id] != 0 then
                set this = thistype.allocate()
                set key = key + 1
                set array[key] = this
                set x = GetUnitX(source)
                set y = GetUnitY(source)
                set group = CreateGroup()
                set player = GetOwningPlayer(source)
                set effect = AddSpecialEffectEx(MODEL, x, y, 0, SCALE)
                set duration = GetDuratoin(flag[id])
                set range = GetPickupRange(flag[id])
                set bonus = GetManaBonus(flag[id])
                
                if key == 0 then
                    call TimerStart(timer, PERIOD, true, function thistype.onPeriod)
                endif
            endif

            set source = null
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
            call RegisterAnyDamageEvent(function thistype.onDamage)
        endmethod
    endstruct
endlibrary
library LivingTide requires RegisterPlayerUnitEvent, SpellEffectEvent, PluginSpellEffect, Missiles, Utilities, MouseUtils
    /* ---------------------- Living Tide v1.0 by Chopinski --------------------- */
    // Credits:
    //     Blizzard        - Icon
    //     Bribe           - SpellEffectEvent
    //     Vexorian        - TimerUtils
    //     Magtheridon96   - RegisterPlayerUnitEvent
    //     MyPad           - MouseUtils
    /* ----------------------------------- END ---------------------------------- */

    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the ability
        private constant integer ABILITY    = 'A004'
        // The Living Tide model
        private constant string  MODEL      = "LivingTide.mdl"
        // The Living Tide scale
        private constant real    SCALE      = 1.
        // The Living Tide speed
        private constant real    SPEED      = 550.
        // The update period
        private constant real    PERIOD     = 0.03125000
    endglobals

    // The amount of damage dealt in a second
    private function GetDamagePerSecond takes unit source, integer level returns real
        return 100. * level
    endfunction

    // The living tide collision size
    private function GetCollision takes unit source, integer level returns real
        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, ABILITY), ABILITY_RLF_AREA_OF_EFFECT, level - 1)
    endfunction

    // The living tide sight range
    private function GetVisionRange takes unit source, integer level returns real
        return 1000. + 0.*level
    endfunction

    // The base mana cost per second
    private function GetBaseManaCostPerSecond takes unit source, integer level returns integer
        return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(source, ABILITY), ABILITY_ILF_MANA_COST, level - 1)
    endfunction

    // The range step to change the amount of mana consumed
    private function GetManaCostRangeIncrement takes unit source, integer level returns real
        return 100.
    endfunction

    // The mana cost amount per range increment
    private function GetManaCostPerIncrement takes unit source, integer level returns real
        return 5.
    endfunction

    // The unit filter for damage
    private function UnitFilter takes player owner, unit target returns boolean
        return UnitAlive(target) and IsUnitEnemy(target, owner) and not IsUnitType(target, UNIT_TYPE_STRUCTURE)
    endfunction

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private struct Tide extends Missiles
        method onHit takes unit u returns boolean
            if UnitFilter(owner, u) then
                if UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) then
                    call flush(u)
                endif
            endif

            return false
        endmethod

        method onFinish takes nothing returns boolean
            call pause(true)
            return false
        endmethod
    endstruct

    private struct LivingTide
        static timer timer = CreateTimer()
        static integer key = -1
        static thistype array array
        static thistype array struct

        unit unit
        player player
        integer id
        integer level
        integer mana
        real step
        real range
        Tide tide

        method remove takes integer i returns integer
            call tide.terminate()
            
            set array[i] = array[key]
            set key = key - 1
            set unit = null
            set player = null
            set tide = 0

            if key == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer i = 0
            local real cost
            local real x
            local real y
            local thistype this

            loop
                exitwhen i > key
                    set this = array[i]

                    if struct[id] != 0 then
                        set x = GetUnitX(unit)
                        set y = GetUnitY(unit)
                        set cost = (mana + step*(DistanceBetweenCoordinates(x, y, tide.x, tide.y)/range)) * PERIOD

                        if cost > GetUnitState(unit, UNIT_STATE_MANA) then
                            call IssueImmediateOrder(unit, "stop")
                            set struct[id] = 0
                            set i = remove(i)
                        else
                            call AddUnitMana(unit, -cost)
                            call tide.deflect(GetPlayerMouseX(player), GetPlayerMouseY(player), 0)
                            call BlzSetUnitFacingEx(unit, AngleBetweenCoordinates(x, y, tide.x, tide.y)*bj_RADTODEG)

                            if tide.paused then
                                call tide.pause(false)
                            endif
                        endif
                    else
                        set i = remove(i)
                    endif
                set i = i + 1
            endloop
        endmethod

        private static method onEnd takes nothing returns nothing
            if GetSpellAbilityId() == ABILITY then
                set struct[GetUnitUserData(GetTriggerUnit())] = 0
            endif
        endmethod

        private static method onCast takes nothing returns nothing
            local thistype this

            if struct[Spell.source.id] == 0 then
                set this = thistype.allocate()
                set id = Spell.source.id
                set unit = Spell.source.unit
                set player = Spell.source.player
                set level = Spell.level
                set mana = GetBaseManaCostPerSecond(unit, level)
                set range = GetManaCostRangeIncrement(unit, level)
                set step = GetManaCostPerIncrement(unit, level)
                set key = key + 1
                set array[key] = this
                set struct[id] = this
                set tide = Tide.create(Spell.x, Spell.y, 0, Spell.source.x, Spell.source.y, 0)

                set tide.model = MODEL
                set tide.scale = SCALE
                set tide.speed = SPEED
                set tide.source = unit
                set tide.owner = player
                set tide.vision = GetVisionRange(unit, level)
                set tide.damage = GetDamagePerSecond(unit, level) / (1/Missiles_PERIOD)
                set tide.collision = GetCollision(unit, level)

                call tide.launch()

                if key == 0 then
                    call TimerStart(timer, PERIOD, true, function thistype.onPeriod)
                endif
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ABILITY, function thistype.onCast)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onEnd)
        endmethod
    endstruct
endlibrary
library BadWeather requires RegisterPlayerUnitEvent, SpellEffectEvent, PluginSpellEffect, TimerUtils, Utilities, DamageInterface
    /* ---------------------- Bad Weather v1.0 by Chopinski --------------------- */
    // Credits:
    //     Bribe                - SpellEffectEvent
    //     Magtheridon96        - RegisterPlayerUnitEvent
    //     Vexorian             - TimerUtils
    /* ----------------------------------- END ---------------------------------- */
    
    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the Ability
        private constant integer ABILITY       = 'A005'
        // The raw code of the debuff Ability
        private constant integer DEBUFF        = 'A007'
        // The raw code of the debuff buff
        private constant integer BUFF          = 'B001'
        // The rain model
        private constant string  MODEL         = "Rain.mdl"
        // The rain model scale
        private constant real    SCALE         = 2.5
        // The raw code of the Jaina unit in the editor
        private constant integer JAINA_ID      = 'H000'
        // The GAIN_AT_LEVEL is greater than 0
        // Jaina will gain Bad Weather at this level 
        private constant integer GAIN_AT_LEVEL = 20
    endglobals

    // The rain duration
    private function GetDuratoin takes unit source, integer level returns real
        return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, ABILITY), ABILITY_RLF_DURATION_HERO, level - 1)
    endfunction

    // The bonus damage dealt (use different buffs per level in the debuff ability)
    private function GetDamageBonus takes integer buffid returns real
        if buffid == BUFF then
            return 0.2
        else
            return 0.2
        endif
    endfunction

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    private struct BadWeather
        timer timer
        unit unit
        effect effect

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call UnitRemoveAbility(unit, DEBUFF)
            call DestroyEffect(effect)
            call DummyRecycle(unit)
            call ReleaseTimer(timer)
            call deallocate()
            
            set timer = null
            set unit = null
            set effect = null
        endmethod

        private static method onDamage takes nothing returns nothing
            if GetUnitAbilityLevel(Damage.target.unit, BUFF) > 0 and Damage.isEnemy then
                call BlzSetEventDamage(GetEventDamage()*(1 + GetDamageBonus(BUFF)))
            endif
        endmethod

        private static method onCast takes nothing returns nothing
            local thistype this = thistype.allocate()

            set timer = NewTimerEx(this)
            set unit = DummyRetrieve(Spell.source.player, Spell.x, Spell.y, 0, 0)
            set effect = AddSpecialEffectEx(MODEL, Spell.x, Spell.y, 0, SCALE)

            call UnitAddAbility(unit, DEBUFF)
            call TimerStart(timer, GetDuratoin(Spell.source.unit, Spell.level), false, function thistype.onExpire)
        endmethod

        private static method onLevelUp takes nothing returns nothing
            local unit u = GetTriggerUnit()
        
            if GAIN_AT_LEVEL > 0 then
                if GetUnitTypeId(u) == JAINA_ID and GetHeroLevel(u) == GAIN_AT_LEVEL then
                    call UnitAddAbility(u, ABILITY)
                    call UnitMakeAbilityPermanent(u, true, ABILITY)
                endif
            endif
        
            set u = null
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_LEVEL, function thistype.onLevelUp)
            call RegisterSpellEffectEvent(ABILITY, function thistype.onCast)
            call RegisterSpellDamageEvent(function thistype.onDamage)
        endmethod
    endstruct
endlibrary
Initialization
Events
Map initialization
Conditions
Actions
Game - Display to (All players) the text: Press the |cffffcc00ESC|r key to Level Up, Heal Health and Mana and reset his cooldowns.Use the |cff00ff00HOME key|r and |cffff0000END key|r to control camera distance.
scope Revive
    private struct Revive
        static method onDeath takes nothing returns nothing
            local unit d = GetTriggerUnit()
            local real x = GetUnitX(d)
            local real y = GetUnitY(d)

            if GetOwningPlayer(d) == Player(0) and IsUnitType(d, UNIT_TYPE_HERO) then
                call ReviveHero(d, x, y, true)
            endif

            set d = null
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
        endmethod
    endstruct
endscope
Refresh
Events
Player - Player 1 (Red) skips a cinematic sequence
Conditions
Actions
Cinematic - Clear the screen of text messages for (All players) .
Unit Group - Pick every unit in (Units owned by Player 1 (Red) matching (((Matching unit) is A Hero) Equal to True).) and do (Actions)
Loop - Actions
Unit - Set life of (Picked unit) to 100.00 %
Unit - Set mana of (Picked unit) to 100.00 %
Unit - Reset ability cooldowns for (Picked unit) .
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Hero level of (Picked unit)) Less than 20
Then - Actions
Hero - Set (Picked unit) Hero-level to ((Hero level of (Picked unit)) + 1) , Hide level-up graphics
Else - Actions
scope Spawn
    private struct Spawns
        static constant real centerX   = GetRectCenterX(gg_rct_Attack)
        static constant real centerY   = GetRectCenterY(gg_rct_Attack)

        private static method Spawn takes real x, real y, integer melee, integer ranged returns nothing
            local integer i = 0
            local unit u

            loop
                exitwhen i > 3
                    if i < 3 then
                        set u = CreateUnit(Player(1), melee, x, y, 0)
                    else
                        set u = CreateUnit(Player(1), ranged, x, y, 0)
                    endif
                    call IssuePointOrderById(u, 851983, centerX, centerY)
                set i = i + 1
            endloop

            set u = null
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer i = 0

            loop
                exitwhen i > 3
                    if i == 0 then
                        call Spawn(GetRectCenterX(gg_rct_Human), GetRectCenterY(gg_rct_Human), 'hfoo', 'hrif')
                    elseif i == 1 then
                        call Spawn(GetRectCenterX(gg_rct_NightElf), GetRectCenterY(gg_rct_NightElf), 'esen', 'earc')
                    elseif i == 2 then
                        call Spawn(GetRectCenterX(gg_rct_Undead), GetRectCenterY(gg_rct_Undead), 'ugho', 'ucry')
                    elseif i == 3 then
                        call Spawn(GetRectCenterX(gg_rct_Orc), GetRectCenterY(gg_rct_Orc), 'ogru', 'ohun')
                    endif
                set i = i + 1
            endloop
        endmethod

        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterTimerEventPeriodic(t, 45.00)
            call TriggerAddAction(t, function thistype.onPeriod)
        endmethod
    endstruct
endscope
scope Camera initializer Init
    private function AdjustCamera takes nothing returns nothing
        local real      dist = GetCameraField(CAMERA_FIELD_TARGET_DISTANCE)
        local oskeytype pKey = BlzGetTriggerPlayerKey()
    
        if pKey == OSKEY_HOME then
            call SetCameraFieldForPlayer(Player(0), CAMERA_FIELD_TARGET_DISTANCE, dist + 25, 0.00)
            call ClearTextMessages()
            call DisplayTextToPlayer(Player(0), 0, 0, "Camera Distance: " + I2S(R2I(dist + 25)))
        elseif pKey == OSKEY_END then
            call SetCameraFieldForPlayer(Player(0), CAMERA_FIELD_TARGET_DISTANCE, dist - 25, 0.00)
            call ClearTextMessages()
            call DisplayTextToPlayer(Player(0), 0, 0, "Camera Distance: " + I2S(R2I(dist - 25)))
        endif

        set pKey = null
    endfunction
    //===========================================================================
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()

        call BlzTriggerRegisterPlayerKeyEvent(t, Player(0), OSKEY_HOME, 0, false)
        call BlzTriggerRegisterPlayerKeyEvent(t, Player(0), OSKEY_END, 0, false)
        call TriggerAddCondition(t, Condition(function AdjustCamera))
        //call Cheat("iseedeadpeople")
    endfunction
endscope
scope OnDamage
    private struct OnDamage
        static method onDamage takes nothing returns nothing
            local unit       source = GetEventDamageSource()
            local unit       target
            local real       damage
            local string     text 
            local damagetype damage_type
            local attacktype attack_type

            if IsUnitType(source, UNIT_TYPE_HERO) then
                set target = BlzGetEventDamageTarget()
                set damage = GetEventDamage() 
                set text   = R2I2S(damage)

                if damage > 0 then
                    set damage_type = BlzGetEventDamageType()
                    set attack_type = BlzGetEventAttackType()

                    if damage_type == DAMAGE_TYPE_NORMAL then
                        call ArcingTextTag.create("|cffff0000" + text + "|r", target)
                    elseif attack_type == ATTACK_TYPE_NORMAL then
                        call ArcingTextTag.create("|cff00ffff" + text + "|r", target)
                    elseif damage_type == DAMAGE_TYPE_ENHANCED then
                        call ArcingTextTag.create("|cff00ff00" + text + "|r", target)
                    elseif damage_type == DAMAGE_TYPE_UNIVERSAL then
                        call ArcingTextTag.create(text, target)
                    else
                        call ArcingTextTag.create("|cffe4fd00" + text + "|r", target)
                    endif
                elseif damage < 0 then
                    call ArcingTextTag.create("|cff00ff00 +" + text + "|r", target)
                endif
            endif

            set source      = null
            set target      = null
            set damage_type = null
            set attack_type = null
        endmethod

        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DAMAGED)
            call TriggerAddCondition(t, Condition(function thistype.onDamage))
        endmethod
    endstruct
endscope
Top