1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  5. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  6. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  7. The results are out! Check them out.
    Dismiss Notice
  8. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  9. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  10. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

Spinning Black Hole v1.8 [3D].w3x
Variables
Spinning Black Hole [3D] by AGD
Required Libraries
UnitMotion
UnitMotionSentinel
StunSystem
Optional Requirements
SpellEffectEvent
DummyRecycler vJASS
ResourcePreloader
UnitDex
GroupUtils
Table
Other Dependencies
ErrorMessage
Vector
TimerUtils
RegisterPlayerUnitEvent
BJObjectId
WorldBounds
AutoFly
The Spell
Spinning Black Hole
Enter map-specific custom script code below. This text will be included in the map script after variables are declared and before any trigger code.
scope Init initializer PreInit

    globals// constants
        private constant integer CREEP_ID = 'hpea'
        private constant integer HERO_ID = 'H000'
        private constant integer TEST_OBJECT_ID = 'N001'
        private constant real CAM_HEIGHT_ADD = 200.00
        private constant real DECAY_TIME = 20.00
        private constant integer CAMP_COUNT = 30
        private constant integer CREEP_COUNT = 20
        private constant player CREEPS_OWNER = Player(1)
    endglobals

    globals
        private constant hashtable table = InitHashtable()
        private real CENTER_X
        private real CENTER_Y
    endglobals

    private constant function Map takes nothing returns rect
        return bj_mapInitialPlayableArea
    endfunction

    private function RandomX takes nothing returns real
        return GetRandomReal(GetRectMinX(Map()), GetRectMaxX(Map()))
    endfunction

    private function RandomY takes nothing returns real
        return GetRandomReal(GetRectMinY(Map()), GetRectMaxY(Map()))
    endfunction

    private function IsHero takes unit u returns boolean
        return IsUnitType(u, UNIT_TYPE_HERO)
    endfunction

    private function A takes nothing returns nothing
        local real manapercent = 100
        local unit u = GetTriggerUnit()
        if IsHero(u) then
            call TriggerSleepAction(2.00)
            call ReviveHero(u, CENTER_X, CENTER_Y, true)
            call SetUnitFlyHeight(u, 400.00, 0.00)
            call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MAX_MANA )*manapercent*0.01)
            call IssueImmediateOrder(u, "manashieldon")
        endif
        set u = null
    endfunction

    private function B takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local integer handleId
        local real x
        local real y
        if not IsHero(u) then
            call TriggerSleepAction(DECAY_TIME)
            set handleId = GetHandleId(u)
            set x = LoadReal(table, handleId, 0)
            set y = LoadReal(table, handleId, 1)
            call RemoveUnit(u)
            call FlushChildHashtable(table, handleId)
            set handleId = GetHandleId(CreateUnit(GetOwningPlayer(u), GetUnitTypeId(u), x, y, 0))
            call SaveReal(table, handleId, 0, x)
            call SaveReal(table, handleId, 1, y)
        endif
        set u = null
    endfunction

    private function C takes nothing returns nothing
        local unit u = GetTriggerUnit()
        if IsUnitAlly(u, GetTriggerPlayer()) then
            if IsHero(u) then
                call SetHeroLevel(u, (GetHeroLevel(u) + 1), false)
            endif
        endif
        set u = null
    endfunction

    private function D takes nothing returns nothing
        if GetLocalPlayer() == Player(0) then
            call SetCameraField(CAMERA_FIELD_ZOFFSET, GetCameraField(CAMERA_FIELD_ZOFFSET) + CAM_HEIGHT_ADD, 0)
        endif
    endfunction

    private function Init takes nothing returns nothing

        local trigger t1 = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local trigger t3 = CreateTrigger()
        local trigger t4 = CreateTrigger()

        local integer a = 1
        local integer b = 1
        local quest q1 = CreateQuest()
        local quest q2 = CreateQuest()
        local string q1_title = "|cff0000ffSpinning Black Hole|r v1.8 |cffffcc00[3D]|r"
        local string q2_title = "|cffffcc00Share your Feedback|r"
        local string q1_descript = "Test Commands:|n - Press Esc to zoom out camera view|n - Select your hero to level up|n"
        local string q2_descript = "If you found some bugs and glithes, you can PM AGD at HiveWorkshop.com.|nPlease also share your comments and suggestions regarding the spell."
        local string q1_icon = "war3mapImported\\WarcraftIcon.tga"
        local string q2_icon = "war3mapImported\\NoIcon.tga"
        local unit u = CreateUnit(CREEPS_OWNER, TEST_OBJECT_ID, RandomX(), RandomY(), 0)
        local real x
        local real y
        local integer handleId

        call SelectUnit(CreateUnit(Player(0), HERO_ID, CENTER_X, CENTER_Y, 0), true)

        call QuestSetTitle(q1, q1_title)
        call QuestSetDescription(q1, q1_descript)
        call QuestSetIconPath(q1, q1_icon)
        call QuestSetRequired(q1, true)
        call QuestSetDiscovered(q1, true)
        call QuestSetCompleted(q1, false)

        call QuestSetTitle(q2, q2_title)
        call QuestSetDescription(q2, q2_descript)
        call QuestSetIconPath(q2, q2_icon)
        call QuestSetRequired(q2, false)
        call QuestSetDiscovered(q2, true)
        call QuestSetCompleted(q2, false)

        call TriggerRegisterPlayerUnitEvent(t1, CREEPS_OWNER, EVENT_PLAYER_UNIT_DEATH, null)
        call TriggerRegisterPlayerUnitEvent(t2, CREEPS_OWNER, EVENT_PLAYER_UNIT_DECAY, null)
        call TriggerRegisterPlayerUnitEvent(t3, Player(0), EVENT_PLAYER_UNIT_SELECTED, null)
        call TriggerRegisterPlayerEvent(t4, Player(0), EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(t1, function A)
        call TriggerAddAction(t2, function B)
        call TriggerAddAction(t3, function C)
        call TriggerAddAction(t4, function D)

        call FogEnable(false)
        call FogMaskEnable(false)
        call SetPlayerState(CREEPS_OWNER, PLAYER_STATE_GIVES_BOUNTY, 1)
        call SetHeroLevel(u, 10, false)
        call SelectHeroSkill(u, 'ANms')
        call SelectHeroSkill(u, 'ANms')
        call SelectHeroSkill(u, 'ANms')
        call SelectHeroSkill(u, 'ANms')
        call SelectHeroSkill(u, 'ANms')

        loop
            exitwhen a > CAMP_COUNT
            set b = 1
            set x = RandomX()
            set y = RandomY()
            loop
                exitwhen b > CREEP_COUNT
                set handleId = GetHandleId(CreateUnit(CREEPS_OWNER, CREEP_ID, x, y, 0))
                call SaveReal(table, handleId, 0, x)
                call SaveReal(table, handleId, 1, y)
                set b = b + 1
            endloop
            set a = a + 1
        endloop

    endfunction

    private function PreInit takes nothing returns nothing
        set CENTER_X = GetRectCenterX(Map())
        set CENTER_Y = GetRectCenterY(Map())
        call Init()
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 120, "
    Test:
    - Press ESC in-game to zoom out camera for a better view of the black hole explosion.
    - Then scroll the mouse wheel upwards to go back to default camera altitude.
    - Select your hero to level up.")
    endfunction

endscope
Name Type Is Array Initial Value
Ca = (v^2)/r
library UnitMotion /*


    */
uses /*

    */
ErrorMessage      /*   https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
    */
Vector            /*   http://www.wc3c.net/showthread.php?t=87027
    */
Table             /*   https://www.hiveworkshop.com/threads/snippet-new-table.188084/
    */
UnitDex           /*   https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
    */
optional AutoFly  /*   https://www.hiveworkshop.com/threads/snippet-autofly.249422/


    */
//! novjass

    /*
    Description:

        This system is used to handle if possible, all unit movements, internally. You can use this
        to easily create knockback movements, throws, and jumps whether it be uniform or accelerated
        motion. By default, this system also takes into consideration the units' acceleration due to
        gravity as well as the side effect of gravity, i.e., ground friction. But if you don't need
        these additional features, you can just set their values to 0 in the configuration section
        below.
    */


    |=========|
    | CREDITS |
    |=========|

        Author:
            - AGD

        Dependencies:
            - Anitarf (Vector)
            - Vexorian (TimerUtils)
            - Bribe (Table)
            - TriggerHappy (UnitDex, AutoFly)
            - Nestharus (ErrorMessage)

    |-----|
    | API |
    |-----|

        struct UnitMotion/*

          */
static constant real FPS                       /*   Frames per second
          */
static constant real FRAME_RATE                /*   Reciprocal of FPS (Refresh rate)
          */
static constant real GRAVITY                   /*   Acceleration due to gravity in units per second^2
          */
static constant real GROUND_FRICTION           /*   The coefficient of friction of the ground
          */
static constant real DEFAULT_UNIT_FRICTION     /*   The default value of unit's coefficient of friction
          */
static constant boolean AUTO_REGISTER_UNITS    /*   Determines whether newly entering units in the game are
                                                                automatically added to the system

          */
readonly static UnitMotion triggerInstance     /*   Use this to refer to the UnitMotion instance of
                                                                the moving unit inside a motion handler function

          */
real x                                         /*   The x-component of the UnitMotion
          */
real y                                         /*   The y-component of the UnitMotion
          */
real z                                         /*   The z-component of the UnitMotion
          */
real coefficientOfFriction                     /*   The coefficient of friction of the unit
          */
readonly real friction                         /*   The magnitude of the ground friction vector acting on the unit
          */
readonly boolean airborne                      /*   Determines if the unit referred by the UnitMotion instance is airborne
          */
readonly boolean freeFalling                   /*   Determines if the unit referred by the UnitMotion instance is free-falling
          */
readonly unit unit                             /*   The unit the UnitMotion instance belongs to

          */
static method operator [] takes unit whichUnit returns UnitMotion/*
                - Reftrieves the UnitMotion instance corresponding to the input unit

          */
method destroy takes nothing returns nothing/*
                - Destroys a UnitMotion instance
                - Only available when AUTO_REGISTER_UNITS == false

          */
method addVelocityByVector takes vector whichVector returns this/*
                - Adds a vector to the UnitMotion's velocity vector
          */
method addVelocity takes Velocity velocity returns this/*
          */
method removeVelocity takes Velocity velocity returns this/*
                - Adds/Removes a Velocity from this UnitMotion instance

          */
method addAccelerationByVector takes vector whichVector returns this/*
                - Adds a vector to the UnitMotion's acceleration vector
          */
method addAcceleration takes Acceleration acceleration returns this/*
          */
method removeAcceleration takes Acceleration acceleration returns this/*
                - Adds/Removes an Acceleration from this UnitMotion instance

          */
method addTorqueByVector takes vector axisOfRotation returns this/*
                - Adds a vector to the UnitMotion's torque axis vector
                - The length of the <axisOfRotation> vector determines the magnitude of the torque
          */
method addTorqueByVectorEx takes vector axisOfRotation, real radius returns this/*
                - Adds a vector to the UnitMotion's torque axis vector
                - The magnitude added by the vector is equal to the magnitude of the torque necessary to make
                  a circular motion with a radius equal to the input radius
          */
method addTorque takes Torque torque returns this/*
          */
method removeTorque takes Torque torque returns this/*
                - Adds a centripetal acceleration to the UnitMotion instance

          */
method addTorqueIncrement takes vector axisOfRotation, real magnitude returns this/*
                - Adds an increment in the torque's magnitude per period

          */
method registerMotionHandler takes boolexpr expr returns this/*
          */
method unregisterMotionHandler takes boolexpr expr returns this/*
          */
method clearMotionHandlers takes nothing returns this/*
                - Manages boolexprs that runs when the unit moves due to a vector applied
                  to the unit using this system

          */
static method registerUnitAddHandler takes boolexpr expr returns nothing/*
          */
static method unregisterUnitAddHandler takes boolexpr expr returns nothing/*
          */
static method clearUnitAddHandlers takes nothing returns nothing/*
                - Manages boolexprs that runs when a UnitMotion instance is first created for a unit
                - Only available when UnitMotion.AUTO_REGISTER_UNITS == false

          */
static method registerRemoveAddHandler takes boolexpr expr returns nothing/*
          */
static method unregisterRemoveAddHandler takes boolexpr expr returns nothing/*
          */
static method clearUnitRemoveHandlers takes nothing returns nothing/*
                - Manages boolexprs that runs when a UnitMotion instance of a unit is destroyed
                - Only available when UnitMotion.AUTO_REGISTER_UNITS == false


      */
struct Velocity/*

          */
static method create takes nothing returns Velocity/*
          */
method destroy takes nothing returns nothing/*
                - Creates/Destroys a Velocity instance

          */
method add takes vector whichVector returns nothing/*
                - Adds a vector to this Velocity's vector


      */
struct Acceleration/*

          */
static method create takes nothing returns Acceleration/*
          */
method destroy takes nothing returns nothing/*
                - Creates/Destroys an Acceleration instance

          */
method add takes vector whichVector returns nothing/*
                - Adds a vector to this Acceleration's vector


      */
struct Torque/*

          */
static method create takes nothing returns Torque/*
          */
method destroy takes nothing returns nothing/*
                - Creates/Destroys a Torque instance

          */
method add takes vector axisOfRotation returns nothing/*
                - Adds a vector to this Torque's vector
                - The length of the <axisOfRotation> vector determines the magnitude
                  of the torque


    */
//! endnovjass

    private keyword Init
    private keyword List

    struct UnitMotion extends array
        /********************************************************/
        /*                 SYSTEM CONFIGURATION                 */
        /********************************************************/
        /*
        The number of frames per second in which
        the periodic function operates.
        Default Value: 32                                       */

        static constant real FPS                        = 32.00
        /*
        The 'acceleration' due to gravity.
        Default Value: -981.00                                  */

        static constant real GRAVITY                    = -981.00
        /*
        The coefficient of the ground friction.
        Default Value: 0.70                                     */

        static constant real GROUND_FRICTION            = 0.70
        /*
        The default value for the coefficient of
        friction for units. This value is applied
        upon UnitMovement creation.
        Default Value: 0.60                                     */

        static constant real DEFAULT_UNIT_FRICTION      = 0.60
        /*
        Determines if newly created units are
        automatically registered into the
        system.                                                 */

        static constant boolean AUTO_REGISTER_UNITS     = false
        /********************************************************/
        /*                 END OF CONFIGURATION                 */
        /********************************************************/
        /*======================================================*/
        /*   Do not change anything below this line if you're   */
        /*          not so sure on what you're doing.           */
        /*======================================================*/

        private thistype prev
        private thistype next
        private boolean flag
        private integer motionHandlerCount
        private real deltaFriction
        private real unitFriction
        private trigger motionHandlerTrigger
        private vector velocity
        private vector acceleration
        private vector torqueAxis
        private vector torqueIncrement
        private vector timerForce

        readonly static thistype triggerInstance = 0
        readonly static thistype addedInstance = 0
        readonly static thistype removedInstance = 0
        static constant real FRAME_RATE = 1.00/FPS
        private static timer timer = CreateTimer()
        private static group enumerator = CreateGroup()
        private static code periodicCode
        private static TableArray tableArray
        private static TableArray velocityTable
        private static TableArray accelerationTable
        private static TableArray torqueTable

        static if not thistype.AUTO_REGISTER_UNITS then
            private static trigger unitAddHandlerTrigger
            private static trigger unitRemoveHandlerTrigger
            private static integer unitAddHandlerCount = 0
            private static integer unitRemoveHandlerCount = 0
        endif

        private method constructor takes nothing returns nothing
            local thistype last = thistype(0).prev
            set thistype(0).prev = this
            set last.next = this
            set this.prev = last
            set this.next = 0
            set this.velocity = vector.create(0.00, 0.00, 0.00)
            set this.acceleration = vector.create(0.00, 0.00, GRAVITY*FRAME_RATE)
            set this.torqueAxis = vector.create(0.00, 0.00, 0.00)
            set this.torqueIncrement = vector.create(0.00, 0.00, 0.00)
            set this.unitFriction = DEFAULT_UNIT_FRICTION
            set this.deltaFriction = GRAVITY*GROUND_FRICTION*DEFAULT_UNIT_FRICTION*FRAME_RATE
            set this.flag = true
            static if not LIBRARY_AutoFly then
                if UnitAddAbility(GetUnitById(this), 'Arav') and UnitRemoveAbility(GetUnitById(this), 'Arav') then
                endif
            endif
            if this.prev == 0 then
                call TimerStart(timer, FRAME_RATE, true, periodicCode)
            endif
        endmethod

        private method destructor takes nothing returns nothing
            static if not thistype.AUTO_REGISTER_UNITS then
                local thistype prev = removedInstance
                set removedInstance = this
                call TriggerEvaluate(unitRemoveHandlerTrigger)
                set removedInstance = prev
            endif
            call this.velocity.destroy()
            call this.acceleration.destroy()
            call this.torqueAxis.destroy()
            call this.torqueIncrement.destroy()
            call tableArray[this].flush()
            call DestroyTrigger(this.motionHandlerTrigger)
            set this.motionHandlerTrigger = null
            set this.velocity = 0
            set this.acceleration = 0
            set this.torqueAxis = 0
            set this.torqueIncrement = 0
            set this.deltaFriction = 0.00
            set this.unitFriction = 0.00
            set this.motionHandlerCount = 0
            set this.flag = false
            set this.next.prev = this.prev
            set this.prev.next = this.next
            if thistype(0).next == 0 then
                call PauseTimer(timer)
            endif
        endmethod

        static method operator [] takes unit u returns thistype
            static if not thistype.AUTO_REGISTER_UNITS then
                local thistype this = GetUnitId(u)
                local thistype prev
                if not this.flag then
                    call this.constructor()
                    set prev = addedInstance
                    set addedInstance = this
                    call TriggerEvaluate(unitAddHandlerTrigger)
                    set addedInstance = prev
                endif
                return this
            else
                return GetUnitId(u)
            endif
        endmethod

        static if not thistype.AUTO_REGISTER_UNITS then
            method destroy takes nothing returns nothing
                debug call ThrowError(not this.flag, "UnitMotion", "destroy", "thistype", this, "Attempted to destroy an unallocated instance")
                call this.destructor()
            endmethod
        endif

        method operator unit takes nothing returns unit
            debug call ThrowError(not this.flag, "UnitMotion", "unit", "thistype", this, "Attempted to access an unallocated instance field")
            return GetUnitById(this)
        endmethod

        method operator airborne takes nothing returns boolean
            debug call ThrowError(not this.flag, "UnitMotion", "airborne", "thistype", this, "Attempted to access an unallocated instance field")
            return GetUnitFlyHeight(this.unit) > GetUnitDefaultFlyHeight(this.unit) + 10.00
        endmethod

        method operator x= takes real x returns nothing
            debug call ThrowError(not this.flag, "UnitMotion", "x=", "thistype", this, "Attempted to configure an unallocated instance")
            set this.velocity.x = x
        endmethod
        method operator x takes nothing returns real
            debug call ThrowError(not this.flag, "UnitMotion", "x", "thistype", this, "Attempted to access an unallocated instance field")
            return this.velocity.x
        endmethod

        method operator y= takes real y returns nothing
            debug call ThrowError(not this.flag, "UnitMotion", "y=", "thistype", this, "Attempted to configure an unallocated instance.")
            set this.velocity.y = y
        endmethod
        method operator y takes nothing returns real
            debug call ThrowError(not this.flag, "UnitMotion", "y", "thistype", this, "Attempted to access an unallocated instance field.")
            return this.velocity.y
        endmethod

        method operator z= takes real z returns nothing
            debug call ThrowError(not this.flag, "UnitMotion", "z=", "thistype", this, "Attempted to configure an unallocated instance")
            set this.velocity.z = z
        endmethod
        method operator z takes nothing returns real
            debug call ThrowError(not this.flag, "UnitMotion", "z", "thistype", this, "Attempted to access an unallocated instance field")
            return this.velocity.z
        endmethod

        method operator coefficientOfFriction= takes real frictionFactor returns nothing
            debug call ThrowError(not this.flag, "UnitMotion", "coefficientOfFriction=", "thistype", this, "Attempted to configure an unallocated instance")
            set this.unitFriction = frictionFactor
            set this.deltaFriction = RMinBJ(this.acceleration.z*GROUND_FRICTION*frictionFactor, 0.00)
        endmethod
        method operator coefficientOfFriction takes nothing returns real
            debug call ThrowError(not .flag, "UnitMotion", "coefficientOfFriction", "thistype", this, "Attempted to access an unallocated instance field")
            return this.unitFriction
        endmethod

        method operator friction takes nothing returns real
            debug call ThrowError(not .flag, "UnitMotion", "friction", "thistype", this, "Attempted to access an unallocated instance field")
            return this.deltaFriction*FPS
        endmethod

        method operator freeFalling takes nothing returns boolean
            local vector acceleration = this.acceleration
            debug call ThrowError(not this.flag, "UnitMotion", "freeFalling", "thistype", this, "Attempted to use an unallocated instance")
            return this.airborne                           and /*
            */
    acceleration.x == 0.00                  and /*
            */
    acceleration.y == 0.00                  and /*
            */
    acceleration.z == GRAVITY*FRAME_RATE
        endmethod

        private static method addVector takes vector this, vector toAdd returns nothing
            if toAdd < 0 then
                call this.subtract(-toAdd)
            else
                call this.add(toAdd)
            endif
        endmethod

        method addVelocityByVector takes vector whichVector returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "addVelocityByVector()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(whichVector == 0, "UnitMotion", "addVelocityByVector()", "thistype", this, "Attempted to use a null vector instance")
            call addVector(this.velocity, whichVector)
            return this
        endmethod
        method addVelocity takes Velocity velocity returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "addVelocity()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(velocity == 0, "UnitMotion", "addVelocity()", "thistype", this, "Attempted to use a null Velocity instance")
            debug call ThrowError(velocity < 0, "UnitMotion", "addVelocity()", "thistype", this, "Attempted to use an invalid Velocity instance")
            debug call ThrowError(velocityTable[this].has(velocity), "UnitMotion", "addVelocity()", "thistype", this, "Attempted to add an already added Velocity instance")
            call this.velocity.add(velocity)
            set velocityTable[this][velocity] = s__Velocity_references[velocity].insert(this)
            return this
        endmethod
        method removeVelocity takes Velocity velocity returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "removeVelocity()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(velocity == 0, "UnitMotion", "removeVelocity()", "thistype", this, "Attempted to use a null Velocity instance")
            debug call ThrowError(velocity < 0, "UnitMotion", "removeVelocity()", "thistype", this, "Attempted to use an invalid Velocity instance")
            debug call ThrowError(not velocityTable[this].has(velocity), "UnitMotion", "removeVelocity()", "thistype", this, "Attempted to remove an unadded Velocity instance")
            call this.velocity.subtract(velocity)
            call List(velocityTable[this][velocity]).remove()
            call velocityTable[this].remove(velocity)
            return this
        endmethod

        method addAccelerationByVector takes vector whichVector returns thistype
            local vector acceleration = this.acceleration
            debug call ThrowError(not this.flag, "UnitMotion", "addAccelerationByVector()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(whichVector == 0, "UnitMotion", "addAccelerationByVector()", "thistype", this, "Attempted to use a null vector instance")
            call whichVector.scale(FRAME_RATE)
            call addVector(acceleration, whichVector)
            call whichVector.scale(FPS)
            set this.deltaFriction = RMinBJ(acceleration.z*GROUND_FRICTION*this.unitFriction, 0.00)
            return this
        endmethod
        method addAcceleration takes Acceleration acceleration returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "addAcceleration()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(acceleration == 0, "UnitMotion", "addAcceleration()", "thistype", this, "Attempted to use a null Acceleration instance")
            debug call ThrowError(acceleration < 0, "UnitMotion", "addAcceleration()", "thistype", this, "Attempted to use an invalid Acceleration instance")
            debug call ThrowError(accelerationTable[this].has(acceleration), "UnitMotion", "addAcceleration()", "thistype", this, "Attempted to add an already added Acceleration instance")
            call this.acceleration.add(acceleration)
            set this.deltaFriction = RMinBJ(this.acceleration.z*GROUND_FRICTION*this.unitFriction, 0.00)
            set accelerationTable[this][acceleration] = s__Acceleration_references[acceleration].insert(this)
            return this
        endmethod
        method removeAcceleration takes Acceleration acceleration returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "removeAcceleration()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(acceleration == 0, "UnitMotion", "removeAcceleration()", "thistype", this, "Attempted to use a null Acceleration instance")
            debug call ThrowError(acceleration < 0, "UnitMotion", "removeAcceleration()", "thistype", this, "Attempted to use an invalid Acceleration instance")
            debug call ThrowError(not accelerationTable[this].has(acceleration), "UnitMotion", "removeAcceleration()", "thistype", this, "Attempted to remove an unadded Acceleration instance")
            call this.acceleration.subtract(acceleration)
            set this.deltaFriction = RMinBJ(this.acceleration.z*GROUND_FRICTION*this.unitFriction, 0.00)
            call List(accelerationTable[this][acceleration]).remove()
            call accelerationTable[this].remove(acceleration)
            return this
        endmethod

        method addTorqueByVector takes vector axis returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "addTorqueByVector()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(axis == 0, "UnitMotion", "addTorqueByVector()", "thistype", this, "Attempted to use a null vector instance")
            call axis.scale(FRAME_RATE)
            call addVector(this.torqueAxis, axis)
            call axis.scale(FPS)
            return this
        endmethod
        method addTorqueByVectorEx takes vector axis, real radius returns thistype
            local real speed = this.velocity.getLength()
            local real length = axis.getLength()
            debug call ThrowError(not this.flag, "UnitMotion", "addTorqueByVectorEx()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(axis == 0, "UnitMotion", "addTorqueByVectorEx()", "thistype", this, "Attempted to use a null vector instance")
            debug call ThrowError(radius == 0.00, "UnitMotion", "addTorqueByVectorEx()", "thistype", this, "Attempted to input a zero radius")
            call axis.scale((speed*speed)/(radius*length))
            call addVector(this.torqueAxis, axis)
            call axis.scale((radius*length)/(speed*speed))
            return this
        endmethod
        method addTorque takes Torque torque returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "addTorque()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(torque == 0, "UnitMotion", "addTorque()", "thistype", this, "Attempted to use a null Torque instance")
            debug call ThrowError(torque < 0, "UnitMotion", "addTorque()", "thistype", this, "Attempted to use an invalid Torque instance")
            debug call ThrowError(torqueTable[this].has(torque), "UnitMotion", "addTorque()", "thistype", this, "Attempted to add an already added Torque instance")
            call this.torqueAxis.add(torque)
            set torqueTable[this][torque] = s__Torque_references[torque].insert(this)
            return this
        endmethod
        method removeTorque takes Torque torque returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "removeTorque()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(torque == 0, "UnitMotion", "removeTorque()", "thistype", this, "Attempted to use a null Torque instance")
            debug call ThrowError(torque < 0, "UnitMotion", "removeTorque()", "thistype", this, "Attempted to use an invalid Torque instance")
            debug call ThrowError(torqueTable[this].has(torque), "UnitMotion", "removeTorque()", "thistype", this, "Attempted to remove an unadded Torque instance")
            call this.torqueAxis.subtract(torque)
            call List(torqueTable[this][torque]).remove()
            call torqueTable[this].remove(torque)
            return this
        endmethod

        method addTorqueIncrement takes vector axis, real magnitude returns thistype
            local real length = axis.getLength()
            debug call ThrowError(not this.flag, "UnitMotion", "addTorqueIncrement()", "thistype", this, "Attempted to use an unallocated instance")
            call axis.setLength(FRAME_RATE*magnitude/length)
            call addVector(this.torqueIncrement, axis)
            call axis.setLength(FPS*length/magnitude)
            return this
        endmethod

        method registerMotionHandler takes boolexpr expr returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "registerMotionHandler()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(expr == null, "UnitMotion", "registerMotionHandler()", "thistype", this, "Attemped to register a null boolexpr")
            debug call ThrowError(tableArray[this].handle.has(GetHandleId(expr)), "UnitMotion", "registerHandler()", "thistype", this, "Attemped to register a boolexpr twice")
            if this.motionHandlerCount == 0 then
                set this.motionHandlerTrigger = CreateTrigger()
            endif
            set this.motionHandlerCount = this.motionHandlerCount + 1
            set tableArray[this].triggercondition[GetHandleId(expr)] = TriggerAddCondition(this.motionHandlerTrigger, expr)
            return this
        endmethod
        method unregisterMotionHandler takes boolexpr expr returns thistype
            local integer exprId
            debug call ThrowError(not this.flag, "UnitMotion", "unregisterMotionHandler()", "thistype", this, "Attempted to use an unallocated instance")
            debug call ThrowError(expr == null, "UnitMotion", "unregisterMotionHandler()", "thistype", this, "Attemped to unregister a null boolexpr")
            debug call ThrowError(not tableArray[this].handle.has(GetHandleId(expr)), "UnitMotion", "unregisterHandler()", "thistype", this, "Attemped to unregister a boolexpr twice")
            set this.motionHandlerCount = this.motionHandlerCount - 1
            if this.motionHandlerCount == 0 then
                call tableArray[this].handle.remove(GetHandleId(expr))
                call DestroyTrigger(this.motionHandlerTrigger)
                set this.motionHandlerTrigger = null
            else
                set exprId = GetHandleId(expr)
                call TriggerRemoveCondition(this.motionHandlerTrigger, tableArray[this].triggercondition[exprId])
                call tableArray[this].handle.remove(exprId)
            endif
            return this
        endmethod
        method clearMotionHandlers takes nothing returns thistype
            debug call ThrowError(not this.flag, "UnitMotion", "unit", "thistype", this, "Attempted to use an unallocated instance")
            call tableArray[this].flush()
            call DestroyTrigger(this.motionHandlerTrigger)
            set this.motionHandlerTrigger = null
            set this.motionHandlerCount = 0
            return this
        endmethod

        static if not thistype.AUTO_REGISTER_UNITS then
            static method registerUnitAddHandler takes boolexpr expr returns nothing
                debug call ThrowError(expr == null, "UnitMotion", "registerUnitAddHandler()", "thistype", 0, "Attemped to register a null boolexpr")
                debug call ThrowError(tableArray[0].handle.has(GetHandleId(expr)), "UnitMotion", "registerUnitAddHandler()", "thistype", 0, "Attemped to register a boolexpr twice")
                if unitAddHandlerCount == 0 then
                    set unitAddHandlerTrigger = CreateTrigger()
                endif
                set unitAddHandlerCount = unitAddHandlerCount + 1
                set tableArray[0].triggercondition[GetHandleId(expr)] = TriggerAddCondition(unitAddHandlerTrigger, expr)
            endmethod
            static method unregisterUnitAddHandler takes boolexpr expr returns nothing
                local integer exprId
                debug call ThrowError(expr == null, "UnitMotion", "unregisterUnitAddHandler()", "thistype", 0, "Attemped to unregister a null boolexpr")
                debug call ThrowError(not tableArray[0].handle.has(GetHandleId(expr)), "UnitMotion", "unregisterUnitAddHandler()", "thistype", 0, "Attemped to unregister a boolexpr twice")
                set unitAddHandlerCount = unitAddHandlerCount - 1
                if unitAddHandlerCount == 0 then
                    call tableArray[0].handle.remove(GetHandleId(expr))
                    call DestroyTrigger(unitAddHandlerTrigger)
                    set unitAddHandlerTrigger = null
                else
                    set exprId = GetHandleId(expr)
                    call TriggerRemoveCondition(unitAddHandlerTrigger, tableArray[0].triggercondition[exprId])
                    call tableArray[0].handle.remove(exprId)
                endif
            endmethod
            static method clearUnitAddHandlers takes nothing returns nothing
                call tableArray[0].flush()
                call DestroyTrigger(unitAddHandlerTrigger)
                set unitAddHandlerTrigger = null
                set unitAddHandlerCount = 0
            endmethod

            static method registerUnitRemoveHandler takes boolexpr expr returns nothing
                debug call ThrowError(expr == null, "UnitMotion", "registerUnitRemoveHandler()", "thistype", 0, "Attemped to register a null boolexpr")
                debug call ThrowError(tableArray[8191].handle.has(GetHandleId(expr)), "UnitMotion", "registerUnitRemoveHandler()", "thistype", 0, "Attemped to register a boolexpr twice")
                if unitRemoveHandlerCount == 0 then
                    set unitRemoveHandlerTrigger = CreateTrigger()
                endif
                set unitRemoveHandlerCount = unitRemoveHandlerCount + 1
                set tableArray[8191].triggercondition[GetHandleId(expr)] = TriggerAddCondition(unitRemoveHandlerTrigger, expr)
            endmethod
            static method unregisterUnitRemoveHandler takes boolexpr expr returns nothing
                local integer exprId
                debug call ThrowError(expr == null, "UnitMotion", "unregisterUnitRemoveHandler()", "thistype", 0, "Attemped to unregister a null boolexpr")
                debug call ThrowError(not tableArray[8191].handle.has(GetHandleId(expr)), "UnitMotion", "unregisterUnitRemoveHandler()", "thistype", 0, "Attemped to unregister a boolexpr twice")
                set unitRemoveHandlerCount = unitRemoveHandlerCount - 1
                if unitRemoveHandlerCount == 0 then
                    call tableArray[8191].handle.remove(GetHandleId(expr))
                    call DestroyTrigger(unitRemoveHandlerTrigger)
                    set unitRemoveHandlerTrigger = null
                else
                    set exprId = GetHandleId(expr)
                    call TriggerRemoveCondition(unitRemoveHandlerTrigger, tableArray[8191].triggercondition[exprId])
                    call tableArray[8191].handle.remove(exprId)
                endif
            endmethod
            static method clearUnitRemoveHandlers takes nothing returns nothing
                call tableArray[8191].flush()
                call DestroyTrigger(unitRemoveHandlerTrigger)
                set unitRemoveHandlerTrigger = null
                set unitRemoveHandlerCount = 0
            endmethod
        endif

        private static method vectorNotZero takes vector this returns boolean
            return this.x != 0.00 or/*
                */
this.y != 0.00 or/*
                */
this.z != 0.00
        endmethod

        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            local thistype prev
            local thistype next
            local unit u
            local real xVel
            local real yVel
            local real unitZ
            local real defaultZ
            local real friction
            local vector velocity
            local vector acceleration
            local vector torqueAxis
            local vector torqueIncrement
            loop
                exitwhen this == 0
                set velocity = this.velocity
                set acceleration = this.acceleration
                if vectorNotZero(acceleration) then
                    call velocity.add(acceleration)
                    set this.deltaFriction = RMinBJ(acceleration.z*GROUND_FRICTION*this.unitFriction, 0.00)
                endif
                if vectorNotZero(velocity) then
                    set u = this.unit
                    set unitZ = GetUnitFlyHeight(u)
                    set defaultZ = GetUnitDefaultFlyHeight(u) + 0.10
                    if unitZ > defaultZ then
                        call SetUnitPropWindow(u, 0.00)
                        call SetUnitTurnSpeed(u, 0.00)
                    else
                        call SetUnitPropWindow(u, GetUnitDefaultPropWindow(u))
                        call SetUnitTurnSpeed(u, GetUnitDefaultTurnSpeed(u))
                        set friction = this.deltaFriction
                        if velocity.z < 0.00 then
                            set velocity.z = 0.00
                        endif
                        if friction < 0.00 then
                            set xVel = velocity.x
                            set yVel = velocity.y
                            if xVel != 0.00 or yVel != 0.00 then
                                if friction*friction > xVel*xVel + yVel*yVel then
                                    set velocity.x = 0.00
                                    set velocity.y = 0.00
                                else
                                    call velocity.setLength(RMaxBJ(velocity.getLength() + friction, 0.00))
                                endif
                            endif
                        endif
                    endif
                    if velocity.x != 0.00 or velocity.y != 0.00 or velocity.z > 0.00 or (unitZ > defaultZ and velocity.z != 0.00) then
                        set torqueAxis = this.torqueAxis
                        set torqueIncrement = this.torqueIncrement
                        if vectorNotZero(torqueIncrement) then
                            call torqueAxis.add(torqueIncrement)
                        endif
                        if vectorNotZero(torqueAxis) then
                            call velocity.rotate(torqueAxis, torqueAxis.getLength()/velocity.getLength())
                        endif
                        call SetUnitX(u, GetUnitX(u) + velocity.x*FRAME_RATE)
                        call SetUnitY(u, GetUnitY(u) + velocity.y*FRAME_RATE)
                        call SetUnitFlyHeight(u, unitZ + velocity.z*FRAME_RATE, 0.00)
                        set triggerInstance = this
                        call TriggerEvaluate(this.motionHandlerTrigger)
                        set triggerInstance = 0
                    endif
                endif
                set this = this.next
            endloop
            set u = null
        endmethod

        static if thistype.AUTO_REGISTER_UNITS then
            private static method onIndex takes nothing returns nothing
                call thistype(GetIndexedUnitId()).constructor()
            endmethod
            private static method onDeindex takes nothing returns nothing
                call thistype(GetIndexedUnitId()).destructor()
            endmethod
        endif

        private static method init takes nothing returns nothing
            static if thistype.AUTO_REGISTER_UNITS then
                local code onIndex = function thistype.onIndex
                local code onDeindex = function thistype.onDeindex
                call OnUnitIndex(onIndex)
                call OnUnitDeindex(onDeindex)
            endif
            set periodicCode = function thistype.periodic
            set tableArray = TableArray[0x2001]
            set velocityTable = TableArray[0x2000]
            set accelerationTable = TableArray[0x2000]
            set torqueTable = TableArray[0x2000]
        endmethod
        implement Init

    endstruct

    private module Init
        private static method onInit takes nothing returns nothing
            call init()
        endmethod
    endmodule

    private struct List extends array
        private static key prevK
        private static key nextK
        private static key dataK

        method operator data takes nothing returns UnitMotion
            return Table(dataK)[this]
        endmethod
        method operator next takes nothing returns thistype
            return Table(nextK)[this]
        endmethod

        method insert takes UnitMotion data returns thistype
            local thistype node = Table.create()
            local thistype last = Table(prevK)[this]
            set Table(prevK)[node] = last
            set Table(nextK)[node] = this
            set Table(prevK)[this] = node
            set Table(nextK)[last] = node
            set Table(dataK)[node] = data
            return node
        endmethod
        method remove takes nothing returns nothing
            local thistype prev = Table(prevK)[this]
            local thistype next = Table(nextK)[this]
            set Table(prevK)[next] = prev
            set Table(nextK)[prev] = next
            call Table(dataK).remove(this)
            call Table(this).destroy()
        endmethod

        method clear takes nothing returns nothing
            local thistype node = this.next
            loop
                exitwhen node == this
                call node.remove()
                set node = node.next
            endloop
        endmethod

        static method create takes nothing returns thistype
            local thistype this = Table.create()
            set Table(prevK)[this] = this
            set Table(nextK)[this] = this
            return this
        endmethod
        method destroy takes nothing returns nothing
            call this.clear()
            call Table(prevK).remove(this)
            call Table(nextK).remove(this)
            call Table(this).destroy()
        endmethod
    endstruct

    private module CommonMembers
        static method create takes nothing returns thistype
            local thistype this = vector.create(0.00, 0.00, 0.00)
            set this.references = List.create()
            return this
        endmethod
        method destroy takes nothing returns nothing
            call this.clearReferences()
            call this.references.destroy()
            call vector(this).destroy()
        endmethod
    endmodule

    struct Velocity extends array
        private List references

        method clearReferences takes nothing returns nothing
            local List node = this.references.next
            loop
                exitwhen node == this
                call node.data.removeVelocity(this)
                set node = node.next
            endloop
        endmethod

        implement CommonMembers

        method add takes vector vec returns nothing
            local List node = this.references.next
            if vec < 0 then
                loop
                    exitwhen node == this.references
                    call s__UnitMotion_velocity[node.data].subtract(-vec)
                    set node = node.next
                endloop
                call vector(this).subtract(-vec)
            else
                loop
                    exitwhen node == this.references
                    call s__UnitMotion_velocity[node.data].add(vec)
                    set node = node.next
                endloop
                call vector(this).add(vec)
            endif
        endmethod
    endstruct

    struct Acceleration extends array
        private List references

        method clearReferences takes nothing returns nothing
            local List node = this.references.next
            loop
                exitwhen node == this
                call node.data.removeAcceleration(this)
                set node = node.next
            endloop
        endmethod

        implement CommonMembers

        method add takes vector vec returns nothing
            local List node = this.references.next
            call vec.scale(UnitMotion.FRAME_RATE)
            if vec < 0 then
                loop
                    exitwhen node == this.references
                    call s__UnitMotion_acceleration[node.data].subtract(-vec)
                    set node.data.coefficientOfFriction = node.data.coefficientOfFriction
                    set node = node.next
                endloop
                call vector(this).subtract(-vec)
            else
                loop
                    exitwhen node == this.references
                    call s__UnitMotion_acceleration[node.data].add(vec)
                    set node.data.coefficientOfFriction = node.data.coefficientOfFriction
                    set node = node.next
                endloop
                call vector(this).add(vec)
            endif
            call vec.scale(UnitMotion.FPS)
        endmethod
    endstruct

    struct Torque extends array
        private List references

        method clearReferences takes nothing returns nothing
            local List node = this.references.next
            loop
                exitwhen node == this
                call node.data.removeTorque(this)
                set node = node.next
            endloop
        endmethod

        implement CommonMembers

        method add takes vector axis returns nothing
            local List node = this.references.next
            call axis.scale(UnitMotion.FRAME_RATE)
            if axis < 0 then
                loop
                    exitwhen node == this.references
                    call s__UnitMotion_torqueAxis[node.data].subtract(-axis)
                    set node = node.next
                endloop
                call vector(this).subtract(-axis)
            else
                loop
                    exitwhen node == this.references
                    call s__UnitMotion_torqueAxis[node.data].add(axis)
                    set node = node.next
                endloop
                call vector(this).add(axis)
            endif
            call axis.scale(UnitMotion.FPS)
        endmethod
    endstruct


endlibrary
library UnitMotionSentinel initializer Init /*


    */
requires /*

    */
UnitMotion   /*
    */
UnitDex      /*
    */
WorldBounds  /*

    */

    private function BoundUnit takes nothing returns nothing
        local unit u = GetUnitById(UnitMotion.triggerInstance)
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        if x < WorldBounds.minX then
            call SetUnitX(u, WorldBounds.minX)
        elseif x > WorldBounds.maxX then
            call SetUnitX(u, WorldBounds.maxX)
        endif
        if y < WorldBounds.minY then
            call SetUnitY(u, WorldBounds.minY)
        elseif y > WorldBounds.maxY then
            call SetUnitY(u, WorldBounds.maxY)
        endif
        set u = null
    endfunction

    private function OnAdd takes nothing returns nothing
        local code c = function BoundUnit
        static if UnitMotion.AUTO_REGISTER_UNITS then
            call UnitMotion[GetIndexedUnit()].registerMotionHandler(Filter(c))
        else
            call UnitMotion.addedInstance.registerMotionHandler(Filter(c))
        endif
    endfunction

    private function Init takes nothing returns nothing
        local code onAdd = function OnAdd
        static if UnitMotion.AUTO_REGISTER_UNITS then
            call OnUnitIndex(onAdd)
        else
            call UnitMotion.registerUnitAddHandler(Filter(onAdd))
        endif
    endfunction


endlibrary
library StunSystem uses Table

//********************************************************************************
//              Stun - Version 1.2.0.0 - By iAyanami aka Ayanami
//********************************************************************************
//
//    Stun:
//         - An easy to use system that stuns units
//         - Able to keep track of the remaining stun duration
//
//    Requirements:
//         - JASS NewGen
//         - Table
//
//    Functions:
//         - Stun.apply takes unit whichUnit, real duration, boolean stack returns nothing
//           * whichUnit is the target to be stunned
//           * duration is the duration of the stun
//           * stack is to determine if the stun should be a stacking one or not
//           * true - the stun will stack, the duration of the stun will add up with the previous duration
//           * false - the stun will not stack, the unit will be stunned for the longer stun duration
//
//         - Stun.getDuration takes unit whichUnit returns real
//           * whichUnit is the target to check
//           * returns the remaining stun duration
//
//         - Stun.stop takes unit whichUnit returns nothing
//           * removes stun from the target
//
//    How to import:
//         - Copy the whole "Stun" Trigger Folder into your map
//         - Save the map. Close and re-open the map.
//         - You should have a new unit (Stun Dummy), ability (Stun (System)) and buff (Stun (System)) created
//         - Read through the Configuration part of the code
//
//    Credits:
//         - Bribe for Table
//
//********************************************************************************
//                                CONFIGURABLES
//********************************************************************************

globals
    // timer period. lower the value, the more accurate but might cause decrease in
    // performance
    private constant real PERIOD = 0.03125

    // raw code of ability "Stun (System)"
    private constant integer ABILID = 'ASTN'
   
    // raw code of buff "Stun (System)"
    private constant integer BUFFID = 'BSTN'

    // raw code of unit "Stun Dummy"
    private constant integer STUNID = 'sTUN'
endglobals

//********************************************************************************
//                                     CODE
//********************************************************************************

// initialization
module Init
    private static method onInit takes nothing returns nothing
        set table = Table.create()
        set caster = CreateUnit(Player(13), STUNID, 0, 0, 0)

        call UnitAddAbility(caster, ABILID)
    endmethod
endmodule

struct Stun extends array
    private unit u
    private real dur

    private thistype next
    private thistype prev
   
    private static Table table
    private static timer t = CreateTimer()
    private static unit caster
    private static integer count = 0

    // remove the stun and deallocate
    private method destroy takes nothing returns nothing
        call UnitRemoveAbility(this.u, BUFFID)
       
        if this.next != 0 then
            set this.next.prev = this.prev
        endif

        set this.prev.next = this.next
        set this.dur = 0
        set this.prev = thistype(0).prev
        set thistype(0).prev = this
       
        if thistype(0).next == 0 then
            call PauseTimer(t)
        endif

        call table.remove(GetHandleId(this.u))
    endmethod
   
    // iterating through all instances every PERIOD
    private static method iterate takes nothing returns nothing
        local thistype this = thistype(0)
       
        loop
            set this = this.next
            exitwhen this == 0
            if this.dur <= 0 or IsUnitType(this.u, UNIT_TYPE_DEAD) or GetUnitTypeId(this.u) == 0 then
                call this.destroy()
            else
                set this.dur = this.dur - PERIOD
            endif
        endloop
    endmethod

    // immediately removes stun for the specified unit
    // ex: call Stun.stop(whichTarget)
    static method stop takes unit u returns nothing
        local integer id = GetHandleId(u)

        if table.has(id) then
            call thistype(table[id]).destroy()
        endif
    endmethod
   
    // gets the duration left for stun, not stunned units always return 0
    // ex: local real r = Stun.getDuration(whichTarget)
    static method getDuration takes unit u returns real
        return thistype(table[GetHandleId(u)]).dur
    endmethod
   
    // stunning specified target and to see if the stun is a stacking one or not
    // ex: call Stun.apply(whichTarget, 5.0, false)
    static method apply takes unit u, real dur, boolean b returns nothing
        local thistype this
        local integer id = GetHandleId(u)
       
        if table.has(id) then
            set this = table[id]
        else    
            if thistype(0).prev == 0 then
                set count = count + 1
                set this = count
            else
                set this = thistype(0).prev
                set thistype(0).prev = this.prev
            endif
           
            if thistype(0).next == 0 then
                call TimerStart(t, PERIOD, true, function thistype.iterate)
            else
                set thistype(0).next.prev = this
            endif
           
            set this.next = thistype(0).next
            set thistype(0).next = this
            set this.prev = thistype(0)
            set table[id] = this
            set this.u = u
            set this.dur = 0
           
            call IssueTargetOrder(caster, "firebolt", this.u)
        endif
       
        if b and dur > 0 then
            set this.dur = this.dur + dur
        elseif this.dur < dur then
            set this.dur = dur
        endif
    endmethod

    implement Init
endstruct

endlibrary
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
//     RegisterSpellEffectEvent(integer abil, code onCast)
//      
//
// Requires
// --------
//     RegisterPlayerUnitEvent: www.hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
//     Table: hiveworkshop.com/forums/showthread.php?t=188084
//
/*
=============   Why this is important? =================

1. Does not generate 16 events per spell.

2. This uses one trigger evaluation instead of one for each
   individual spell (some maps have quite a few). This helps keep
   framerate high and fluid when a spell is cast.
*/

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
library DummyRecycler /*
 
//                      DummyRecycler v1.24
//                          by Flux
//
//  A system that recycles dummy units while considering their facing angle.
//  It can be used as attachment units or as dummy caster.
//
//  Why is recycling a unit important?
//      Because creating a unit is is one of the slowest function in the game
//      and it will always leave a permanent tiny bit of memory (0.04 KB).
//      Furthermore, if a Damage Detection System (DDS) or a Unit Indexer is
//      imported to your map, using this system will even result to better
//      performance because DDS and Unit Indexer process new created units.
//
//
    */
requires /*
       nothing
     
    */
optional Table/*
        if not found, this system will use a hashtable. Hashtables are limited to
        255 per map.

    */
optional WorldBounds /*
        if not found, this system will initialize its own Map Boundaries.
//
//
//  Features:
//
//    -- Dummy Sharing
//        When a Dummy List gets low on unit count, it will borrow Dummy Units
//        from the Dummy List with the highest unit count. The transfer is not
//        instant because the shared Dummy Unit has to turn to the appropriate
//        angle of its new Dummy List before it can be recycled.
//        See BORROW_REQUEST.
//
//    -- Self-balancing recycling algorithm
//        Recycled Dummy Units will be thrown to the List having the least number
//        of Dummy Units.
//
//    -- Recycling least used
//        Allows recycling a Dummy from the Dummy List with the highest
//        unit count. It is useful when the facing angle of the Dummy Unit
//        does not matter.
//        See GetRecycledDummyAnyAngle.
//
//    -- Self-adaptation
//        When there are no free Dummy Units from a Dummy List, it will end up creating
//        a new unit instead but that unit will be permanently added as a Dummy
//        Unit to be recycled increasing the overall total Dummy Unit count.
//
//    -- Count control
//        Allows limiting the overall number of Dummy Units.
//        See MAX_DUMMY_COUNT.
//
//    -- Delayed Recycle
//        Allows recycling Dummy Units after some delay to allocate time for the
//        death animation of Special Effects to be seen.
//        See DummyAddRecycleTimer.
//
// ******************************************************************
// ***************************** API: *******************************
// ******************************************************************
//
//  function GetRecycledDummy takes real x, real y, real z, real facing returns unit
//      - Retrieve an unused Dummy Unit from the List.
//      - The equivalent of CreateUnit.
//      - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
//  function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
//      - Use this function if the facing angle of the Dummy doesn't matter to you.
//      - It will return a unit from the list having the highest number of unused Dummy Units.
//      - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
//  function RecycleDummy takes unit u returns nothing
//      - Recycle the Dummy unit for it to be used again later.
//      - The equivalent of RemoveUnit.
//
//  function DummyAddRecycleTimer takes unit u, real time returns nothing
//      - Recycle the Dummy unit after a certain time.
//      - Use this to allocate time for the the death animation of an effect attached to the
//        Dummy Unit to finish..
//      - The equivalent of UnitApplyTimedLife.
//
//--------------------
//      CREDITS
//--------------------
//  Bribe - for the MissileRecycler (vJASS) where I got this concept from
//       http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
//        - for the optional Table
//       http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
//  Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
//       http://www.wc3c.net/showthread.php?t=101150
//  Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
//       http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
//  Nestharus - for the data structure
//       http://www.hiveworkshop.com/forums/2809461-post7.html
//            - for the optional WorldBounds
//       http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j

// =============================================================== //
// ====================== CONFIGURATION ========================== //
// =============================================================== */


    globals
        //The rawcode of the Dummy Unit
        private constant integer DUMMY_ID = 'sbh3'

        //The owner of the Dummy Unit
        private constant player OWNER = Player(15)
     
        //The number of indexed angle. The higher the value the:
        // - Lesser the turning time for the Dummy Units.
        // - Higher the total number of Dummy Units created at Map Initialization.
        //          Recommended Value: 10 (Max difference of 18 degrees)
        private constant integer ANGLES_COUNT = 10
     
        //The number of Dummy units per ANGLES_COUNT. The higher the value the:
        // - Higher the number of units that can be recycled per angle, when
        //   no more units are in queue, the system will resort to use CreateUnit.
        // - Higher the total number of Dummy Units created at Map Initialization.
        //    Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
        private constant integer STORED_UNIT_COUNT = 3
     
        //The maximum number of Dummy units that can exist. When the system resort
        //to using CreateUnit, the unit will be permanently added to the Dummy
        //List. To avoid spamming Dummy Units and having too much free Dummy
        //Units to allocate, the maximum number of Dummy Units is capped.
        //               Recommended Value: 80 to 120
        private constant integer MAX_DUMMY_COUNT = 100
     
        //When a certain angle have less than BORROW_REQUEST units in its list,
        //it will start to borrow Dummy Units from the list with the highest
        //Dummy Unit count.
        //      Recommended Value: Half of maximum STORED_UNIT_COUNT
        private constant integer BORROW_REQUEST = 5
     
        //It will only return a Dummy if the current dummy is close
        //to it's appropriate facing angle. This is to avoid returning
        //a Dummy which is still turning to face it's list angle.
        private constant real ANGLE_TOLERANCE = 10.0
     
        //An additional option to automatically hide recycled dummy units in the
        //corner of the map camera bounds
        private constant boolean HIDE_ON_MAP_CORNER = true
    endglobals
 
    //Every time a new dummy unit is retrieved, it will apply this resets
    //If it is redundant/you dont need it, remove it.
    //! textmacro DUMMY_UNIT_RESET
        call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
        call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
        call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
    //! endtextmacro
// =============================================================== //
// ==================== END CONFIGURATION ======================== //
// =============================================================== //
 
 
    globals
        private integer dummyCount = ANGLES_COUNT*STORED_UNIT_COUNT
        private real array angle
        private integer array count
        private integer array countHead
        private integer array countNext
        private integer array countPrev
        private integer array next
        private integer array prev
        private unit array dummy
        private integer upper
        private integer lower
        private integer lastInstance
        private constant real FACING_OFFSET = 180.0/ANGLES_COUNT
    endglobals
 
    static if HIDE_ON_MAP_CORNER and not LIBRARY_WorldBounds then
        private module BoundsInit
     
            readonly static real x
            readonly static real y
         
            private static method onInit takes nothing returns nothing
                local rect map = GetWorldBounds()
                set thistype.x = GetRectMaxX(map)
                set thistype.y = GetRectMaxY(map)
                call RemoveRect(map)
                set map = null
            endmethod
         
        endmodule
     
        private struct Bounds extends array
            implement BoundsInit
        endstruct
    endif
 
    private module M
     
        static if LIBRARY_Table then
            static Table tb
        else
            static hashtable hash = InitHashtable()
        endif
     
        private static method onInit takes nothing returns nothing
            local real add = 360.0/ANGLES_COUNT
            local real a = 0
            local integer this = ANGLES_COUNT
            local integer head = 0
            local integer cHead = JASS_MAX_ARRAY_SIZE - 1   //avoid allocation collision
            local integer i = R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
            set upper = STORED_UNIT_COUNT
            set lower = STORED_UNIT_COUNT
            static if LIBRARY_Table then
                set tb = Table.create()
            endif
            //Initialize countHeads
            loop
                exitwhen i < 0
                set countNext[cHead] = cHead
                set countPrev[cHead] = cHead
                set countHead[i] = cHead
                set cHead = cHead - 1
                set i = i - 1
            endloop
            set cHead = countHead[STORED_UNIT_COUNT]  //All heads will be inserted here initially
            //Create the Dummy units
            loop
                exitwhen a >= 360
                //Initialize head
                set next[head] = head
                set prev[head] = head
                set count[head] = STORED_UNIT_COUNT
                set angle[head] = a
                //Insert head in the Count List
                set countNext[head] = cHead
                set countPrev[head] = countPrev[cHead]
                set countNext[countPrev[head]] = head
                set countPrev[countNext[head]] = head
                set i = 0
                loop
                    exitwhen i >= STORED_UNIT_COUNT
                    //Queued Linked List
                    set next[this] = head
                    set prev[this] = prev[head]
                    set next[prev[this]] = this
                    set prev[next[this]] = this
                    static if HIDE_ON_MAP_CORNER then
                        static if LIBRARY_WorldBounds then
                            set dummy[this] = CreateUnit(OWNER, DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, a)
                        else
                            set dummy[this] = CreateUnit(OWNER, DUMMY_ID, Bounds.x, Bounds.y, a)
                        endif
                    else
                        set dummy[this] = CreateUnit(OWNER, DUMMY_ID, 0, 0, a)
                    endif
                    call PauseUnit(dummy[this], true)
                    static if LIBRARY_Table then
                        set tb[GetHandleId(dummy[this])] = this
                    else
                        call SaveInteger(hash, GetHandleId(dummy[this]), 0, this)
                    endif
                    set this = this + 1
                    set i = i + 1
                endloop
                set head = head + 1
                set a = a + add
            endloop
            set lastInstance = this
        endmethod
     
    endmodule
 
    private struct S extends array
        implement M
    endstruct
 
    private function GetHead takes integer facing returns integer
        if facing < 0 or facing >= 360 then
            set facing = facing - (facing/360)*360
            if facing < 0 then
                set facing = facing + 360
            endif
        endif
        return R2I((facing*ANGLES_COUNT/360.0))
    endfunction
 
    function GetRecycledDummy takes real x, real y, real z, real facing returns unit
        local integer head = GetHead(R2I(facing + FACING_OFFSET))
        local integer this = next[head]
        local integer cHead
     
        //If there are Dummy Units in the Queue List already facing close to the appropriate angle
        if this != head and RAbsBJ(GetUnitFacing(dummy[this]) - angle[head]) <= ANGLE_TOLERANCE then
            //Remove from the Queue List
            set next[prev[this]] = next[this]
            set prev[next[this]] = prev[this]
            //For double free protection
            set next[this] = -1
            //Unit Properties
            set bj_lastCreatedUnit = dummy[this]
            call SetUnitX(bj_lastCreatedUnit, x)
            call SetUnitY(bj_lastCreatedUnit, y)
            call SetUnitFacing(bj_lastCreatedUnit, facing)
            call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
            //! runtextmacro DUMMY_UNIT_RESET()
            //Update Count and Bounds
            set count[head] = count[head] - 1
         
            //------------------------------------------------
            //                 Unit Sharing
            //------------------------------------------------
            if count[head] < BORROW_REQUEST and count[countNext[countHead[upper]]] > count[head] then
                set count[head] = count[head] + 1
                set this = next[countNext[countHead[upper]]]
                call SetUnitFacing(dummy[this], angle[head])
                //Remove
                set next[prev[this]] = next[this]
                set prev[next[this]] = prev[this]
                //Add to the Current List
                set next[this] = head
                set prev[this] = prev[head]
                set next[prev[this]] = this
                set prev[next[this]] = this
                set head = countNext[countHead[upper]]
                set count[head] = count[head] - 1
            endif
         
            //---------------------------
            //Update Count Lists
            //---------------------------
            //Remove from the current Count List
            set countNext[countPrev[head]] = countNext[head]
            set countPrev[countNext[head]] = countPrev[head]
            //Add to the new Count List
            set cHead = countHead[count[head]]
            set countNext[head] = cHead
            set countPrev[head] = countPrev[cHead]
            set countNext[countPrev[head]] = head
            set countPrev[countNext[head]] = head
         
            //---------------------------
            //  Update Bounds
            //---------------------------
            set cHead = countHead[upper]
            if countNext[cHead] == cHead then
                set upper = upper - 1
            endif
            if count[head] < lower then
                set lower = count[head]
            endif
        else
            set bj_lastCreatedUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
            call PauseUnit(bj_lastCreatedUnit, true)
            call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
            if dummyCount < MAX_DUMMY_COUNT then
                set this = lastInstance
                //For double free protection
                set next[this] = -1
                set dummy[this] = bj_lastCreatedUnit
                static if LIBRARY_Table then
                    set S.tb[GetHandleId(bj_lastCreatedUnit)] = this
                else
                    call SaveInteger(S.hash, GetHandleId(bj_lastCreatedUnit), 0, this)
                endif
                set lastInstance = lastInstance + 1
            endif
            set dummyCount = dummyCount + 1
        endif

        return bj_lastCreatedUnit
    endfunction
 
    function RecycleDummy takes unit u returns nothing
        static if LIBRARY_Table then
            local integer this = S.tb[GetHandleId(u)]
        else
            local integer this = LoadInteger(S.hash, GetHandleId(u), 0)
        endif
        local integer head
        local integer cHead
     
        //If the unit is a legit Dummy Unit
        if this > 0 and next[this] == -1 then
            //Find where to insert based on the list having the least number of units
            set head = countNext[countHead[lower]]
            set next[this] = head
            set prev[this] = prev[head]
            set next[prev[this]] = this
            set prev[next[this]] = this
            //Update Status
            call SetUnitFacing(u, angle[head])
            call PauseUnit(u, true)
            call SetUnitOwner(u, OWNER, false)
            static if HIDE_ON_MAP_CORNER then
                static if LIBRARY_WorldBounds then
                    call SetUnitX(u, WorldBounds.maxX)
                    call SetUnitY(u, WorldBounds.maxY)
                else
                    call SetUnitX(u, Bounds.x)
                    call SetUnitY(u, Bounds.y)
                endif
            else
                call SetUnitScale(u, 0, 0, 0)
                call SetUnitVertexColor(u, 0, 0, 0, 0)
            endif
            set count[head] = count[head] + 1
         
            //---------------------------
            //    Update Count Lists
            //---------------------------
            //Remove
            set countNext[countPrev[head]] = countNext[head]
            set countPrev[countNext[head]] = countPrev[head]
            //Add to the new Count List
            set cHead = countHead[count[head]]
            set countNext[head] = cHead
            set countPrev[head] = countPrev[cHead]
            set countNext[countPrev[head]] = head
            set countPrev[countNext[head]] = head
         
            //---------------------------
            //  Update Bounds
            //---------------------------
            set cHead = countHead[lower]
            if countNext[cHead] == cHead then
                set lower = lower + 1
            endif
            if count[head] > upper then
                set upper = count[head]
            endif
        elseif this == 0 then
            call RemoveUnit(u)
        debug elseif next[this] != -1 then
            debug call BJDebugMsg("|cffffcc00[DummyRecycler]:|r Attempted to recycle a pending/free Dummy Unit.")
        endif
     
    endfunction
 
    private function Expires takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = GetHandleId(t)
        static if LIBRARY_Table then
            call RecycleDummy(S.tb.unit[id])
            call S.tb.unit.remove(id)
        else
            call RecycleDummy(LoadUnitHandle(S.hash, id, 0))
            call FlushChildHashtable(S.hash, id)
        endif
        call DestroyTimer(t)
        set t = null
    endfunction

    function DummyAddRecycleTimer takes unit u, real time returns nothing
        local timer t = CreateTimer()
        static if LIBRARY_Table then
            set S.tb.unit[GetHandleId(t)] = u
        else
            call SaveUnitHandle(S.hash, GetHandleId(t), 0, u)
        endif
        call TimerStart(t, time, false, function Expires)
        set t = null
    endfunction
 
    function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
        return GetRecycledDummy(x, y, z, angle[countNext[countHead[upper]]])
    endfunction
 
    // runtextmacro DUMMY_DEBUG_TOOLS()
 
endlibrary
library ResourcePreloader /* v1.4c


    */
uses /*

    */
optional BJObjectId   /*   https://www.hiveworkshop.com/threads/bjobjectid.287128/
    */
optional Table        /*   https://www.hiveworkshop.com/threads/snippet-new-table.188084/
    */
optional UnitRecycler /*   https://www.hiveworkshop.com/threads/snippet-unit-recycler.286701/


    */
//! novjass

    |================|
    | Written by AGD |
    |================|

        [CREDITS]
    /*      IcemanBo - for suggesting further improvements
            Silvenon - for the sound preloading method                            */



        |-----|
        | API |
        |-----|

            function PreloadUnit takes integer rawcode returns nothing/*
                - Assigns a certain type of unit to be preloaded

          */
function PreloadItem takes integer rawcode returns nothing/*
                - Assigns a certain type of item to be preloaded

          */
function PreloadAbility takes integer rawcode returns nothing/*
                - Assigns a certain type of ability to be preloaded

          */
function PreloadEffect takes string modelPath returns nothing/*
                - Assigns a certain type of effect to be preloaded

          */
function PreloadSound takes string soundPath returns nothing/*
                - Assigns a certain type of sound to be preloaded


          The following functions requires the BJObjectId library:

          */
function PreloadUnitEx takes integer start, integer end returns nothing/*
                - Assigns a range of units to be preloaded

          */
function PreloadItemEx takes integer start, integer end returns nothing/*
                - Assigns a range of items to be preloaded

          */
function PreloadAbilityEx takes integer start, integer end returns nothing/*
                - Assigns a range of abilities to be preloaded


    */
//! endnovjass

    /*========================================================================================================*/
    /*            Do not try to change below this line if you're not so sure on what you're doing.            */
    /*========================================================================================================*/

    private keyword S

    /*============================================== TextMacros ==============================================*/

    //! textmacro PRELOAD_TYPE takes NAME, ARG, TYPE, INDEX, I
    function Preload$NAME$ takes $ARG$ what returns nothing
        static if LIBRARY_Table then
            if S.tb[$I$].boolean[$INDEX$] then
                return
            endif
            set S.tb[$I$].boolean[$INDEX$] = true
            call Do$NAME$Preload(what)
        else
            if LoadBoolean(S.tb, $I$, $INDEX$) then
                return
            endif
            call SaveBoolean(S.tb, $I$, $INDEX$, true)
            call Do$NAME$Preload(what)
        endif
    endfunction
    //! endtextmacro

    //! textmacro RANGED_PRELOAD_TYPE takes NAME
    function Preload$NAME$Ex takes integer start, integer end returns nothing
        local boolean mode = start < end
        loop
            call Preload$NAME$(start)
            exitwhen start == end
            if mode then
                set start = BJObjectId(start).plus_1()
                exitwhen start > end
            else
                set start = BJObjectId(start).minus_1()
                exitwhen start < end
            endif
        endloop
    endfunction
    //! endtextmacro

    /*========================================================================================================*/

    private function DoUnitPreload takes integer id returns nothing
        static if LIBRARY_UnitRecycler then
            call RecycleUnitEx(CreateUnit(Player(15), id, 0, 0, 270))
        else
            call RemoveUnit(CreateUnit(Player(15), id, 0, 0, 0))
        endif
    endfunction

    private function DoItemPreload takes integer id returns nothing
        call RemoveItem(UnitAddItemById(S.dummy, id))
    endfunction

    private function DoAbilityPreload takes integer id returns boolean
        return UnitAddAbility(S.dummy, id) and UnitRemoveAbility(S.dummy, id)
    endfunction

    private function DoEffectPreload takes string path returns nothing
        call DestroyEffect(AddSpecialEffectTarget(path, S.dummy, "origin"))
    endfunction

    private function DoSoundPreload takes string path returns nothing
        local sound s = CreateSound(path, false, false, false, 10, 10, "")
        call SetSoundVolume(s, 0)
        call StartSound(s)
        call KillSoundWhenDone(s)
        set s = null
    endfunction

    //! runtextmacro PRELOAD_TYPE("Unit", "integer", "unit", "what", "0")
    //! runtextmacro PRELOAD_TYPE("Item", "integer", "item", "what", "1")
    //! runtextmacro PRELOAD_TYPE("Ability", "integer", "ability", "what", "2")
    //! runtextmacro PRELOAD_TYPE("Effect", "string", "effect", "StringHash(what)", "3")
    //! runtextmacro PRELOAD_TYPE("Sound", "string", "sound", "StringHash(what)", "4")

    static if LIBRARY_BJObjectId then
    //! runtextmacro RANGED_PRELOAD_TYPE("Unit")
    //! runtextmacro RANGED_PRELOAD_TYPE("Item")
    //! runtextmacro RANGED_PRELOAD_TYPE("Ability")
    endif

    /*========================================================================================================*/

    private module Init
        private static method onInit takes nothing returns nothing
            local rect world = GetWorldBounds()
            static if LIBRARY_Table then
                set tb = TableArray[5]
            endif
            set dummy = CreateUnit(Player(15), 'hpea', 0, 0, 0)
            call UnitAddAbility(dummy, 'AInv')
            call UnitAddAbility(dummy, 'Avul')
            call UnitRemoveAbility(dummy, 'Amov')
            call SetUnitY(dummy, GetRectMaxY(world) + 1000)
            call RemoveRect(world)
            set world = null
        endmethod
    endmodule

    private struct S extends array
        static if LIBRARY_Table then
            static TableArray tb
        else
            static hashtable tb = InitHashtable()
        endif
        static unit dummy
        implement Init
    endstruct


endlibrary
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
*   v1.2.1, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   UnitDex assigns every unit an unique integer. It attempts to make that number within the
*   maximum array limit so you can associate it with one.
*   _________________________________________________________________________
*   1. Installation
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Copy the script to your map, save it, then restart the editor and comment out the line below.
*/

    // external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/*  ________________________________________________________________________
*   2. Configuration
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
 
    private module UnitDexConfig

        // The raw code of the leave detection ability.
        static constant integer DETECT_LEAVE_ABILITY = 'uDex'
       
        // Allow debug messages (debug mode must also be on)
        static constant boolean ALLOW_DEBUGGING      = true
       
        // Uncomment the lines below to define a filter for units being indexed
       
        /*static method onFilter takes unit u returns boolean
            return true
        endmethod*/

       
    endmodule
/*  _________________________________________________________________________
*   3. Function API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Every function inlines except for UnitDexRemove
*
*       function GetUnitId takes unit whichUnit returns integer
*       function GetUnitById takes integer index returns unit
*    
*       function UnitDexEnable takes boolean flag returns nothing
*       function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
*       function IsUnitIndexed takes unit u returns boolean
*       function IsIndexingEnabled takes nothing returns boolean
*
*       function GetIndexedUnit takes nothing returns unit
*       function GetIndexedUnitId takes nothing returns integer
*
*       function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
*       function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
*       function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
*       function OnUnitIndex takes code func returns triggercondition
*       function OnUnitDeidex takes code func returns triggercondition
*   _________________________________________________________________________
*   4. Struct API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       UnitDex.Enabled = false // toggle the indexer
*       UnitDex.Initialized     // returns true if the preload timer has finished
*       UnitDex.Count           // returns the amount of units indexed
*       UnitDex.Unit[0]         // access the UnitDex array directly
*       UnitDex.Group           // a unit group containing every indexed unit (for enumeration)
*       UnitDex.LastIndex       // returns the last indexed unit's id
*   _________________________________________________________________________
*   5. Public Variables
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       These are to be used with the "eventtype" argument of the event API:
*
*           constant integer EVENT_UNIT_INDEX     = 0
*           constant integer EVENT_UNIT_DEINDEX   = 1
*   _________________________________________________________________________
*   6. Examples
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Associate a unit with a variable
*
*           set UnitFlag[GetUnitId(yourUnit)] = true
*
*       2. Allocate a struct instance using a units assigned ID
*
*           local somestruct data = GetUnitId(yourUnit)
*  
*       3. Detect when a unit leaves the map
*
*           function Exit takes nothing returns nothing
*               call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
*           endfunction
*
*           call OnUnitDeindex(function Exit)
*           // or
*           call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
*   _________________________________________________________________________
*   7. How it works
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Enumerate all preplaced units
*       2. TriggerRegisterEnterRegion native to detect when a unit enters the map
*       3. Assign each unit that enters the map a unique integer
*       4. Give every unit an ability based off of defend. There is no native to accurately
*          detect when a unit leaves the map but when a unit dies or is removed from the game
*          they are issued the "undefend" order
*       5. Catch the "undefend" order and remove unit from the queue
*   _________________________________________________________________________
*   8. Notes
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
*       - The object merger line should be commented out after saving and restarting.
*       - All public functions are inlined except UnitDexRemove.
*
*   -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/

   
    globals
        // Event types
        constant integer EVENT_UNIT_INDEX     = 0
        constant integer EVENT_UNIT_DEINDEX   = 1
       
        // System variables
        private trigger array IndexTrig
        private integer Index = 0
        private real E=-1
        private boolexpr FilterEnter
    endglobals
   
    function GetUnitId takes unit whichUnit returns integer
        return GetUnitUserData(whichUnit)
    endfunction
   
    function GetUnitById takes integer index returns unit
        return UnitDex.Unit[index]
    endfunction
   
    function GetIndexedUnit takes nothing returns unit
        return UnitDex.Unit[UnitDex.LastIndex]
    endfunction
   
    function GetIndexedUnitId takes nothing returns integer
        return UnitDex.LastIndex
    endfunction
   
    function IsUnitIndexed takes unit u returns boolean
        return (GetUnitById(GetUnitId(u)) != null)
    endfunction
   
    function UnitDexEnable takes boolean flag returns nothing
        set UnitDex.Enabled = flag
    endfunction
   
    function IsIndexingEnabled takes nothing returns boolean
        return UnitDex.Enabled
    endfunction
   
    function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
        return TriggerAddCondition(IndexTrig[eventtype], func)
    endfunction
   
    function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
        call TriggerRemoveCondition(IndexTrig[eventtype], c)
    endfunction
   
    function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
        call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
    endfunction
   
    function OnUnitIndex takes code func returns triggercondition
        return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
    endfunction

    function OnUnitDeindex takes code func returns triggercondition
        return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
    endfunction
   
    function UnitDexRemove takes unit u, boolean runEvents returns boolean
        return UnitDex.Remove(u, runEvents)
    endfunction
   
    /****************************************************************/
   
    private keyword UnitDexCore
   
    struct UnitDex extends array
        static boolean Enabled = true
       
        readonly static integer LastIndex
        readonly static boolean Initialized=false
        readonly static group Group=CreateGroup()
        readonly static unit array Unit
        readonly static integer Count = 0
        readonly static integer array List
       
        implement UnitDexConfig
       
        private static integer Counter = 0
       
        implement UnitDexCore
    endstruct
   
    /****************************************************************/
     
    private module UnitDexCore
   
        static method Remove takes unit u, boolean runEvents returns boolean
            local integer i
           
            if (IsUnitIndexed(u)) then
                set i = GetUnitId(u)
                set UnitDex.List[i] = Index
                set Index = i
               
                call GroupRemoveUnit(UnitDex.Group, u)
                call SetUnitUserData(u, 0)
           
                if (runEvents) then
                    set UnitDex.LastIndex = i
                    set E = EVENT_UNIT_DEINDEX
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
                    set E = -1
                endif
               
                set UnitDex.Unit[i] = null
                set UnitDex.Count = UnitDex.Count - 1
               
                return true
            endif
           
            return false
        endmethod
       
        private static method onGameStart takes nothing returns nothing
            local integer i = 0
            static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
                local group ENUM_GROUP = CreateGroup() // If not, create the group.
            endif
           
            // Index preplaced units
            loop
                call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
               
                set i = i + 1
               
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
           
            static if (not LIBRARY_GroupUtils) then
                call DestroyGroup(ENUM_GROUP)
                set ENUM_GROUP = null
            endif
           
            // run init triggers
            set i = 1
            loop
                exitwhen i > Counter
               
                set LastIndex = i
               
                call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
                set E = EVENT_UNIT_INDEX
                set E = -1
               
                set i = i + 1
            endloop

            set LastIndex   = Counter
            set Initialized = true
            set FilterEnter = null
           
            call DestroyTimer(GetExpiredTimer())
        endmethod
       
        private static method onEnter takes nothing returns boolean
            local unit    u = GetFilterUnit()
            local integer i = GetUnitId(u)
            local integer t = Index
           
            if (i == 0 and thistype.Enabled) then
               
                // If a filter was defined pass the unit through it.
                static if (thistype.onFilter.exists) then
                    if (not thistype.onFilter(u)) then
                        set u = null
                        return false // check failed
                    endif
                endif
               
                // Handle debugging
                static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
                    if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
                        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
                        set u = null
                        return false
                    endif
                endif
               
                // Add to group of indexed units
                call GroupAddUnit(thistype.Group, u)
               
                // Give unit the leave detection ability
                call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
                call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
               
                // Allocate index
                if (Index != 0) then
                    set Index = List[t]
                else
                    set Counter = Counter + 1
                    set t = Counter
                endif
               
                set List[t] = -1
                set LastIndex = t
                set thistype.Unit[t] = u
                set Count = Count + 1
               
                // Store the index
                call SetUnitUserData(u, t)
               
                if (thistype.Initialized) then
                    // Execute custom events registered with RegisterUnitIndexEvent
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
                   
                    // Handle TriggerRegisterUnitIndexEvent
                    set E = EVENT_UNIT_INDEX

                    // Reset so the event can occur again
                    set E = -1
                endif
            endif
           
            set u = null
           
            return false
        endmethod

        private static method onLeave takes nothing returns boolean
            local unit    u
            local integer i
           
            // Check if order is undefend.
            if (thistype.Enabled and GetIssuedOrderId() == 852056) then
               
                set u = GetTriggerUnit()
               
                // If unit was killed (not removed) then don't continue
                if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
                    set u = null
                    return false
                endif
               
                set i = GetUnitId(u)

                // If unit has been indexed then deindex it
                if (i > 0 and i <= Counter and u == GetUnitById(i)) then
                   
                    // Recycle the index
                    set List[i]   = Index
                    set Index     = i
                    set LastIndex = i
                   
                    // Remove to group of indexed units
                    call GroupRemoveUnit(thistype.Group, u)
               
                    // Execute custom events without any associated triggers
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
                   
                    // Handle TriggerRegisterUnitIndexEvent
                    set E = EVENT_UNIT_DEINDEX
                   
                    // Remove entry
                    call SetUnitUserData(u, 0)
                    set thistype.Unit[i] = null
                   
                    // Decrement unit count
                    set Count = Count - 1
               
                    // Reset so the event can occur again
                    set E = -1
                endif
               
                set u = null
            endif
           
            return false
        endmethod
       
        private static method onInit takes nothing returns nothing
            local trigger t         = CreateTrigger()
            local integer i         = 0
            local player p
            local unit u
           
            static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
                local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
                local rect world = GetWorldBounds()
            endif
           
            set FilterEnter = Filter(function thistype.onEnter)
           
            // Begin to index units when they enter the map
            static if (LIBRARY_WorldBounds) then
                call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
            else
                call RegionAddRect(reg, world)
                call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
                call RemoveRect(world)
                set world = null
            endif

            call TriggerAddCondition(t, Filter(function thistype.onLeave))

            set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
            set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
           
            loop
                set p = Player(i)
               
                // Detect "undefend"
                call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
               
                // Hide the detect ability from players
                call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
               
                set i = i + 1
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
           
            call TimerStart(CreateTimer(), 0, false, function thistype.onGameStart)
        endmethod
   
    endmodule
   
endlibrary
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//*     [group]    ENUM_GROUP      As you might expect, this group is good for
//*                                when you need a group just for enumeration.
//*     [boolexpr] BOOLEXPR_TRUE   This is a true boolexpr, which is important
//*                                because a 'null' boolexpr in enumeration
//*                                calls results in a leak. Use this instead.
//*     [boolexpr] BOOLEXPR_FALSE  This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//*     local group MyGroup = NewGroup()
//*     call GroupRefresh(MyGroup)
//*     call ReleaseGroup(MyGroup)
//*     call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//*     call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
    //If you don't have xebasic in your map, this value will be used instead.
    //This value corresponds to the max collision size of a unit in your map.
    private constant real    MAX_COLLISION_SIZE = 197.
    //If you are insane and don't care about any of the protection involved in
    //this library, but want this script to be really fast, set this to true.
    private constant boolean LESS_SAFETY        = false
endglobals

globals
    //* Constants that are available to the user
    group    ENUM_GROUP     = CreateGroup()
    boolexpr BOOLEXPR_TRUE  = null
    boolexpr BOOLEXPR_FALSE = null
endglobals

globals
    //* Hashtable for debug purposes
    private hashtable     ht     = InitHashtable()
    //* Temporary references for GroupRefresh
    private boolean       Flag   = false
    private group         Refr   = null
    //* Arrays and counter for the group stack
    private group   array Groups
    private integer       Count  = 0
    //* Variables for use with the GroupUnitsInArea function
    private real          X      = 0.
    private real          Y      = 0.
    private real          R      = 0.
    private hashtable     H      = InitHashtable()
endglobals

private function HookDestroyGroup takes group g returns nothing
    if g == ENUM_GROUP then
        call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
    endif
endfunction

debug hook DestroyGroup HookDestroyGroup

private function AddEx takes nothing returns nothing
    if Flag then
        call GroupClear(Refr)
        set Flag = false
    endif
    call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
    set Flag = true
    set Refr = g
    call ForGroup(Refr, function AddEx)
    if Flag then
        call GroupClear(g)
    endif
endfunction

function NewGroup takes nothing returns group
    if Count == 0 then
        set Groups[0] = CreateGroup()
    else
        set Count = Count - 1
    endif
    static if not LESS_SAFETY then
        call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
    endif
    return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
    local integer id = GetHandleId(g)
    static if LESS_SAFETY then
        if g == null then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
            return false
        elseif Count == 8191 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
            call DestroyGroup(g)
            return false
        endif
    else
        if g == null then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
            return false
        elseif not HaveSavedInteger(ht, 0, id) then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
            return false
        elseif LoadInteger(ht, 0, id) == 2 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
            return false
        elseif Count == 8191 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
            call DestroyGroup(g)
            return false
        endif
        call SaveInteger(ht, 0, id, 2)
    endif
    call GroupClear(g)
    set Groups[Count] = g
    set Count         = Count + 1
    return true
endfunction

private function Filter takes nothing returns boolean
    return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction

private function HookDestroyBoolExpr takes boolexpr b returns nothing
    local integer bid = GetHandleId(b)
    if HaveSavedHandle(H, 0, bid) then
        //Clear the saved boolexpr
        call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
        call RemoveSavedHandle(H, 0, bid)
    endif
endfunction

hook DestroyBoolExpr HookDestroyBoolExpr

private constant function GetRadius takes real radius returns real
    static if LIBRARY_xebasic then
        return radius+XE_MAX_COLLISION_SIZE
    else
        return radius+MAX_COLLISION_SIZE
    endif
endfunction

function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
    local real    prevX = X
    local real    prevY = Y
    local real    prevR = R
    local integer bid   = 0
   
    //Set variables to new values
    set X = x
    set Y = y
    set R = radius
    if filter == null then
        //Adjusts for null boolexprs passed to the function
        set filter = Condition(function Filter)
    else
        //Check for a saved boolexpr
        set bid = GetHandleId(filter)
        if HaveSavedHandle(H, 0, bid) then
            //Set the filter to use to the saved one
            set filter = LoadBooleanExprHandle(H, 0, bid)
        else
            //Create a new And() boolexpr for this filter
            set filter = And(Condition(function Filter), filter)
            call SaveBooleanExprHandle(H, 0, bid, filter)
        endif
    endif
    //Enumerate, if they want to use the boolexpr, this lets them
    call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
    //Give back original settings so nested enumerations work
    set X = prevX
    set Y = prevY
    set R = prevR
endfunction

function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
    local real prevX = X
    local real prevY = Y
    local real prevR = R

    //Set variables to new values
    set X = x
    set Y = y
    set R = radius
    //Enumerate
    call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
    //Give back original settings so nested enumerations work
    set X = prevX
    set Y = prevY
    set R = prevR
endfunction

private function True takes nothing returns boolean
    return true
endfunction
private function False takes nothing returns boolean
    return false
endfunction
private function Init takes nothing returns nothing
    set BOOLEXPR_TRUE  = Condition(function True)
    set BOOLEXPR_FALSE = Condition(function False)
endfunction
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
 
Requirements of the spell requirements
library ErrorMessage /* v1.0.2.0
*************************************************************************************
*
*   Issue Compliant Error Messages
*
************************************************************************************
*
*   function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
*       -   In the event of an error the game will be permanently paused
*
*   function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
*
************************************************************************************/

    private struct Fields extends array
        static constant string COLOR_RED = "|cffff0000"
        static constant string COLOR_YELLOW = "|cffffff00"
        static string lastError = null
    endstruct
   
    private function Pause takes nothing returns nothing
        call PauseGame(true)
    endfunction
   
    private function ThrowMessage takes string libraryName, string functionName, string objectName, integer objectInstance, string description, string errorType, string color returns nothing
        local string str
       
        local string color_braces = "|cff66FF99"
        local string orange = "|cffff6600"
       
        set str = "->\n-> " + color_braces + "{|r " + "Library" + color_braces + "(" + orange + libraryName + color_braces + ")"
        if (objectName != null) then
            if (objectInstance != 0) then
                set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + " (|rinstance = " + orange + I2S(objectInstance) + color_braces + ") )" + "|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
            else
                set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + ")|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
            endif
        else
            set str = str + "|r." + "Function" + color_braces + "(" + orange + functionName + color_braces + ")"
        endif
       
        set str = str + color_braces + " }|r " + "has thrown an exception of type " + color_braces + "(" + color + errorType + color_braces + ")|r."
       
        set Fields.lastError = str + "\n->\n" + "-> " + color + description + "|r\n->"
    endfunction
   
    function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
        if (Fields.lastError != null) then
            set objectInstance = 1/0
        endif
   
        if (expression) then
            call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Error", Fields.COLOR_RED)
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
            call TimerStart(CreateTimer(), 0, true, function Pause)
            set objectInstance = 1/0
        endif
    endfunction
    function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
        if (Fields.lastError != null) then
            set objectInstance = 1/0
        endif
   
        if (expression) then
            call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Warning", Fields.COLOR_YELLOW)
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
            set Fields.lastError = null
        endif
    endfunction
endlibrary
library Vector

//*****************************************************************
//*  VECTOR LIBRARY
//*
//*  written by: Anitarf
//*
//*  The library contains a struct named vector, which represents a
//*  point in 3D space. As such, it has three real members, one for
//*  each coordinate: x, y, z. It also has the following methods:
//*
//*        static method create takes real x, real y, real z returns vector
//*  Creates a new vector with the given coordinates.
//*
//*        method getLength takes nothing returns real
//*  Returns the length of the vector it is called on.
//*
//*        static method sum takes vector augend, vector addend returns vector
//*  Returns the sum of two vectors as a new vector.
//*
//*        method add takes vector addend returns nothing
//*  Similar to sum, except that it doesn't create a new vector for the result,
//*  but changes the vector it is called on by adding the "added" to it.
//*
//*        static method difference takes vector minuend, vector subtrahend returns vector
//*  Returns the difference between two vectors as a new vector.
//*
//*        method subtract takes vector subtrahend returns nothing
//*  Similar to difference, except that it doesn't create a new vector for the result,
//*  but changes the vector it is called on by subtracting the "subtrahend" from it.
//*
//*        method scale takes real factor returns nothing
//*  Scales the vector it is called on by the given factor.
//*
//*        method setLength takes real length returns nothing
//*  Sets the length of the vector it is called on to the given value, maintaining its orientation.
//*
//*        static method dotProduct takes vector a, vector b returns real
//*  Calculates the dot product (also called scalar product) of two vectors.
//*
//*        static method crossProduct takes vector a, vector b returns vector
//*  Calculates the cross product (also called vector product) of two vectors
//*  and returns it as a new vector.
//*
//*        static method tripleProductScalar takes vector a, vector b, vector c returns real
//*  Calculates the triple scalar product of three vectors.
//*
//*        static method tripleProductVector takes vector a, vector b, vector c returns vector
//*  Calculates the triple vector product of three vectors and returns it as a new vector.
//*
//*
//*        static method projectionVector takes vector projected, vector direction returns vector
//*  Calculates the projection of the vector "projected" onto the vector "direction"
//*  and returns it as a new vector.
//*  Returns null if the vector "direction" has a length of 0.
//*
//*        method projectVector takes vector direction returns nothing
//*  Projects the vector it is called on onto the vector "direction".
//*  Does nothing if the vector "direction" has a length of 0.
//*
//*        static method projectionPlane takes vector projected, vector normal returns vector
//*  Calculates the projection of the vector "projected" onto a plane defined by
//*  its normal vector and returns it as a new vector.
//*  Returns null if the vector "normal" has a length of 0.
//*
//*        method projectPlane takes vector normal returns nothing
//*  Projects the vector it is called on onto a plane defined by its normal vector.
//*  Does nothing if the vector "normal" has a length of 0.
//*
//*        static method getAngle takes vector a, vector b returns real
//*  Returns the angle between two vectors, in radians, returns a value between 0 and pi.
//*  Returns 0.0 if any of the vectors are 0 units long.
//*
//*        method rotate takes vector axis, real angle returns nothing
//*  Rotates the vector it is called on around the axis defined by the vector "axis"
//*  by the given angle, which should be input in radians.
//*  Does nothing if axis is 0 units long.
//*
//*
//*        static method createTerrainPoint takes real x, real y returns vector
//*  Creates a vector to the given terrain coordinate, taking its z height into account.
//*
//*        method getTerrainPoint takes real x, real y returns nothing
//*  Sets the vector it is called on to the given terrain coordinate, taking its z height into account.
//*
//*        static method createTerrainNormal takes real x, real y, real sampleRadius returns vector
//*  Creates the normal vector of the terrain at given coordinates. "sampleRadius" defines
//*  how far apart the reference points will be, if they are further apart, the result will
//*  be an impression of smoother terrain; normaly the value should be between 0 and 128.
//*
//*        method getTerrainNormal takes real x, real y, real sampleRadius returns nothing
//*  Sets the vector it is called on to the normal of the terrain at given coordinates.
//*
//*
//*        method isInCylinder takes vector cylinderOrigin, vector cylinderHeight, real cylinderRadius returns boolean
//*  Determines if a point is within a given cylinder. The cylinder's origin vector points
//*  to the center of one of the two paralel circular sides, and the height vector points
//*  from the origin point to the center of the other of the two paralel circular sides.
//*  Returns false if the point is not in the cylinder or if the vector cylinderHeight is 0 units long.
//*
//*        method isInCone takes vector coneOrigin, vector coneHeight, real coneRadius returns boolean
//*  Determines if a point is within a given cone. The cone's origin vector points to the
//*  center of the circular side, and the height vector points from the origin point to
//*  the tip of the cone.
//*  Returns false if the point is not in the cylinder or if the vector coneHeight is 0 units long.
//*
//*        method isInSphere takes vector sphereOrigin, real sphereRadius returns boolean
//*  Determines if a point is within a give sphere. The sphere's origin vector points to the
//*  center of the sphere.
//*  Returns false if the point is not in the sphere.
//****************************************************************

    struct vector
        real x
        real y
        real z
       
        static method create takes real x, real y, real z returns vector
            local vector v = vector.allocate()
            set v.x=x
            set v.y=y
            set v.z=z
            return v
        endmethod
       
        method getLength takes nothing returns real
          return SquareRoot(.x*.x + .y*.y + .z*.z)
        endmethod
       
        static method sum takes vector augend, vector addend returns vector
            local vector v = vector.allocate()
            set v.x = augend.x+addend.x
            set v.y = augend.y+addend.y
            set v.z = augend.z+addend.z
            return v
        endmethod
        method add takes vector addend returns nothing
            set this.x=this.x+addend.x
            set this.y=this.y+addend.y
            set this.z=this.z+addend.z
        endmethod
       
        static method difference takes vector minuend, vector subtrahend returns vector
            local vector v = vector.allocate()
            set v.x = minuend.x-subtrahend.x
            set v.y = minuend.y-subtrahend.y
            set v.z = minuend.z-subtrahend.z
            return v
        endmethod
        method subtract takes vector subtrahend returns nothing
            set this.x=this.x-subtrahend.x
            set this.y=this.y-subtrahend.y
            set this.z=this.z-subtrahend.z
        endmethod
       
        method scale takes real factor returns nothing
            set this.x=this.x*factor
            set this.y=this.y*factor
            set this.z=this.z*factor
        endmethod
       
        method setLength takes real length returns nothing
            local real l = SquareRoot(.x*.x + .y*.y + .z*.z)
            if l == 0.0 then
                debug call BJDebugMsg("vector.setLength error: The length of the vector is 0.0!")
                return
            endif
            set l = length/l
            set this.x = this.x*l
            set this.y = this.y*l
            set this.z = this.z*l
        endmethod
       
        static method dotProduct takes vector a, vector b returns real
            return (a.x*b.x+a.y*b.y+a.z*b.z)
        endmethod
       
        static method crossProduct takes vector a, vector b returns vector
            local vector v = vector.allocate()
            set v.x = a.y*b.z - a.z*b.y
            set v.y = a.z*b.x - a.x*b.z
            set v.z = a.x*b.y - a.y*b.x
            return v
        endmethod

        static method tripleProductScalar takes vector a, vector b, vector c returns real
            return ((a.y*b.z - a.z*b.y)*c.x+(a.z*b.x - a.x*b.z)*c.y+(a.x*b.y - a.y*b.x)*c.z)
        endmethod

        static method tripleProductVector takes vector a, vector b, vector c returns vector
            local vector v = vector.allocate()
            local real n = a.x*c.x+a.y*c.y+a.z*c.z
            local real m = a.x*b.x+a.y*b.y+a.z*b.z
            set v.x = b.x*n-c.x*m
            set v.y = b.y*n-c.y*m
            set v.z = b.z*n-c.z*m
            return v
        endmethod

// ================================================================

        static method projectionVector takes vector projected, vector direction returns vector
            local vector v = vector.allocate()
            local real l = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z
            if l == 0.0 then
                call v.destroy()
                debug call BJDebugMsg("vector.projectionVector error: The length of the direction vector is 0.0!")
                return 0
            endif
            set l = (projected.x*direction.x+projected.y*direction.y+projected.z*direction.z) / l
            set v.x = direction.x*l
            set v.y = direction.y*l
            set v.z = direction.z*l
            return v
        endmethod
        method projectVector takes vector direction returns nothing
            local real l = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z
            if l == 0.0 then
                debug call BJDebugMsg("vector.projectVector error: The length of the direction vector is 0.0!")
                return
            endif
            set l = (this.x*direction.x+this.y*direction.y+this.z*direction.z) / l
            set this.x = direction.x*l
            set this.y = direction.y*l
            set this.z = direction.z*l
        endmethod

        static method projectionPlane takes vector projected, vector normal returns vector
            local vector v = vector.allocate()
            local real l = normal.x*normal.x+normal.y*normal.y+normal.z*normal.z
            if l == 0.0 then
                call v.destroy()
                debug call BJDebugMsg("vector.projectionPlane error: The length of the normal vector is 0.0!")
                return 0
            endif
            set l = (projected.x*normal.x+projected.y*normal.y+projected.z*normal.z) / l
            set v.x = projected.x - normal.x*l
            set v.y = projected.y - normal.y*l
            set v.z = projected.z - normal.z*l
            return v
        endmethod
        method projectPlane takes vector normal returns nothing
            local real l = normal.x*normal.x+normal.y*normal.y+normal.z*normal.z
            if l == 0.0 then
                debug call BJDebugMsg("vector.projectPlane error: The length of the normal vector is 0.0!")
                return
            endif
            set l = (this.x*normal.x+this.y*normal.y+this.z*normal.z) / l
            set this.x = this.x - normal.x*l
            set this.y = this.y - normal.y*l
            set this.z = this.z - normal.z*l
        endmethod

        static method getAngle takes vector a, vector b returns real
            local real l = SquareRoot(a.x*a.x + a.y*a.y + a.z*a.z)*SquareRoot(b.x*b.x + b.y*b.y + b.z*b.z)
            if l == 0 then
                debug call BJDebugMsg("vector.getAngle error: The length of at least one of the vectors is 0.0!")
                return 0.0
            endif
            return Acos((a.x*b.x+a.y*b.y+a.z*b.z)/l) //angle is returned in radians
        endmethod
       
        method rotate takes vector axis, real angle returns nothing //angle is taken in radians
            local real xx
            local real xy
            local real xz
            local real yx
            local real yy
            local real yz
            local real zx
            local real zy
            local real zz
            local real al = axis.x*axis.x+axis.y*axis.y+axis.z*axis.z //axis length^2
            local real f
            local real c = Cos(angle)
            local real s = Sin(angle)
            if al == 0.0 then
                debug call BJDebugMsg("vector.rotate error: The length of the axis vector is 0.0!")
                return
            endif
            set f = (this.x*axis.x+this.y*axis.y+this.z*axis.z) / al
            set zx = axis.x*f
            set zy = axis.y*f
            set zz = axis.z*f //axis component of rotated vector
            set xx = this.x-zx
            set xy = this.y-zy
            set xz = this.z-zz //component of vector perpendicular to axis
            set al = SquareRoot(al)
            set yx = (axis.y*xz - axis.z*xy)/al
            set yy = (axis.z*xx - axis.x*xz)/al //y same length as x by using cross product and dividing with axis length
            set yz = (axis.x*xy - axis.y*xx)/al //x,y - coordinate system in which we rotate
            set this.x=xx*c+yx*s+zx
            set this.y=xy*c+yy*s+zy
            set this.z=xz*c+yz*s+zz
        endmethod
       
// ================================================================

        private static location loc = Location(0.0,0.0)

        static method createTerrainPoint takes real x, real y returns vector
            local vector v = vector.allocate()
            call MoveLocation(vector.loc,x,y)
            set v.x=x
            set v.y=y
            set v.z=GetLocationZ(loc)
            return v
        endmethod
        method getTerrainPoint takes real x, real y returns nothing
            call MoveLocation(vector.loc,x,y)
            set this.x=x
            set this.y=y
            set this.z=GetLocationZ(loc)
        endmethod

        static method createTerrainNormal takes real x, real y, real sampleRadius returns vector
            local vector v = vector.allocate()
            local real zx
            local real zy
            call MoveLocation(vector.loc, x-sampleRadius, y)
            set zx=GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x+sampleRadius, y)
            set zx=zx-GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x, y-sampleRadius)
            set zy=GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x, y+sampleRadius)
            set zy=zy-GetLocationZ(vector.loc)
            set sampleRadius=2*sampleRadius
            set v.x = zx*sampleRadius
            set v.y = zy*sampleRadius
            set v.z = sampleRadius*sampleRadius
            return v
        endmethod
        method getTerrainNormal takes real x, real y, real sampleRadius returns nothing
            local real zx
            local real zy
            call MoveLocation(vector.loc, x-sampleRadius, y)
            set zx=GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x+sampleRadius, y)
            set zx=zx-GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x, y-sampleRadius)
            set zy=GetLocationZ(vector.loc)
            call MoveLocation(vector.loc, x, y+sampleRadius)
            set zy=zy-GetLocationZ(vector.loc)
            set sampleRadius=2*sampleRadius
            set this.x = zx*sampleRadius
            set this.y = zy*sampleRadius
            set this.z = sampleRadius*sampleRadius
        endmethod

// ================================================================

        method isInCylinder takes vector cylinderOrigin, vector cylinderHeight, real cylinderRadius returns boolean
            local real l

            local real x = this.x-cylinderOrigin.x
            local real y = this.y-cylinderOrigin.y
            local real z = this.z-cylinderOrigin.z
            if x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z < 0.0 then //point below cylinder
                return false
            endif
           
            set x = x-cylinderHeight.x
            set y = y-cylinderHeight.y
            set z = z-cylinderHeight.z
            if x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z > 0.0 then //point above cylinder
                return false
            endif
           
            set l = cylinderHeight.x*cylinderHeight.x+cylinderHeight.y*cylinderHeight.y+cylinderHeight.z*cylinderHeight.z
            if l == 0.0 then
                debug call BJDebugMsg("vector.isInCylinder error: The length of the cylinderHeight vector is 0.0!")
                return false
            endif
            set l = (x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z) / l
            set x = x - cylinderHeight.x*l
            set y = y - cylinderHeight.y*l
            set z = z - cylinderHeight.z*l
            if x*x+y*y+z*z > cylinderRadius*cylinderRadius then //point outside cylinder
                return false
            endif
           
            return true
        endmethod

        method isInCone takes vector coneOrigin, vector coneHeight, real coneRadius returns boolean
            local real l

            local real x = this.x-coneOrigin.x
            local real y = this.y-coneOrigin.y
            local real z = this.z-coneOrigin.z
            if x*coneHeight.x+y*coneHeight.y+z*coneHeight.z < 0.0 then //point below cone
                return false
            endif
           
            set l = coneHeight.x*coneHeight.x+coneHeight.y*coneHeight.y+coneHeight.z*coneHeight.z
            if l == 0.0 then
                debug call BJDebugMsg("vector.isInCone error: The length of the coneHeight vector is 0.0!")
                return false
            endif
            set l = (x*coneHeight.x+y*coneHeight.y+z*coneHeight.z) / l
            set x = x - coneHeight.x*l
            set y = y - coneHeight.y*l
            set z = z - coneHeight.z*l
            if SquareRoot(x*x+y*y+z*z) > coneRadius*(1.0-l) then //point outside cone
                return false
            endif
           
            return true
        endmethod

        method isInSphere takes vector sphereOrigin, real sphereRadius returns boolean
            if sphereRadius*sphereRadius < ((this.x-sphereOrigin.x)*(this.x-sphereOrigin.x)+(this.y-sphereOrigin.y)*(this.y-sphereOrigin.y)+(this.z-sphereOrigin.z)*(this.z-sphereOrigin.z)) then
                return false
            endif
            return true
        endmethod
    endstruct

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: http://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
/**************************************************************
*
*   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 BJObjectId

//! novjass

// creating:

    // from native object-id / integer
    set oid = BJObjectId('A000')

    // from string
    set oid = BJObjectId.from_str("I001")


// printing:

    call BJDebugMsg(oid.to_str())


// looping through a set of object-id(s)

    // forward
    local BJObjectId oid = BJObjectId('A000')
    local BJObjectId last_oid = BJObjectId('A010')
    loop
        exitwhen oid > last_oid
        // do stuff
        set oid = oid.plus_1()
    endloop

    // backward
    local BJObjectId oid = BJObjectId('A010')
    local BJObjectId last_oid = BJObjectId('A000')
    loop
        exitwhen oid < last_oid
        // do stuff
        set oid = oid.minus_1()
    endloop


// mapping BJObjectId(s) to array-indices / struct instances

    local integer index
    local MyStruct my_struct

    set index = BJObjectId('H000').to_unit_index()
    set my_struct = MyStruct( BJObjectId('h001').to_unit_index() )

    set index = BJObjectId('I000').to_item_index()
    set my_struct = BJObjectId('I001').to_item_index() // don't really need the MyStruct cast (because vJass =))

    set index = BJObjectId('B000').to_destructable_index()
    set my_struct = BJObjectId('B001').to_destructable_index()

    set index = BJObjectId('D000').to_doodad_index()
    set my_struct = BJObjectId('D001').to_doodad_index()

    set index = BJObjectId('A000').to_ability_index()
    set my_struct = BJObjectId('A001').to_ability_index()

    set index = BJObjectId('B000').to_buff_index()
    set my_struct = BJObjectId('B001').to_buff_index()

    set index = BJObjectId('R000').to_upgrade_index()
    set my_struct = BJObjectId('R001').to_upgrade_index()

    // for campaign objects the methods have a "c" after the "to_"
    set index = BJObjectId('A000').to_cunit_index()
    set index = BJObjectId('A000').to_cability_index()
    ...

// NOTE: the to_*_index methods break for object-id(s) > 'XXOZ', i.e
// 900 is the maximum number of objects you can have and still be able to use those methods;
// the reason is that the object-id(s) after 'XXOZ' have indices > 8190

//! endnovjass

    struct BJObjectId extends array

        static method from_str takes string oid returns thistype
            // '0' = 48 .. '9' = 57,
            // 'A' = 65 .. 'Z' = 90
            // 'a' = 97 .. 'z' = 122
            //
            // index(<chr>):
            // '0' = 0; chr(0 + 48) = '0' = 48
            // 'A' = 17; chr(17 + 48) = 'A' = 65
            // 'a' = 49; chr(49 + 48) = 'a' = 97
            //
            local string chars = "0123456789.......ABCDEFGHIJKLMNOPQRSTUVWXYZ......abcdefghijklmnopqrstuvwxyz"
            local integer this = 0
            local integer i
            local integer j
            local integer ordinal
            local string chr
            local integer pow_256 = 1

            set i = 3
            loop
                exitwhen i < 0
                set chr = SubString(oid, i, i + 1)

                set j = 0
                loop
                    exitwhen j >= 75

                    if chr == SubString(chars, j, j + 1) then
                        set this = this + (j + 48) * pow_256
                        set pow_256 = pow_256 * 256
                        exitwhen true
                    endif

                    set j = j + 1
                endloop

                set i = i - 1
            endloop

            return this
        endmethod

        method to_str takes nothing returns string
            local string chars = "0123456789.......ABCDEFGHIJKLMNOPQRSTUVWXYZ......abcdefghijklmnopqrstuvwxyz"
            local integer t = this
            local integer i
            local integer b
            local string result = ""

            set i = t
            set t = i / 0x100
            set b = i - t * 0x100 - 48
            set result = SubString(chars, b, b + 1) + result

            set i = t
            set t = i / 0x100
            set b = i - t * 0x100 - 48
            set result = SubString(chars, b, b + 1) + result

            set i = t
            set t = i / 0x100
            set b = i - t * 0x100 - 48
            set result = SubString(chars, b, b + 1) + result

            set t = t - 48
            set result = SubString(chars, t, t + 1) + result

            return result
        endmethod

        method plus_1 takes nothing returns thistype
            local integer t = this
            local integer i
            local integer b1
            local integer b2
            local integer b3
            local integer b4

            set i = t
            set t = i / 0x100
            set b4 = i - t * 0x100

            if b4 < 'Z' then
                if b4 != '9' then
                    set i = i + 1
                else
                    set i = i + 8
                endif
            else

                set i = t
                set t = i / 0x100
                set b3 = i - t * 0x100
                if b3 < 'Z' then
                    if b3 != '9' then
                        set i = i * 0x00000100 + 0x00000100 + '0'
                    else
                        set i = i * 0x00000100 + 0x00000800 + '0'
                    endif
                else

                    set i = t
                    set t = i / 0x100
                    set b2 = i - t * 0x100
                    if b2 < 'Z' then
                        if b2 != '9' then
                            set i = i * 0x00010000 + 0x00010000 + '0' * 0x00000100 + '0'
                        else
                            set i = i * 0x00010000 + 0x00080000 + '0' * 0x00000100 + '0'
                        endif
                    else

                        set i = t
                        if i != '9' then
                            set i = i * 0x01000000 + 0x01000000 + '0' * 0x00010000 + '0' * 0x00000100 + '0'
                        else
                            set i = i * 0x01000000 + 0x08000000 + '0' * 0x00010000 + '0' * 0x00000100 + '0'
                        endif
                    endif
                endif
            endif

            return i
        endmethod

        method minus_1 takes nothing returns thistype
            local integer t = this
            local integer i
            local integer b1
            local integer b2
            local integer b3
            local integer b4

            set i = t
            set t = i / 0x100
            set b4 = i - t * 0x100
            if b4 > '0' then
                if b4 != 'A' then
                    set i = i - 1
                else
                    set i = i - 8
                endif
            else

                set i = t
                set t = i / 0x100
                set b3 = i - t * 0x100
                if b3 > '0' then
                    if b3 != 'A' then
                        set i = i * 0x00000100 - 0x00000100 + 'Z'
                    else
                        set i = i * 0x00000100 - 0x00000800 + 'Z'
                    endif
                else

                    set i = t
                    set t = i / 0x100
                    set b2 = i - t * 0x100
                    if b2 > '0' then
                        if b2 != 'A' then
                            set i = i * 0x00010000 - 0x00010000 + 'Z' * 0x00000100 + 'Z'
                        else
                            set i = i * 0x00010000 - 0x00080000 + 'Z' * 0x00000100 + 'Z'
                        endif
                    else

                        set i = t
                        if i != 'A' then
                            set i = i * 0x01000000 - 0x01000000 + 'Z' * 0x00010000 + 'Z' * 0x00000100 + 'Z'
                        else
                            set i = i * 0x01000000 - 0x08000000 + 'Z' * 0x00010000 + 'Z' * 0x00000100 + 'Z'
                        endif
                    endif
                endif
            endif

            return i
        endmethod

        method operator< takes thistype other returns boolean
            return integer(this) < integer(other)
        endmethod

        private static integer array first_unit_oid
        private static integer array first_cunit_oid
        private static method onInit takes nothing returns nothing
            set first_unit_oid['H'] = 'H000'
            set first_unit_oid['h'] = 'h000'
            set first_unit_oid['O'] = 'O000'
            set first_unit_oid['o'] = 'o000'
            set first_unit_oid['E'] = 'E000'
            set first_unit_oid['e'] = 'e000'
            set first_unit_oid['U'] = 'U000'
            set first_unit_oid['u'] = 'u000'
            set first_unit_oid['N'] = 'N000'
            set first_unit_oid['n'] = 'n000'

            set first_cunit_oid['H'] = 'H600'
            set first_cunit_oid['h'] = 'h600'
            set first_cunit_oid['O'] = 'O600'
            set first_cunit_oid['o'] = 'o600'
            set first_cunit_oid['E'] = 'E600'
            set first_cunit_oid['e'] = 'e600'
            set first_cunit_oid['U'] = 'U600'
            set first_cunit_oid['u'] = 'u600'
            set first_cunit_oid['N'] = 'N600'
            set first_cunit_oid['n'] = 'n600'
        endmethod

        method to_unit_index takes nothing returns integer
            return this - first_unit_oid[this / 0x01000000] + 1
        endmethod
        method to_cunit_index takes nothing returns integer
            return this - first_cunit_oid[this / 0x01000000] + 1
        endmethod

        method to_item_index takes nothing returns integer
            return this - 'I000' + 1
        endmethod
        method to_citem_index takes nothing returns integer
            return this - 'I600' + 1
        endmethod

        method to_destructable_index takes nothing returns integer
            return this - 'B000' + 1
        endmethod
        method to_cdestructable_index takes nothing returns integer
            return this - 'B600' + 1
        endmethod

        method to_doodad_index takes nothing returns integer
            return this - 'D000' + 1
        endmethod
        method to_cdoodad_index takes nothing returns integer
            return this - 'D600' + 1
        endmethod

        method to_ability_index takes nothing returns integer
            return this - 'A000' + 1
        endmethod
        method to_cability_index takes nothing returns integer
            return this - 'A600' + 1
        endmethod

        method to_buff_index takes nothing returns integer
            return this - 'B000' + 1
        endmethod
        method to_cbuff_index takes nothing returns integer
            return this - 'B600' + 1
        endmethod

        method to_upgrade_index takes nothing returns integer
            return this - 'R000' + 1
        endmethod
        method to_cupgrade_index takes nothing returns integer
            return this - 'R600' + 1
        endmethod

    endstruct

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 = bj_mapInitialPlayableArea

            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 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
       
        implement WorldBoundInit
    endstruct
endlibrary
library AutoFly initializer onInit requires UnitDex
/***************************************************************
*
*   v1.0, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   AutoFly adds & removes crow form upon any unit entering the map. This
*   allows modifying of the units height without having to do it manually.
*
*   Credits go to azlier for the orginal idea.
*
****************************************************************/


    globals
        private constant integer CROW_FORM = 'Amrf'
    endglobals

    private function AddFly takes nothing returns boolean
        return UnitAddAbility(GetIndexedUnit(), CROW_FORM) and UnitRemoveAbility(GetIndexedUnit(), CROW_FORM)
    endfunction

    private function onInit takes nothing returns nothing
        call RegisterUnitIndexEvent(Filter(function AddFly), EVENT_UNIT_INDEX)
    endfunction

endlibrary
library SpinningBlackHole /* v1.8 [3D]


    */
requires /*

    */
UnitMotion                  /*
    */
UnitMotionSentinel          /*
    */
StunSystem                  /*   https://www.hiveworkshop.com/threads/system-stun.196749/
    */
optional SpellEffectEvent   /*   https://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
    */
optional ResourcePreloader  /*   https://www.hiveworkshop.com/threads/snippet-resource-preloader.287358/
    */
optional DummyRecycler      /*   https://www.hiveworkshop.com/threads/dummy-recycler-v1-24.277659/
    */
optional UnitDex            /*   https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
    */
optional GroupUtils         /*   https://www.wc3c.net/showthread.php?t=104464
    */
optional Table              /*   https://www.hiveworkshop.com/threads/snippet-new-table.188084/
    */
optional ErrorMessage       /*   https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j


    |---------|
    | Credits |
    |---------|

        Author:
            - AGD

        Dependencies:
            - iAyanami aka Ayanami (StunSystem)
            - Bribe (SpellEffectEvent, Table)
            - Flux (DummyRecycler)
            - TriggerHappy (UnitDex)
            - AGD (ResourcePreloader)
            - Rising_Dusk (GroupUtils)

        Others:
            - Vexorian (dummy.mdx)


    |-------------------------|
    | Importing Instructions: |
    |-------------------------|

        - Copy the dummy model in the import manager
        - Copy all the neccessary object data in the object editor
        - Copy the "Spinning Black Hole [3D] by AGD" trigger category together with the library requirements
        - Configure the object rawcodes in the configuration below

        Note:
            This requires jasshelper or much better if you just install JASS NewGen Pack or WEX


    Do not release another version of this spell or make any form of duplication (aside for your
    personal use) without the creator's permission and please give credits to the author if you
    use this resource in your map.

    Share your feedback and suggestions about this spell at Hive Workshop and if you have some
    problems in configuring the spell, don't hesitate to ask the author. Please also report bugs and
    glitches if you found some.


    |----------------|
    | Spell Summary: |
    |----------------|

    Creates a spinning black hole at a target area that gradually increases its radius over time.
    Units within the radius of the event horizon will be pulled towards the singularity with a speed
    that increases as they approach it and will take damage over time. Units at the singularity are
    crushed together with the other masses, taking a percentage of their maximum hit points as an
    additional damage over time. Before the black hole vanishes, it explodes with great force,
    throwing units from within outwards with a distance that increases the closer they are to the
    singularity, dealing damage and stunning units for a certain duration.                          */



    globals
        /*=================================================================*/
        /*                       Spell Configuration                       */
        /*=================================================================*/

        /*-----------------------------------------------------------------*/
        /*                      Static Configuration                       */
        /*-----------------------------------------------------------------*/
        /*******************************************************************/
        /*                       Basic Configuration                       */
        /*******************************************************************/
        /*
        The rawcode of the spell                                           */

        private constant integer SPELL_ID                   = 'SBH1'
        /*
        The rawcode of the dummy unit                                      */

        private constant integer DUMMY_ID                   = 'sbh3'
        /*
        Determines if the target units will be paused when being
        pulled (Reduces a significant amount of lag)                       */

        private constant boolean PAUSE_TARGETS              = true
        /*
        Determines if the targets shrink as they approach the singularity  */

        private constant boolean SHRINK_UNIT_SIZE           = true
        /*
        The periodic execution interval
        The default value is 0.03125 (32 FPS)                              */

        private constant real PERIOD                        = 1.00/32.00
        /*
        The attack type of the black hole's pull                           */

        private constant attacktype PULL_ATTACK_TYPE        = ATTACK_TYPE_NORMAL
        /*
        The attack type of the black hole's singularity compression        */

        private constant attacktype COMPRESS_ATTACK_TYPE    = ATTACK_TYPE_NORMAL
        /*
        The attack type of the black hole's explosion                      */

        private constant attacktype EXPLOSION_ATTACK_TYPE   = ATTACK_TYPE_NORMAL
        /*
        The damage type of the black hole's pull                           */

        private constant damagetype PULL_DAMAGE_TYPE        = DAMAGE_TYPE_MAGIC
        /*
        The damage type of the black hole's singularity compression        */

        private constant damagetype COMPRESS_DAMAGE_TYPE    = DAMAGE_TYPE_MAGIC
        /*
        The damage type of the black hole's explosion                      */

        private constant damagetype EXPLOSION_DAMAGE_TYPE   = DAMAGE_TYPE_MAGIC
        /*
        The weapon type of the black hole's pull                           */

        private constant weapontype PULL_WEAPON_TYPE        = null
        /*
        The weapon type of the black hole's singularity compression        */

        private constant weapontype COMPRESS_WEAPON_TYPE    = null
        /*
        The weapon type of the black hole's explosion                      */

        private constant weapontype EXPLOSION_WEAPON_TYPE   = null

        /*******************************************************************/
        /*                        Spell Aesthetics                         */
        /*******************************************************************/
        /*
        Determines if the spell uses special effects for the black hole's
        target units                                                       */

        private constant boolean USE_TARGET_SFX             = true
        /*
        If true, special effect will only be attached to the unit once and
        will last until the black hole explosion. Else, special effect will
        constantly be attached on the unit and destroyed per period.       */

        private constant boolean STATIC_PULL_SFX            = true
        /*
        The special effect model of the black hole's singularity           */

        private constant string SFX_MODEL_1                 = "Abilities\\Spells\\Undead\\Possession\\PossessionTarget.mdl"
        private constant string SFX_MODEL_2                 = "Abilities\\Spells\\Other\\Drain\\ManaDrainTarget.mdl"
        /*
        The special effect model of the black hole                         */

        private constant string SFX_MODEL_3                 = "Abilities\\Spells\\Human\\slow\\slowtarget.mdl"
        /*
        The special effect model of the black hole's explosion             */

        private constant string SFX_MODEL_4                 = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
        /*
        The special effect model attached to pulled units
        (Only works if USE_TARGET_SFX is true)                             */

        private constant string PULL_SFX_MODEL              = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
        /*
        Attachment point for SFX_MODEL_1                                   */

        private constant string ATTACH_POINT_1              = "origin"
        /*
        Attachment point for SFX_MODEL_2                                   */

        private constant string ATTACH_POINT_2              = "origin"
        /*
        Attachment point for SFX_MODEL_3                                   */

        private constant string ATTACH_POINT_3              = "origin"
        /*
        Attachment point for SFX_MODEL_4                                   */

        private constant string ATTACH_POINT_4              = "origin"
        /*
        Attachment point for PULL_SFX_MODEL                                */

        private constant string PULL_SFX_ATTACH_POINT       = "chest"
        /*
        The scaling of the special effect of the explosion in relation to
        the radius                                                         */

        private constant real EXPLOSION_SCALE               = 0.20

    endglobals

        /*-----------------------------------------------------------------*/
        /*                     Dynamic Configuration                       */
        /*-----------------------------------------------------------------*/
        /*******************************************************************/
        /*                     Main Spell Mechanics                        */
        /*******************************************************************/
        /*
        Determines the duration of the black hole                          */

        private constant function Duration takes integer level returns real
            return 7.00 + 1.00*level
        endfunction
        /*
        Determines the explosion stun duration                             */

        private constant function StunDuration takes integer level returns real
            return 2.50 + 0.50*level
        endfunction
        /*
        Determines the duration of increase in radius (The value must
        obviously be smaller that the black hole's duration)
        The value of the final radius is obviously not affected by this
        value. This duration doesn't start at the same time with the
        appearance of the black hole. Rather it ends at the same time as
        its disappearance.                                                 */

        private constant function RadiusIncreaseDuration takes integer level returns real
            return 3.00 + 1.00*level
        endfunction
        /*
        Determines the damage dealt per second to the units being pulled   */

        private constant function PullDamagePerSecond takes integer level returns real
            return 50.00 + 10.00*level
        endfunction
        /*
        Determines the damage dealt per second by the black hole's
        singularity (Max Health Percentage)                                */

        private constant function PullDamagePerSecondPercentage takes integer level returns real
            return 2.00 + 1.00*level
        endfunction
        /*
        Determines the damage done by the black hole's explosion           */

        private constant function ThrowDamage takes integer level, real radius, real distanceFromCenter returns real
            local real minimum = 70.00 + 30.00*level
            local real maximum = 350.00 + 150.00*level
            return (maximum - minimum)*(1.00 - distanceFromCenter/radius) + minimum
        endfunction
        /*
        Determines the radius of the black hole (Synchronize the
        Black Hole Stun ability's AOE in the object editor with
        this value)                                                        */

        private constant function Radius takes integer level returns real
            return 300.00 + 100.00*level
        endfunction
        /*
        Determines the radius of the singularity                           */

        private constant function SingularityRadius takes integer level returns real
            return 50.00 + 10.00*level
        endfunction
        /*
        Determines the maximum value of increase in radius before the black
        hole vanishes (Percentage)                                         */

        private constant function RadiusIncrease takes integer level returns real
            return 40.00 + 0.00*level
        endfunction
        /*
        Determines how small the scale of the unit is reduced upon landing
        in the singularity (Value of 0 means no scale reduction)
        This function may cause problems if you have units in your map will
        scaling value not equal to 1. If this does cause problems, just set
        the value to 0.                                                    */

        private constant function UnitScaleReduction takes integer level returns real
            return 70.00 + 0.00*level
        endfunction

        /*******************************************************************/
        /*                         Spell Kinematics                        */
        /*******************************************************************/
        /*
        The acceleration due to the black hole's gravity                   */

        private function PullAcceleration takes integer level, real percentDistanceFromEventHorizon returns real
            return (14.00 + 0.00*level) + (6.00 + 1.00*level)*percentDistanceFromEventHorizon
        endfunction
        /*
        The pull angle deflection in relation to the target's distance from
        the singularity (In other words, the angle deflection of the pull
        that causes the target to "spiral-in" towards the black hole's
        singularity)
        You can put a POSITIVE value for a COUNTER-CLOCKWISE spin or a
        NEGATIVE value for a CLOCKWISE spin (A value of ZERO will turn the
        spell to a Non-Spinning Black Hole)                                */

        private function PullAngleDeflection takes integer level, real distanceFromEventHorizon returns real
            return -(0.00*level + Pow((distanceFromEventHorizon)*0.10, 4.00)/800.00)
        endfunction
        /*
        The absolute value limit for the pull angle deflection (This is
        important so that the angle deflection will not continually
        increase to the point where it will already cause a "spiral-out"
        motion)                                                            */

        private function PullAngleDeflectionLimit takes integer level returns real
            return 25.50 - 0.00*level
        endfunction
        /*
        The distance of the explosion throw in the xy-plane                */

        private function ThrowDistance takes integer level, real radius, real distanceFromCenter returns real
            local real minimum = (0.10 + 0.00*level)*radius
            local real maximum = (1.00 + 0.00*level)*radius
            return (maximum - minimum)*(1.00 - distanceFromCenter/radius) + minimum
        endfunction
        /*
        The maximum height of the parabola of the explosion throw          */

        private function ThrowHeight takes integer level, real radius, real distanceFromCenter returns real
            local real minimum = (0.05 + 0.00*level)*radius
            local real maximum = (0.40 + 0.00*level)*radius
            return (maximum - minimum)*(1.00 - distanceFromCenter/radius) + minimum
        endfunction

        /*******************************************************************/
        /*                       Spell Target Filter                       */
        /*******************************************************************/
        /*
        Determines the type of units allowed as the spell's target. The
        default setup filters out allies and structures.                   */

        private function PullTargetFilter takes unit target, unit caster, player owner returns boolean
            return not IsUnitAlly(target, owner)                    and/*
                */
not IsUnitType(target, UNIT_TYPE_STRUCTURE)
        endfunction
        /*
        Determines the type of units allowed as the spell's target. The
        default setup filters out allies and structures.                   */

        private function ExplosionTargetFilter takes unit target, unit caster, player owner returns boolean
            return not IsUnitAlly(target, owner)                    and/*
                */
not IsUnitType(target, UNIT_TYPE_STRUCTURE)
        endfunction
        /*=================================================================*/
        /*                   End of Spell Configuration                    */
        /*=================================================================*/


    /*===== Do not change anything below this line if you're not so sure on what you're doing =====*/


    native UnitAlive takes unit u returns boolean


    private struct SpinningBlackHole extends array

        private static timer staticTimer = CreateTimer()
        private static thistype this = 0
        private static vector zAxis
        private static vector tempVector
        private static group forSwap
        private static group tempGroup
        private static unit tempCaster
        private static player tempOwner
        private static integer tempLevel
        private static real tempRadius
        private static real tempSmallRadius
        private static real tempCenterX
        private static real tempCenterY
        private static real tempDistance
        private static real tempDamage
        private static real tempDamagePercent
        private static unit picked
        private static real dx
        private static real dy
        private static real tempX
        private static real tempY
        private static real distance
        private static real angle
        private static real zAngle
        private static real deflectLimit
        private static real velocity
        private static real xyVelocity
        private static real stunDuration
        private thistype recycler
        private thistype prev
        private thistype next
        private player owner
        private unit caster
        private unit dummy
        private integer level
        private integer stageId
        private real duration
        private real elapsed
        private real radius
        private real radiusIncrease
        private real growthDuration
        private real smallRadius
        private real frictionalForce
        private real damagePerInterval
        private real percentDamagePerInterval
        private real scaleReduction
        private real centerX
        private real centerY
        private effect sfx1
        private effect sfx2
        private effect sfx3
        private effect targetSfx
        private group targetGroup

        static if SHRINK_UNIT_SIZE then
            private static real tempScaleReduction
        endif

        static if not LIBRARY_GroupUtils then
            private static group ENUM_GROUP = CreateGroup()
        endif

        static if STATIC_PULL_SFX and USE_TARGET_SFX then

            private static thistype unitId

            static if LIBRARY_Table then
                private static TableArray table
            else
                private static hashtable table = InitHashtable()
            endif

        endif

        /*
        Group refresher - removes 'shadow units' from the static target group   */

        static if LIBRARY_UnitDex then

            private static method onDeindex takes nothing returns nothing

                local unit deindexed = GetIndexedUnit()
                local thistype this = thistype(0).next

                loop
                    exitwhen this == 0
                    call GroupRemoveUnit(.targetGroup, deindexed)
                    set this = .next
                endloop

                set deindexed = null

            endmethod

        elseif not LIBRARY_GroupUtils then

            private static boolean clearGroup

            private static method refresh takes nothing returns nothing

                if clearGroup then
                    set clearGroup = false
                    call GroupClear(forSwap)
                endif

                call GroupAddUnit(forSwap, GetEnumUnit())

            endmethod

            private static method refreshGroup takes nothing returns nothing

                set clearGroup = true
                call ForGroup(tempGroup, function thistype.refresh)

                if clearGroup then
                    call GroupClear(tempGroup)
                endif

            endmethod

        endif

        /*
        This removes unit from the target group once it no longer meets
        the condition for being allowed as a target                     */

        private static method removeTarget takes nothing returns nothing

            static if SHRINK_UNIT_SIZE then
                if tempScaleReduction > 0 then
                    call SetUnitScale(picked, 1.00, 0, 0)
                endif
            endif

            static if PAUSE_TARGETS then
                call PauseUnit(picked, false)
            else
                call SetUnitPathing(picked, true)
            endif

            static if USE_TARGET_SFX and STATIC_PULL_SFX then
                set unitId = GetHandleId(picked)

                static if LIBRARY_Table then
                    call DestroyEffect(table[this].effect[unitId])
                    call table[this].remove(unitId)
                else
                    call DestroyEffect(LoadEffectHandle(table, 0, unitId))
                    call RemoveSavedHandle(table, 0, unitId)
                endif
            endif

        endmethod

        /*
        Periodic actions
        Executes the main spell mechanics                              */

        private static method periodic takes nothing returns nothing

            set this = thistype(0).next

            loop

                exitwhen this == 0

                set tempCaster = this.caster
                set tempOwner = this.owner
                set tempGroup = this.targetGroup
                set tempRadius = this.radius
                set tempCenterX = this.centerX
                set tempCenterY = this.centerY
                set tempLevel = this.level

                static if SHRINK_UNIT_SIZE then
                    set tempScaleReduction = this.scaleReduction
                endif

                /*
                Stage 1:
                Pull units within the radius of the black hole     */

                if this.stageId == 1 then

                    set this.elapsed = this.elapsed + PERIOD

                    set tempDamage = this.damagePerInterval
                    set tempDamagePercent = this.percentDamagePerInterval
                    set tempSmallRadius = this.smallRadius

                    if this.elapsed > this.duration - this.growthDuration then
                        set tempRadius = tempRadius*(1.00 + this.radiusIncrease)
                        set this.radius = tempRadius
                        call SetUnitScale(this.dummy, tempRadius*0.01, 0, 0)
                    endif

                    /* Enumerate units within the radius and add each of them to the group
                       if they weren't added yet                                             */

                    call GroupEnumUnitsInRange(ENUM_GROUP, tempCenterX, tempCenterY, tempRadius, null)

                    loop

                        set picked = FirstOfGroup(ENUM_GROUP)
                        exitwhen picked == null
                        call GroupRemoveUnit(ENUM_GROUP, picked)

                        if not IsUnitInGroup(picked, tempGroup) and PullTargetFilter(picked, tempCaster, tempOwner) then

                            call GroupAddUnit(tempGroup, picked)

                            static if PAUSE_TARGETS then
                                call PauseUnit(picked, true)
                            else
                                call SetUnitPathing(picked, false)
                            endif

                            static if USE_TARGET_SFX and STATIC_PULL_SFX then
                                static if LIBRARY_Table then
                                    set table[this].effect[GetHandleId(picked)] = AddSpecialEffectTarget(PULL_SFX_MODEL, picked, PULL_SFX_ATTACH_POINT)
                                else
                                    call SaveEffectHandle(table, this, GetHandleId(picked), AddSpecialEffectTarget(PULL_SFX_MODEL, picked, PULL_SFX_ATTACH_POINT))
                                endif
                            endif

                        endif

                    endloop

                    /* Do manual group refresh if UnitDex is not found */
                    static if not LIBRARY_UnitDex then
                        static if LIBRARY_GroupUtils then
                            call GroupRefresh(tempGroup)
                        else
                            call refreshGroup()
                        endif
                    endif

                    /* Pull Targets */
                    loop

                        set picked = FirstOfGroup(tempGroup)
                        exitwhen picked == null
                        call GroupRemoveUnit(tempGroup, picked)

                        if PullTargetFilter(picked, tempCaster, tempOwner) then

                            set dx = tempCenterX - GetUnitX(picked)
                            set dy = tempCenterY - GetUnitY(picked)
                            set distance = SquareRoot(dx*dx + dy*dy)

                            if distance <= tempRadius then

                                call GroupAddUnit(ENUM_GROUP, picked)

                                set tempDistance = tempRadius - distance
                                set angle = bj_DEGTORAD*PullAngleDeflection(tempLevel, tempDistance)
                                set deflectLimit = RAbsBJ(PullAngleDeflectionLimit(tempLevel)*bj_DEGTORAD)

                                if deflectLimit > 0.00 and RAbsBJ(angle) > deflectLimit then
                                    if angle > 0.00 then
                                        set angle = Atan2(dy, dx) - deflectLimit
                                    else
                                        set angle = Atan2(dy, dx) + deflectLimit
                                    endif
                                else
                                    set angle = Atan2(dy, dx) - angle
                                endif

                                set xyVelocity = PullAcceleration(tempLevel, tempDistance/tempRadius)
                                set tempVector = vector.create(xyVelocity*Cos(angle), xyVelocity*Sin(angle), 0.00)
                                call UnitMotion[picked].addVelocityByVector(tempVector)
                                call tempVector.destroy()

                                static if PAUSE_TARGETS then
                                    call PauseUnit(picked, true)
                                else
                                    call SetUnitPathing(picked, false)
                                endif

                                static if SHRINK_UNIT_SIZE then
                                    if tempScaleReduction > 0 then
                                        call SetUnitScale(picked, 1.00 - (tempScaleReduction*tempDistance/tempRadius*0.01), 0, 0)
                                    endif
                                endif

                                static if USE_TARGET_SFX and not STATIC_PULL_SFX then
                                    call DestroyEffect(AddSpecialEffectTarget(PULL_SFX_MODEL, picked, PULL_SFX_ATTACH_POINT))
                                endif

                                /* Deal damage to pulled units */
                                call UnitDamageTarget(tempCaster, picked, tempDamage, true, false, PULL_ATTACK_TYPE, PULL_DAMAGE_TYPE, PULL_WEAPON_TYPE)

                                /* Deal additional damage to units compressed in the singularity */
                                if distance <= tempSmallRadius then
                                    call UnitDamageTarget(tempCaster, picked, GetUnitState(picked, UNIT_STATE_MAX_LIFE)*tempDamagePercent, true, false, COMPRESS_ATTACK_TYPE, COMPRESS_DAMAGE_TYPE, COMPRESS_WEAPON_TYPE)
                                endif

                            else

                                call removeTarget()

                            endif

                        else

                            call removeTarget()

                        endif

                    endloop

                    set forSwap = tempGroup
                    set this.targetGroup = ENUM_GROUP
                    set ENUM_GROUP = forSwap

                    if this.elapsed > this.duration then
                        set this.stageId = 2
                    endif

                /*
                Stage 2:
                Explode and destroy the black hole instance                             */

                elseif this.stageId == 2 then

                    set stunDuration = StunDuration(tempLevel)

                    /* Do manual group refresh if UnitDex is not found */
                    static if not LIBRARY_UnitDex then
                        static if LIBRARY_GroupUtils then
                            call GroupRefresh(tempGroup)
                        else
                            call refreshGroup()
                        endif
                    endif

                    /* Loop through all targets and stun them */
                    loop

                        set picked = FirstOfGroup(tempGroup)
                        exitwhen picked == null
                        call GroupRemoveUnit(tempGroup, picked)
                        call removeTarget()

                        if ExplosionTargetFilter(picked, tempCaster, tempOwner) then

                            set dx = GetUnitX(picked) - tempCenterX
                            set dy = GetUnitY(picked) - tempCenterY
                            set tempDistance = SquareRoot(dx*dx + dy*dy)
                            set angle = Atan2(dy, dx)
                            set distance = ThrowDistance(tempLevel, tempRadius, tempDistance)

                            if ThrowHeight(tempLevel, tempRadius, tempDistance) > 0.00 then
                                set zAngle = Atan2(4.00*ThrowHeight(tempLevel, tempRadius, tempDistance), distance)
                                set velocity = SquareRoot(-UnitMotion.GRAVITY*distance/Sin(2.00*zAngle))
                                set xyVelocity = velocity*Cos(zAngle)
                                set tempVector = vector.create(xyVelocity*Cos(angle), xyVelocity*Sin(angle), velocity*Sin(zAngle))
                            else
                                set xyVelocity = SquareRoot(2.00*UnitMotion[picked].friction*distance)
                                set tempVector = vector.create(xyVelocity*Cos(angle), xyVelocity*Sin(angle), 0.00)
                            endif

                            call UnitMotion[picked].addVelocityByVector(tempVector)
                            call tempVector.destroy()
                            call UnitDamageTarget(tempCaster, picked, ThrowDamage(tempLevel, tempRadius, tempDistance), true, false, EXPLOSION_ATTACK_TYPE, EXPLOSION_DAMAGE_TYPE, EXPLOSION_WEAPON_TYPE)

                            if stunDuration > 0 then
                                call Stun.apply(picked, stunDuration, false)
                            endif

                        endif

                    endloop

                    call DestroyGroup(tempGroup)

                    /* Remove the black hole's special effects */
                    call DestroyEffect(this.sfx1)
                    call DestroyEffect(this.sfx2)
                    call DestroyEffect(this.sfx3)

                    /* Explode black hole, remove special effects, and stun units */
                    static if LIBRARY_DummyRecycler then
                        call RecycleDummy(this.dummy)
                        set this.dummy = GetRecycledDummyAnyAngle(tempCenterX, tempCenterY, 0.00)
                        call DummyAddRecycleTimer(this.dummy, 2.00)
                    else
                        call RemoveUnit(this.dummy)
                        set this.dummy = CreateUnit(Player(14), DUMMY_ID, tempCenterX, tempCenterY, 0.00)
                        call UnitApplyTimedLife(this.dummy, 'BTLF', 2.00)
                    endif

                    call SetUnitScale(this.dummy, tempRadius*EXPLOSION_SCALE*0.01, 0.00, 0.00)
                    call DestroyEffect(AddSpecialEffectTarget(SFX_MODEL_4, this.dummy, ATTACH_POINT_4))

                    /* Recycling spell instance */
                    set this.elapsed          = 0.00
                    set this.stageId          = 0
                    set this.targetGroup      = null
                    set this.caster           = null
                    set this.owner            = null
                    set this.dummy            = null
                    set this.sfx1             = null
                    set this.sfx2             = null
                    set this.sfx3             = null
                    set this.next.prev        = this.prev
                    set this.prev.next        = this.next
                    set this.recycler         = thistype(0).recycler
                    set thistype(0).recycler  = this

                    /* If this is the only black hole instance left, pause the timer to stop
                       running the periodic actions                                         */

                    if thistype(0).next == 0 then
                        call PauseTimer(GetExpiredTimer())
                    endif

                endif

                set this = this.next

            endloop

        endmethod

        /*
        This method sets up the necessary spell data and cache those
        values that remain constant throughout the instance's execution
        duration so that it won't need to be refered repeatedly in the
        periodic method                                                 */

        private static method setup takes nothing returns nothing

            static if LIBRARY_ErrorMessage then
                debug call ThrowError(thistype(0).recycler == 0, "SpinningBlackHole", "allocate()", "thistype", 0, "Overflow")
            endif

            /* Allocate a new instance and add it to the linked-list */
            set this                            = thistype(0).recycler
            set thistype(0).recycler            = this.recycler
            set this.next                       = 0
            set this.prev                       = thistype(0).prev
            set thistype(0).prev.next           = this
            set thistype(0).prev                = this

            /* Initialize the necessary data */
            set this.caster                     = GetTriggerUnit()
            set this.owner                      = GetTriggerPlayer()
            set this.centerX                    = GetSpellTargetX()
            set this.centerY                    = GetSpellTargetY()

            static if LIBRARY_DummyRecycler then
                set this.dummy                  = GetRecycledDummy(this.centerX, this.centerY, 0, 0)
            else
                set this.dummy                  = CreateUnit(Player(14), DUMMY_ID, this.centerX, this.centerY, 0)
            endif

            set tempLevel                       = GetUnitAbilityLevel(this.caster, SPELL_ID)
            set this.level                      = tempLevel
            set this.duration                   = Duration(tempLevel)
            set this.radius                     = Radius(tempLevel)
            set this.growthDuration             = RadiusIncreaseDuration(tempLevel)
            set this.radiusIncrease             = RadiusIncrease(tempLevel)/this.growthDuration*0.01*PERIOD
            set this.smallRadius                = SingularityRadius(tempLevel)
            set this.damagePerInterval          = PullDamagePerSecond(tempLevel)*PERIOD
            set this.percentDamagePerInterval   = PullDamagePerSecondPercentage(tempLevel)*0.01*PERIOD
            set this.sfx1                       = AddSpecialEffectTarget(SFX_MODEL_1, this.dummy, ATTACH_POINT_1)
            set this.sfx2                       = AddSpecialEffectTarget(SFX_MODEL_2, this.dummy, ATTACH_POINT_2)
            set this.sfx3                       = AddSpecialEffectTarget(SFX_MODEL_3, this.dummy, ATTACH_POINT_3)
            set this.targetGroup                = CreateGroup()
            set this.stageId                    = 1

            static if SHRINK_UNIT_SIZE then
                set this.scaleReduction         = UnitScaleReduction(tempLevel)
            endif

            /* Scaling the black hole's model */
            call SetUnitScale(this.dummy, this.radius*0.01, 0, 0)

            /* If this is the only instance, start the timer to run the periodic actions */
            if this.prev == 0 then
                call TimerStart(staticTimer, PERIOD, true, function thistype.periodic)
            endif

        endmethod

        static if not LIBRARY_SpellEffectEvent then

            private static method onCast takes nothing returns nothing
                if GetSpellAbilityId() == SPELL_ID then
                    call setup()
                endif
            endmethod

        endif

        /*
        Spell data initialization                               */

        private static method onInit takes nothing returns nothing

            static if LIBRARY_UnitDex then
                local code onDeindex = function thistype.onDeindex
            endif

            static if not LIBRARY_ResourcePreloader then
                local unit u = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
            endif

            /* Setup spell event */
            static if LIBRARY_SpellEffectEvent then
                call RegisterSpellEffectEvent(SPELL_ID, function thistype.setup)
            elseif LIBRARY_RegisterPlayerUnitEvent then
                call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
            else
                local code onCast = function thistype.onCast
                local trigger t = CreateTrigger()
                local integer i = 16
                loop
                    set i = i - 1
                    call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
                    exitwhen i == 0
                endloop
                call TriggerAddCondition(t, Filter(onCast))
                set t = null
            endif

            static if LIBRARY_UnitDex then
                call OnUnitDeindex(onDeindex)
            endif

            /* Preload spell resources */
            static if LIBRARY_ResourcePreloader then
                call PreloadEffect(SFX_MODEL_1)
                call PreloadEffect(SFX_MODEL_2)
                call PreloadEffect(SFX_MODEL_3)
                call PreloadEffect(SFX_MODEL_4)
                call PreloadEffect(PULL_SFX_MODEL)
                call PreloadAbility(SPELL_ID)
                call PreloadUnit(DUMMY_ID)
            else
                call DestroyEffect(AddSpecialEffectTarget(SFX_MODEL_1, u, "origin"))
                call DestroyEffect(AddSpecialEffectTarget(SFX_MODEL_2, u, "origin"))
                call DestroyEffect(AddSpecialEffectTarget(SFX_MODEL_3, u, "origin"))
                call DestroyEffect(AddSpecialEffectTarget(SFX_MODEL_4, u, "origin"))
                call DestroyEffect(AddSpecialEffectTarget(PULL_SFX_MODEL, u, "origin"))
                call UnitAddAbility(u, SPELL_ID)
                call RemoveUnit(u)
                set u = null
            endif

            static if USE_TARGET_SFX and STATIC_PULL_SFX and LIBRARY_Table then
                set table = TableArray[0x2000]
            endif

            set zAxis = vector.create(0.00, 0.00, 1.00)

            loop
                exitwhen this == 8190
                set this.recycler = this + 1
                set this = this + 1
            endloop
            set this.recycler = 0

        endmethod

    endstruct


endlibrary