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. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  4. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  5. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  6. 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

TorrentArray v1.1b.w3x
Variables
TorrentArray by AGD
TorrentArray Configurations
Geysers
MeteorShower
EruptionHelix
TorentArray Spell Core
TorrentArray
Dependencies
TorrentSystem
SpellEvent
UniqueList
Table
WorldBounds
ResourcePreloader
Other Dependencies
DummyRecycler vJASS
UnitDex
AutoFly
Alloc
ErrorMessage
StunSystem
RegisterNativeEvent
RegisterPlayerUnitEvent
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 InitFunction

    private function RandomX takes nothing returns real
        return GetRandomReal(GetRectMinX(bj_mapInitialPlayableArea), GetRectMaxX(bj_mapInitialPlayableArea))
    endfunction
    private function RandomY takes nothing returns real
        return GetRandomReal(GetRectMinY(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea))
    endfunction

    private function A takes nothing returns nothing
        local unit u = GetTriggerUnit()
        call CreateUnit(GetOwningPlayer(u), GetUnitTypeId(u), RandomX(), RandomY(), GetRandomReal(0, 360))
        call TriggerSleepAction(10)
        call RemoveUnit(u)
        set u = null
    endfunction

    private function B takes nothing returns nothing
        call SetCameraField(CAMERA_FIELD_ZOFFSET, GetCameraField(CAMERA_FIELD_ZOFFSET) + 200, 0)
    endfunction

    private function InitFunction takes nothing returns nothing
        local unit u = CreateUnit(Player(0), 'H001', 0, 0, 0)
        local player p = Player(1)
        local real dist = 1300
        local integer creepcount = 20
        local integer campcount = 15
        local integer i = 0
        local integer f = 360
        local real x
        local real y
        local integer a
        local real facing
        local trigger t1 = CreateTrigger()
        local trigger t2 = CreateTrigger()
        call FogEnable(false)
        call FogMaskEnable(false)
        call SetPlayerState(p, PLAYER_STATE_GIVES_BOUNTY, 1)
        call SetHeroLevel(u, 10, false)
        call SelectUnit(u, true )
        loop
            exitwhen i == f
            set i = i + f/campcount
            set x = dist*Cos(i*bj_DEGTORAD)
            set y = dist*Sin(i*bj_DEGTORAD)
            set facing = bj_RADTODEG*Atan2(y, x)
            set a = 0
            loop
                set a = a + 1
                call CreateUnit(p, 'hpea', x, y, facing)
                exitwhen a == creepcount
            endloop
        endloop
        set u = null
        call TriggerRegisterAnyUnitEventBJ(t1, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(t1, function A)
        call TriggerRegisterPlayerEvent(t2, Player(0), EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddAction(t2, function B)
        set t1 = null
        set t2 = null
    endfunction


endscope
Name Type Is Array Initial Value
library Geysers /*


    */
uses /*

    */
TorrentArray  /*  https://www.hiveworkshop.com/threads/torrentarray-v1-0.291207/
    */
SpellEvent    /*


    |-------------------|
    | Spell Description |
    |-------------------|

        Creates a linear array of Geysers, sending tall columns of hot water that toss
        units within the vicinity upwards, and damage them over a short duration.

    */

    private struct Geyser extends array
        /*=================================================================*/
        /*                       Spell Configuration                       */
        /*=================================================================*/

        /*-----------------------------------------------------------------*/
        /*                      Static Configuration                       */
        /*-----------------------------------------------------------------*/
        /*******************************************************************/
        /*                       Basic Configuration                       */
        /*******************************************************************/
        /*
        *   Activation spell's rawcode
        */

        private static constant integer SPELL_ID                        = 'ATOR'
        /*
        *   Determines the type of spell event (See SpellEvent library for
        *   more options)
        */

        private static constant integer SPELL_EVENT_TYPE                = SpellEventType.EFFECT
        /*
        *   Determines if the tossed units are stunned or not (Normally,
        *   you should set this to true)
        */

        private static constant boolean PAUSE_TOSSED_TARGETS            = true
        /*
        *   Determines if the toss unit feature is enabled or not
        */

        private static constant boolean TOSS_TARGETS                    = true
        /*
        *   Determines if the torrent borders will be hidden from the
        *   enemies' vision
        */

        private static constant boolean BLIND_ENEMIES                   = true
        /*
        *   If true, the target ground's location will serve as the basis
        *   for where the location of the first torrent will deviate from
        *   but if false, the caster's location will serve as the basis
        *   of deviation instead. This might be good to enable if you're
        *   configuring a circular of a spiral array of torrents so that
        *   it will have its center/origin at the target location instead
        *   of the caster's.
        */

        private static constant boolean TARGET_IS_CENTER                = false
        /*
        *   Determines the attack type of the damage by the torrent
        */

        private static constant attacktype ATTACK_TYPE                  = ATTACK_TYPE_HERO
        /*
        *   Determines the damage type of the damage by the torrent
        */

        private static constant damagetype DAMAGE_TYPE                  = DAMAGE_TYPE_NORMAL
        /*
        *   Determines the weapon type of the damage by the torrent
        */

        private static constant weapontype WEAPON_TYPE                  = null

        /*******************************************************************/
        /*                        Spell Aesthetics                         */
        /*******************************************************************/
        /*
        *   Determines the model of the SFX when a torrent is set up
        */

        private static constant string SUMMON_SFX_MODEL                 = ""
        /*
        *   Determines the SFXModel used for the torrent
        */

        private static constant string EXPLOSION_SFX_MODEL              = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
        /*
        *   Determines the SFXModel used for the spell borders
        */

        private static constant string BORDER_SFX_MODEL                 = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"
        /*
        *   Determines the SFXModel used to attach to the tossed targets
        */

        private static constant string TOSSED_TARGET_SFX_MODEL          = ""
        /*
        *   Determines the SFXModel used to attach to the affected units
        *   upon landing
        */

        private static constant string LANDED_TARGET_SFX_MODEL          = "Abilities\\Spells\\Items\\AIob\\AIobSpecialArt.mdl"
        /*
        *   Determines the attachment point for tossedTargetSfxModel
        */

        private static constant string TOSSED_TARGET_SFX_ATTACH_POINT   = ""
        /*
        *   Determines the attachment point for landedTargetSfxModel
        */

        private static constant string LANDED_TARGET_SFX_ATTACH_POINT   = "overhead"
        /*
        *   Determines the animation played by the affected units when
        *   being thrown on air (Default value is an empty string)
        */

        private static constant string TOSSED_TARGET_ANIMATION          = ""

        /*-----------------------------------------------------------------*/
        /*                     Dynamic Configuration                       */
        /*-----------------------------------------------------------------*/
        /*******************************************************************/
        /*                        Spell Mechanics                          */
        /*******************************************************************/
        /*
        *   Determines the torrent count
        */

        private static constant method torrentCount takes integer level returns integer
            return 10 + 0*level
        endmethod
        /*
        *   Determines the delay of the first torrent's explosion after
        *   its appearance
        */

        private static constant method explosionDelay takes integer level returns real
            return 2.00 + 0.00*level
        endmethod
        /*
        *   Determines the time interval of the next torrent release
        */

        private static constant method explosionInterval takes integer level returns real
            return 0.25 + 0.00*level
        endmethod
        /*
        *   Determines the distance between the torrents along the axis of the
        *   line between the caster and the target
        */

        private static constant method torrentPositionOffset takes integer level returns real
            return 300.00 + 0.00*level
        endmethod
        /*
        *   Determines the angle deflection of the torrents' location
        *   with respect to the caster as the origin
        */

        private static constant method torrentPositionAngleDeflection takes integer level returns real
            return 0.00 + 0.00*level
        endmethod
        /*
        *   Determines the duration of the torrent
        */

        private static constant method tossDuration takes integer level returns real
            return 1.75 + 0.00*level
        endmethod
        /*
        *   Determines the radius area affected by torrent
        */

        private static constant method torrentRadius takes integer level returns real
            return 200.00 + 0.00*level
        endmethod
        /*
        *   Determines the damage done by the torrent upon its explosion
        */

        private static constant method explosionDamage takes integer level returns real
            return 75.00 + 25.00*level
        endmethod
        /*
        *   Determines the damage dealt per second to the affected units
        */

        private static constant method damagePerSecond takes integer level returns real
            return 5.00 + 5.00*level
        endmethod
        /*
        *   Determines the maximum height of the thrown units
        */

        private static constant method torrentHeight takes integer level returns real
            return 650.00 + 0.00*level
        endmethod
        /*
        *   Determines the distance of the border special effect from
        *   each other
        */

        private static constant method borderPositionOffset takes integer level returns real
            return 60.00 + 0.00*level
        endmethod
        /*
        *   The code for filtering target units
        */

        private static method targetFilter takes unit target, unit caster returns boolean
            return IsUnitEnemy(target, GetOwningPlayer(caster)) and /*
                 */
not IsUnitType(target, UNIT_TYPE_STRUCTURE) and /*
                 */
not IsUnitType(target, UNIT_TYPE_ETHEREAL)
        endmethod

        /*-----------------------------------------------------------------*/
        /*                    Extension Configuration                      */
        /*   (The following methods are optional, it's better to delete    */
        /*                  them if they're not needed)                    */
        /*-----------------------------------------------------------------*/
        /*
        *   The code that will run upon each torrent's explosion
        */

        private static method onTorrentExplode takes Torrent this, integer level returns nothing
            /*
            *   ->  Put onExplode responses here
            */

        endmethod
        /*
        *   The code that will run when each torrent vanishes
        */

        private static method onTorrentVanish takes Torrent this, integer level returns nothing
            /*
            *   ->  Put onVanish responses here
            */

        endmethod

        /*=================================================================*/
        /*                   End of Spell Configuration                    */
        /*=================================================================*/

        implement TorrentArrayConfiguration

    endstruct


endlibrary
library MeteorShower /*


    */
uses /*

    */
TorrentArray  /*  https://www.hiveworkshop.com/threads/torrentarray-v1-0.291207/
    */
SpellEvent    /*
    */
StunSystem    /*  https://www.hiveworkshop.com/threads/system-stun.196749/


    |-------------------|
    | Spell Description |
    |-------------------|

        Calls down a circular array of meteors that damage and stun units within their
        area of impact. Units are also burned afterwards, taking damage over time.

    */

    private struct MeteorShower extends array
        /*=================================================================*/
        /*                       Spell Configuration                       */
        /*=================================================================*/

        /*-----------------------------------------------------------------*/
        /*                      Static Configuration                       */
        /*-----------------------------------------------------------------*/
        /*******************************************************************/
        /*                       Basic Configuration                       */
        /*******************************************************************/
        /*
        *   Activation spell's rawcode
        */

        private static constant integer SPELL_ID                        = 'AMET'
        /*
        *   Determines the type of spell event (See SpellEvent library for
        *   more options)
        */

        private static constant integer SPELL_EVENT_TYPE                = SpellEventType.EFFECT
        /*
        *   Determines if the tossed units are stunned or not (Normally,
        *   you should set this to true)
        */

        private static constant boolean PAUSE_TOSSED_TARGETS            = false
        /*
        *   Determines if the toss unit feature is enabled or not
        */

        private static constant boolean TOSS_TARGETS                    = true
        /*
        *   Determines if the torrent borders will be hidden from the
        *   enemies' vision
        */

        private static constant boolean BLIND_ENEMIES                   = false
        /*
        *   If true, the target ground's location will serve as the basis
        *   for where the location of the first torrent will deviate from
        *   but if false, the caster's location will serve as the basis
        *   of deviation instead. This might be good to enable if you're
        *   configuring a circular of a spiral array of torrents so that
        *   it will have its center/origin at the target location instead
        *   of the caster's.
        */

        private static constant boolean TARGET_IS_CENTER                = true
        /*
        *   Determines the attack type of the damage by the torrent
        */

        private static constant attacktype ATTACK_TYPE                  = ATTACK_TYPE_HERO
        /*
        *   Determines the damage type of the damage by the torrent
        */

        private static constant damagetype DAMAGE_TYPE                  = DAMAGE_TYPE_NORMAL
        /*
        *   Determines the weapon type of the damage by the torrent
        */

        private static constant weapontype WEAPON_TYPE                  = null

        /*******************************************************************/
        /*                        Spell Aesthetics                         */
        /*******************************************************************/
        /*
        *   Determines the model of the SFX when a torrent is set up
        */

        private static constant string SUMMON_SFX_MODEL                 = "Units\\Demon\\Infernal\\InfernalBirth.mdl"
        /*
        *   Determines the SFXModel used for the torrent
        */

        private static constant string EXPLOSION_SFX_MODEL              = ""
        /*
        *   Determines the SFXModel used for the spell borders
        */

        private static constant string BORDER_SFX_MODEL                 = "Environment\\UndeadBuildingFire\\UndeadLargeBuildingFire2.mdl"
        /*
        *   Determines the SFXModel used to attach to the tossed targets
        */

        private static constant string TOSSED_TARGET_SFX_MODEL          = "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl"
        /*
        *   Determines the SFXModel used to attach to the affected units
        *   upon landing
        */

        private static constant string LANDED_TARGET_SFX_MODEL          = ""
        /*
        *   Determines the attachment point for tossedTargetSfxModel
        */

        private static constant string TOSSED_TARGET_SFX_ATTACH_POINT   = "origin"
        /*
        *   Determines the attachment point for landedTargetSfxModel
        */

        private static constant string LANDED_TARGET_SFX_ATTACH_POINT   = ""
        /*
        *   Determines the animation played by the affected units when
        *   being thrown on air (Default value is an empty string)
        */

        private static constant string TOSSED_TARGET_ANIMATION          = ""

        /*-----------------------------------------------------------------*/
        /*                     Dynamic Configuration                       */
        /*-----------------------------------------------------------------*/
        /*******************************************************************/
        /*                        Spell Mechanics                          */
        /*******************************************************************/
        /*
        *   Determines the torrent count
        */

        private static constant method torrentCount takes integer level returns integer
            return 10 + 0*level
        endmethod
        /*
        *   Determines the delay of the first torrent's explosion after
        *   its appearance
        */

        private static constant method explosionDelay takes integer level returns real
            return 1.00 + 0.00*level
        endmethod
        /*
        *   Determines the time interval of the next torrent release
        */

        private static constant method explosionInterval takes integer level returns real
            return 0.00 + 0.00*level
        endmethod
        /*
        *   Determines the distance between the torrents along the axis of the
        *   line between the caster and the target
        */

        private static constant method torrentPositionOffset takes integer level returns real
            return 0.00 + 0.00*level
        endmethod
        /*
        *   Determines the angle deflection of the torrents' location
        *   with respect to the caster as the origin
        */

        private static constant method torrentPositionAngleDeflection takes integer level returns real
            return 36.00 + 0.00*level
        endmethod
        /*
        *   Determines the duration of the torrent
        */

        private static constant method tossDuration takes integer level returns real
            return 3.00 + 2.00*level
        endmethod
        /*
        *   Determines the radius area affected by torrent
        */

        private static constant method torrentRadius takes integer level returns real
            return 175.00 + 0.00*level
        endmethod
        /*
        *   Determines the damage done by the torrent upon its explosion
        */

        private static constant method explosionDamage takes integer level returns real
            return 75.00 + 25.00*level
        endmethod
        /*
        *   Determines the damage dealt per second to the affected units
        */

        private static constant method damagePerSecond takes integer level returns real
            return 20.00 + 0.00*level
        endmethod
        /*
        *   Determines the maximum height of the thrown units
        */

        private static constant method torrentHeight takes integer level returns real
            return 0.00 + 0.00*level
        endmethod
        /*
        *   Determines the distance of the border special effect from
        *   each other
        */

        private static constant method borderPositionOffset takes integer level returns real
            return 60.00 + 0.00*level
        endmethod
        /*
        *   Determines the stun duration
        */

        private static constant method stunDuration takes integer level returns real
            return 0.50 + 0.50*level
        endmethod
        /*
        *   The code for filtering target units
        */

        private static method targetFilter takes unit target, unit caster returns boolean
            return UnitAlive(target) and /*
                 */
IsUnitEnemy(target, GetOwningPlayer(caster)) and /*
                 */
not IsUnitType(target, UNIT_TYPE_STRUCTURE) and /*
                 */
not IsUnitType(target, UNIT_TYPE_ETHEREAL)
        endmethod

        /*-----------------------------------------------------------------*/
        /*                    Extension Configuration                      */
        /*   (The following methods are optional, it's better to delete    */
        /*                  them if they're not needed)                    */
        /*-----------------------------------------------------------------*/
        /*
        *   The code that will run upon each torrent's explosion
        */

        private static method onTorrentExplode takes Torrent this, integer level returns nothing
            local group targetGroup = this.targetGroup
            local unit u
            loop
                set u = FirstOfGroup(targetGroup)
                exitwhen u == null
                /* It's okay to empty this group because it will be
                   refilled later                                       */

                call GroupRemoveUnit(targetGroup, u)
                call Stun.apply(u, stunDuration(level), false)
            endloop
            set targetGroup = null
        endmethod
        /*
        *   The code that will run when each torrent vanishes
        */

        private static method onTorrentVanish takes Torrent this, integer level returns nothing
            /*
            *   ->  Put onVanish responses here
            */

        endmethod

        /*=================================================================*/
        /*                   End of Spell Configuration                    */
        /*=================================================================*/

        implement TorrentArrayConfiguration

    endstruct


endlibrary
library EruptionHelix /*


    */
uses /*

    */
TorrentArray  /*  https://www.hiveworkshop.com/threads/torrentarray-v1-0.291207/
    */
SpellEvent    /*
    */
StunSystem    /*  https://www.hiveworkshop.com/threads/system-stun.196749/


    |-------------------|
    | Spell Description |
    |-------------------|

        Creates a spiral chain of explosions that damage and stun units in their
        vicinity

    */

    private struct EruptionHelix extends array
        /*=================================================================*/
        /*                       Spell Configuration                       */
        /*=================================================================*/

        /*-----------------------------------------------------------------*/
        /*                      Static Configuration                       */
        /*-----------------------------------------------------------------*/
        /*******************************************************************/
        /*                       Basic Configuration                       */
        /*******************************************************************/
        /*
        *   Activation spell's rawcode
        */

        private static constant integer SPELL_ID                        = 'AERP'
        /*
        *   Determines the type of spell event (See SpellEvent library for
        *   more options)
        */

        private static constant integer SPELL_EVENT_TYPE                = SpellEventType.EFFECT
        /*
        *   Determines if the tossed units are stunned or not (Normally,
        *   you should set this to true)
        */

        private static constant boolean PAUSE_TOSSED_TARGETS            = false
        /*
        *   Determines if the toss unit feature is enabled or not
        */

        private static constant boolean TOSS_TARGETS                    = false
        /*
        *   Determines if the torrent borders will be hidden from the
        *   enemies' vision
        */

        private static constant boolean BLIND_ENEMIES                   = false
        /*
        *   If true, the target ground's location will serve as the basis
        *   for where the location of the first torrent will deviate from
        *   but if false, the caster's location will serve as the basis
        *   of deviation instead. This might be good to enable if you're
        *   configuring a circular of a spiral array of torrents so that
        *   it will have its center/origin at the target location instead
        *   of the caster's.
        */

        private static constant boolean TARGET_IS_CENTER                = true
        /*
        *   Determines the attack type of the damage by the torrent
        */

        private static constant attacktype ATTACK_TYPE                  = ATTACK_TYPE_HERO
        /*
        *   Determines the damage type of the damage by the torrent
        */

        private static constant damagetype DAMAGE_TYPE                  = DAMAGE_TYPE_NORMAL
        /*
        *   Determines the weapon type of the damage by the torrent
        */

        private static constant weapontype WEAPON_TYPE                  = null

        /*******************************************************************/
        /*                        Spell Aesthetics                         */
        /*******************************************************************/
        /*
        *   Determines the model of the SFX when a torrent is set up
        */

        private static constant string SUMMON_SFX_MODEL                 = "Abilities\\Spells\\Human\\SpellSteal\\SpellStealMissile.mdl"
        /*
        *   Determines the SFXModel used for the torrent
        */

        private static constant string EXPLOSION_SFX_MODEL              = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
        /*
        *   Determines the SFXModel used for the spell borders
        */

        private static constant string BORDER_SFX_MODEL                 = ""
        /*
        *   Determines the SFXModel used to attach to the tossed targets
        */

        private static constant string TOSSED_TARGET_SFX_MODEL          = "Abilities\\Spells\\Items\\AIfb\\AIfbSpecialArt.mdl"
        /*
        *   Determines the SFXModel used to attach to the affected units
        *   upon landing
        */

        private static constant string LANDED_TARGET_SFX_MODEL          = ""
        /*
        *   Determines the attachment point for tossedTargetSfxModel
        */

        private static constant string TOSSED_TARGET_SFX_ATTACH_POINT   = "head"
        /*
        *   Determines the attachment point for landedTargetSfxModel
        */

        private static constant string LANDED_TARGET_SFX_ATTACH_POINT   = ""
        /*
        *   Determines the animation played by the affected units when
        *   being thrown on air (Default value is an empty string)
        */

        private static constant string TOSSED_TARGET_ANIMATION          = ""

        /*-----------------------------------------------------------------*/
        /*                     Dynamic Configuration                       */
        /*-----------------------------------------------------------------*/
        /*******************************************************************/
        /*                        Spell Mechanics                          */
        /*******************************************************************/
        /*
        *   Determines the torrent count
        */

        private static constant method torrentCount takes integer level returns integer
            return 80 + 0*level
        endmethod
        /*
        *   Determines the delay of the first torrent's explosion after
        *   its appearance
        */

        private static constant method explosionDelay takes integer level returns real
            return 0.00 + 0.00*level
        endmethod
        /*
        *   Determines the time interval of the next torrent release
        */

        private static constant method explosionInterval takes integer level returns real
            return 0.02 + 0.00*level
        endmethod
        /*
        *   Determines the distance between the torrents along the axis of the
        *   line between the caster and the target
        */

        private static constant method torrentPositionOffset takes integer level returns real
            return 10.00 + 0.00*level
        endmethod
        /*
        *   Determines the angle deflection of the torrents' location
        *   with respect to the caster as the origin
        */

        private static constant method torrentPositionAngleDeflection takes integer level returns real
            return -18.00 + 0.00*level
        endmethod
        /*
        *   Determines the duration of the torrent
        */

        private static constant method tossDuration takes integer level returns real
            return 0.00 + 0.00*level
        endmethod
        /*
        *   Determines the radius area affected by torrent
        */

        private static constant method torrentRadius takes integer level returns real
            return 100.00 + 0.00*level
        endmethod
        /*
        *   Determines the damage done by the torrent upon its explosion
        */

        private static constant method explosionDamage takes integer level returns real
            return 150.00 + 50.00*level
        endmethod
        /*
        *   Determines the damage dealt per second to the affected units
        */

        private static constant method damagePerSecond takes integer level returns real
            return 0.00 + 0.00*level
        endmethod
        /*
        *   Determines the maximum height of the thrown units
        */

        private static constant method torrentHeight takes integer level returns real
            return 0.00 + 0.00*level
        endmethod
        /*
        *   Determines the distance of the border special effect from
        *   each other
        */

        private static constant method borderPositionOffset takes integer level returns real
            return 60.00 + 0.00*level
        endmethod
        /*
        *   Determines the stun duration
        */

        private static constant method stunDuration takes integer level returns real
            return 0.50 + 0.50*level
        endmethod
        /*
        *   The code for filtering target units
        */

        private static method targetFilter takes unit target, unit caster returns boolean
            return UnitAlive(target) and /*
                 */
IsUnitEnemy(target, GetOwningPlayer(caster)) and /*
                 */
not IsUnitType(target, UNIT_TYPE_STRUCTURE) and /*
                 */
not IsUnitType(target, UNIT_TYPE_ETHEREAL)
        endmethod

        /*-----------------------------------------------------------------*/
        /*                    Extension Configuration                      */
        /*   (The following methods are optional, it's better to delete    */
        /*                  them if they're not needed)                    */
        /*-----------------------------------------------------------------*/
        /*
        *   The code that will run upon each torrent's explosion
        */

        private static method onTorrentExplode takes Torrent this, integer level returns nothing
            local group targetGroup = this.targetGroup
            local unit u
            loop
                set u = FirstOfGroup(targetGroup)
                exitwhen u == null
                /* It's okay to empty this group because it will be
                   refilled later                                       */

                call GroupRemoveUnit(targetGroup, u)
                call Stun.apply(u, stunDuration(level), false)
            endloop
            set targetGroup = null
        endmethod
        /*
        *   The code that will run when each torrent vanishes
        */

        private static method onTorrentVanish takes Torrent this, integer level returns nothing
            /*
            *   ->  Put onVanish responses here
            */

        endmethod

        /*=================================================================*/
        /*                   End of Spell Configuration                    */
        /*=================================================================*/

        implement TorrentArrayConfiguration

    endstruct


endlibrary
library TorrentArray /* v1.1b


    */
requires /*

    */
TorrentSystem                /*  https://www.hiveworkshop.com/threads/torrentsystem-v2-0b.305301/
    */
SpellEvent                   /*  https://www.hiveworkshop.com/threads/spellevent.301895/
    */
UniqueList                   /*  https://github.com/nestharus/JASS/blob/master/jass/Data%20Structures/UniqueList/script.j
    */
Table                        /*  https://www.hiveworkshop.com/threads/snippet-new-table.188084/
    */
optional WorldBounds         /*  https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
    */
optional ResourcePreloader   /*  https://www.hiveworkshop.com/threads/snippet-resource-preloader.287358/


    Resource Link: https://www.hiveworkshop.com/threads/torrentarray-v1-0.291207/

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

        Author:
            - AGD
        Dependencies:
            - Bribe (Table)
            - Nestharus (WorldBounds, UniqueList)
            - AGD (TorrentSystem, SpellEvent, ResourcePreloader)


    |-----------|
    | Importing |
    |-----------|

        - Import the dummy.mdx model to your map
        - Copy the dummy unit and the Torrent ability in Object Editor
        - Copy the trigger category containing this script and its dependencies
        - Configure the spell's data in the default configuration library or alternatively,
          make your own one


    |--------------------|
    | Configuration Tips |
    |--------------------|

        The configuration allows you to do many possible tricks. One of these is that it
        allows you to configure the shape of the torrent array.

        1. Linear Torrent Array

            This is the default setup of the configuration in the Geysers sample library. If
            (TARGET_IS_CENTER) is true, the location of the first torrent is twice the
            distance between the caster and the target ground, else, the location is exactly
            the target ground.

            Conditions:

            - torrentCount(level) > 1
            - torrentPositionOffset(level) != 0.00
            - torrentPositionAngleDeflection(level) == 0.00

        2. Circular Torrent Array

            If (TARGET_IS_CENTER) is true, the center of the circle is the target ground,
            else, the caster's position.

            Conditions:

            - torrentCount(level) > 1
            - torrentPositionOffset(level) == 0.00
            - torrentPositionAngleDeflection(level) == 360.00/torrentCount(level)

        3. Spiral Torrent Array

            If (TARGET_IS_CENTER) is true, the center of the spiral is the target ground,
            else, the caster's position.

            Conditions:

            - torrentCount(level) > 1
            - torrentPositionOffset(level) != 0.00
            - torrentPositionAngleDeflection(level) != 0.00


    |-------------------|
    | Configuration API |
    |-------------------|

        - Each new configuration must be inside a struct
        - If you use a private struct inside a library, make sure to make that library
          require this library to ensure proper initialization order

        struct YourSpellConfiguration extends array
            /*
            *   Activation spell's rawcode
            */

            private static constant integer SPELL_ID
            /*
            *   Determines the type of spell event (See SpellEvent library for
            *   more options)
            */

            private static constant integer SPELL_EVENT_TYPE
            /*
            *   Determines if the tossed units are stunned or not (Normally,
            *   you should set this to true)
            */

            private static constant boolean PAUSE_TOSSED_TARGETS
            /*
            *   Determines if the toss unit feature is enabled or not
            */

            private static constant boolean TOSS_TARGETS
            /*
            *   Determines if the torrent borders will be hidden from the
            *   enemies' vision
            */

            private static constant boolean BLIND_ENEMIES
            /*
            *   If true, the target ground's location will serve as the basis
            *   for where the location of the first torrent will deviate from
            *   but if false, the caster's location will serve as the basis
            *   of deviation instead. This might be good to enable if you're
            *   configuring a circular of a spiral array of torrents so that
            *   it will have its center/origin at the target location instead
            *   of the caster's.
            */

            private static constant boolean TARGET_IS_CENTER
            /*
            *   Determines the attack type of the damage by the torrent
            */

            private static constant attacktype ATTACK_TYPE
            /*
            *   Determines the damage type of the damage by the torrent
            */

            private static constant damagetype DAMAGE_TYPE
            /*
            *   Determines the weapon type of the damage by the torrent
            */

            private static constant weapontype WEAPON_TYPE
            /*
            *   Determines the model of the SFX when a torrent is set up
            */

            private static constant string SUMMON_SFX_MODEL
            /*
            *   Determines the SFXModel used for the torrent
            */

            private static constant string EXPLOSION_SFX_MODEL
            /*
            *   Determines the SFXModel used for the spell borders
            */

            private static constant string BORDER_SFX_MODEL
            /*
            *   Determines the SFXModel used to attach to the tossed targets
            */

            private static constant string TOSSED_TARGET_SFX_MODEL
            /*
            *   Determines the SFXModel used to attach to the affected units
            *   upon landing
            */

            private static constant string LANDED_TARGET_SFX_MODEL
            /*
            *   Determines the attachment point for tossedTargetSfxModel
            */

            private static constant string TOSSED_TARGET_SFX_ATTACH_POINT
            /*
            *   Determines the attachment point for landedTargetSfxModel
            */

            private static constant string LANDED_TARGET_SFX_ATTACH_POINT
            /*
            *   Determines the animation played by the affected units when
            *   being thrown on air (Default value is an empty string)
            */

            private static constant string TOSSED_TARGET_ANIMATION
            /*
            *   Determines the torrent count
            */

            private static constant method torrentCount takes integer level returns integer
            /*
            *   Determines the delay of the first torrent's explosion after
            *   its appearance
            */

            private static constant method explosionDelay takes integer level returns real
            /*
            *   Determines the time interval of the next torrent release
            */

            private static constant method explosionInterval takes integer level returns real
            /*
            *   Determines the distance between the torrents along the axis of the
            *   line between the caster and the target
            */

            private static constant method torrentPositionOffset takes integer level returns real
            /*
            *   Determines the angle deflection of the torrents' location
            *   with respect to the caster as the origin
            */

            private static constant method torrentPositionAngleDeflection takes integer level returns real
            /*
            *   Determines the duration of the torrent
            */

            private static constant method tossDuration takes integer level returns real
            /*
            *   Determines the radius area affected by torrent
            */

            private static constant method torrentRadius takes integer level returns real
            /*
            *   Determines the damage done by the torrent upon its appearance
            */

            private static constant method explosionDamage takes integer level returns real
            /*
            *   Determines the damage dealt per second to the affected units
            */

            private static constant method damagePerSecond takes integer level returns real
            /*
            *   Determines the maximum height of the thrown units
            */

            private static constant method torrentHeight takes integer level returns real
            /*
            *   Determines the distance of the border special effect from
            *   each other
            */

            private static constant method borderPositionOffset takes integer level returns real
            /*
            *   The code for filtering target units
            */

            private static method targetFilter takes unit target, unit caster returns boolean
            /*-----------------------------------------------------------------*/
            /*                 Optional Configuration Methods                  */
            /*-----------------------------------------------------------------*/
            /*
            *   The code that will run upon each torrent's explosion
            */

            private static method onTorrentExplode takes Torrent this, integer level returns nothing
            /*
            *   The code that will run when each torrent vanishes
            */

            private static method onTorrentVanish takes Torrent this, integer level returns nothing

            implement TorrentArrayConfiguration

        endstruct


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

    private keyword Init

    private struct Configuration extends array

        static integer torrentCount = 0
        static boolean pauseTossedTargets
        static boolean tossTargets
        static boolean blindEnemies
        static boolean targetIsCenter
        static real explosionDelay
        static real explosionInterval
        static real torrentPositionOffset
        static real torrentPositionAngleDeflection
        static real tossDuration
        static real torrentRadius
        static real explosionDamage
        static real damagePerSecond
        static real torrentHeight
        static real borderPositionOffset
        static attacktype attackType
        static damagetype damageType
        static weapontype weaponType
        static boolexpr filterExpr
        static boolexpr torrentExplosionHandlerExpr
        static boolexpr torrentVanishHandlerExpr
        static string summonSfxModel
        static string explosionSfxModel
        static string borderSfxModel
        static string tossedTargetSfxModel
        static string landedTargetSfxModel
        static string tossedTargetSfxAttachPoint
        static string landedTargetSfxAttachPoint
        static string tossedTargetAnimation

        static method operator torrentExplosionHandler= takes code handlerFunction returns nothing
            set torrentExplosionHandlerExpr = Filter(handlerFunction)
            return
        endmethod
        static method operator torrentVanishHandler= takes code handlerFunction returns nothing
            set torrentVanishHandlerExpr = Filter(handlerFunction)
            return
        endmethod

    endstruct

    private struct ConfigurationList extends array
        thistype current
        implement UniqueList
    endstruct

    private struct TorrentList extends array
        implement UniqueList
    endstruct

    private struct TorrentArray extends array

        static if not LIBRARY_WorldBounds then
            private static real minX
            private static real maxX
            private static real minY
            private static real maxY
        endif

        private static Table table
        private static trigger evaluator = CreateTrigger()
        private static thistype configurationCount = 0
        private TorrentList current
        private boolexpr configurationExpr
        private boolean tossTargetUnits
        private real timerInterval
        readonly static thistype triggerInstance = 0
        readonly integer level

        private static method getBoundedValue takes real value, real min, real max returns real
            if value < min then
                return min
            elseif value > max then
                return max
            endif
            return value
        endmethod

        private static method periodic takes nothing returns nothing
            local timer expired = GetExpiredTimer()
            local thistype this = table[-GetHandleId(expired)]
            local TorrentList current = this.current
            set triggerInstance = this
            call Torrent(current).explode(this.tossTargetUnits)
            set triggerInstance = 0
            if current.next == 0 then
                /* If the last Torrent in the list has exploded, destroy
                   this spell instance.                                  */

                call TorrentList(this).destroy()
                call PauseTimer(expired)
                call DestroyTimer(expired)
            else
                set this.current = current.next
            endif
            set expired = null
        endmethod

        private static method beginChainExplosion takes nothing returns nothing
            local timer expired = GetExpiredTimer()
            call TimerStart(expired, thistype(table[-GetHandleId(expired)]).timerInterval, true, function thistype.periodic)
            set expired = null
        endmethod

        private static method onSpellEffect takes nothing returns nothing
            local thistype this     = TorrentList.create()
            local timer spellTimer  = CreateTimer()
            local integer borders   = R2I(2.00*Configuration.torrentRadius*bj_PI/Configuration.borderPositionOffset)
            local real casterX      = GetUnitX(Spell.CASTER)
            local real casterY      = GetUnitY(Spell.CASTER)
            local real dx           = Spell.TARGET_X - casterX
            local real dy           = Spell.TARGET_Y - casterY
            local real dr           = SquareRoot(dx*dx + dy*dy)
            local real angle        = 0
            local real arcTan
            local real distance
            local real centerX
            local real centerY
            local real x
            local real y
            local integer i
            local Torrent torrent
            set this.level = Spell.LEVEL
            set this.timerInterval = Configuration.explosionInterval
            set this.tossTargetUnits = Configuration.tossTargets
            if Configuration.blindEnemies and IsUnitEnemy(Spell.CASTER, GetLocalPlayer()) then
                /* Hide the torrent borders from enemies' vision */
                set Configuration.borderSfxModel = ""
            endif
            if dr == 0.00 then
                set arcTan = GetUnitFacing(Spell.CASTER)*bj_DEGTORAD
            else
                set arcTan = Atan2(dy, dx)
            endif
            /* Setup torrents in their designated location */
            loop
                exitwhen Configuration.torrentCount == 0
                set Configuration.torrentCount = Configuration.torrentCount - 1
                set angle = arcTan + (Configuration.torrentPositionAngleDeflection*Configuration.torrentCount)*bj_DEGTORAD
                set distance = dr + Configuration.torrentPositionOffset*Configuration.torrentCount
                if Configuration.targetIsCenter then
                    set centerX = Spell.TARGET_X + distance*Cos(angle)
                    set centerY = Spell.TARGET_Y + distance*Sin(angle)
                else
                    set centerX = casterX + distance*Cos(angle)
                    set centerY = casterY + distance*Sin(angle)
                endif
                /* Implement map bounds */
                static if LIBRARY_WorldBounds then
                    set centerX = getBoundedValue(centerX, WorldBounds.minX, WorldBounds.maxX)
                    set centerY = getBoundedValue(centerY, WorldBounds.minY, WorldBounds.maxY)
                else
                    set centerX = getBoundedValue(centerX, minX, maxX)
                    set centerY = getBoundedValue(centerY, minY, maxY)
                endif
                /* Create the torrent and setup the neccessary data */
                set torrent                             = Torrent.create(centerX, centerY, Configuration.torrentRadius, borders, Configuration.explosionSfxModel, Configuration.borderSfxModel, Configuration.summonSfxModel)
                set torrent.caster                      = Spell.CASTER
                set torrent.explosionDamage             = Configuration.explosionDamage
                set torrent.damagePerSecond             = Configuration.damagePerSecond
                set torrent.height                      = Configuration.torrentHeight
                set torrent.duration                    = Configuration.tossDuration
                set torrent.attackType                  = Configuration.attackType
                set torrent.damageType                  = Configuration.damageType
                set torrent.weaponType                  = Configuration.weaponType
                set torrent.filterExpr                  = Configuration.filterExpr
                set torrent.pauseTargets                = Configuration.pauseTossedTargets
                set torrent.tossedTargetSfxModel        = Configuration.tossedTargetSfxModel
                set torrent.landedTargetSfxModel        = Configuration.landedTargetSfxModel
                set torrent.tossedTargetSfxAttachPoint  = Configuration.tossedTargetSfxAttachPoint
                set torrent.landedTargetSfxAttachPoint  = Configuration.landedTargetSfxAttachPoint
                set torrent.tossedTargetAnimation       = Configuration.tossedTargetAnimation
                call torrent.registerExplosionHandler(Configuration.torrentExplosionHandlerExpr)
                call torrent.registerVanishHandler(Configuration.torrentVanishHandlerExpr)
                call TorrentList(this).push(torrent)
            endloop
            set this.current = TorrentList(this).first
            set table[-GetHandleId(spellTimer)] = this
            call TimerStart(spellTimer, Configuration.explosionDelay - Configuration.explosionInterval, false, function thistype.beginChainExplosion)
            set spellTimer = null
        endmethod

        /* After casting, determine the ability casted and then loop
           through all its configuration sets                       */

        private static method onCast takes nothing returns nothing
            local ConfigurationList configList = table[Spell.ABILITY_ID]
            if configList.current == 0 then
                set configList.current = configList.first
            else
                set configList.current = configList.next
            endif
            /* Evaluate the proper configuration set */
            call TriggerAddCondition(evaluator, thistype(configList.current).configurationExpr)
            call TriggerEvaluate(evaluator)
            call TriggerClearConditions(evaluator)
            if configList.current.next == 0 then
                set configList.current = 0
            endif
            /* Proceed if the torrent count is 1 or more */
            if Configuration.torrentCount > 0 then
                call onSpellEffect()
            endif
        endmethod

        private static method init takes nothing returns nothing
            /* Setup Map Bounds for the spell */
            static if not LIBRARY_WorldBounds then
                local rect bounds = GetWorldBounds()
                set minX = GetRectMinX(bounds)
                set minY = GetRectMinY(bounds)
                set maxX = GetRectMaxX(bounds)
                set maxY = GetRectMaxY(bounds)
                call RemoveRect(bounds)
                set bounds = null
            endif
            set table = Table.create()
        endmethod
        implement Init

        static method create takes integer spellId, integer eventType, code configurationFunc returns thistype
            local ConfigurationList configList = table[spellId]
            if configList == 0 then
                set configList = ConfigurationList.create()
                set table[spellId] = configList
            endif
            set configurationCount = configurationCount + 1
            call configList.enqueue(configurationCount)
            set configurationCount.configurationExpr = Filter(configurationFunc)
            call Spell[spellId].registerEventHandler(eventType, function thistype.onCast)
            return configurationCount
        endmethod

    endstruct

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

    /*
    *   Configuration Module
    *   - Transforms a struct into a TorrentArray configuration
    */

    module TorrentArrayConfiguration

        private static method targetFilterFunc takes nothing returns boolean
            return targetFilter(GetFilterUnit(), Torrent.triggerInstance.caster)
        endmethod

        static if thistype.onTorrentExplode.exists then
            private static method explosionHandler takes nothing returns nothing
                call onTorrentExplode(Torrent.triggerInstance, TorrentArray.triggerInstance.level)
            endmethod
        endif

        static if thistype.onTorrentVanish.exists then
            private static method vanishHandler takes nothing returns nothing
                call onTorrentVanish(Torrent.triggerInstance, TorrentArray.triggerInstance.level)
            endmethod
        endif

        private static method configurationFunction takes nothing returns nothing
            set Configuration.torrentCount                       = R2I(1./(1./IMaxBJ(0, torrentCount(Spell.LEVEL))))
            set Configuration.pauseTossedTargets                 = PAUSE_TOSSED_TARGETS
            set Configuration.tossTargets                        = TOSS_TARGETS
            set Configuration.blindEnemies                       = BLIND_ENEMIES
            set Configuration.targetIsCenter                     = TARGET_IS_CENTER
            set Configuration.attackType                         = ATTACK_TYPE
            set Configuration.damageType                         = DAMAGE_TYPE
            set Configuration.weaponType                         = WEAPON_TYPE
            set Configuration.summonSfxModel                     = SUMMON_SFX_MODEL
            set Configuration.explosionSfxModel                  = EXPLOSION_SFX_MODEL
            set Configuration.borderSfxModel                     = BORDER_SFX_MODEL
            set Configuration.tossedTargetSfxModel               = TOSSED_TARGET_SFX_MODEL
            set Configuration.landedTargetSfxModel               = LANDED_TARGET_SFX_MODEL
            set Configuration.tossedTargetSfxAttachPoint         = TOSSED_TARGET_SFX_ATTACH_POINT
            set Configuration.landedTargetSfxAttachPoint         = LANDED_TARGET_SFX_ATTACH_POINT
            set Configuration.tossedTargetAnimation              = TOSSED_TARGET_ANIMATION
            set Configuration.explosionDelay                     = explosionDelay(Spell.LEVEL)
            set Configuration.explosionInterval                  = explosionInterval(Spell.LEVEL)
            set Configuration.torrentPositionOffset              = torrentPositionOffset(Spell.LEVEL)
            set Configuration.torrentPositionAngleDeflection     = torrentPositionAngleDeflection(Spell.LEVEL)
            set Configuration.tossDuration                       = tossDuration(Spell.LEVEL)
            set Configuration.torrentRadius                      = torrentRadius(Spell.LEVEL)
            set Configuration.explosionDamage                    = explosionDamage(Spell.LEVEL)
            set Configuration.damagePerSecond                    = damagePerSecond(Spell.LEVEL)
            set Configuration.torrentHeight                      = torrentHeight(Spell.LEVEL)
            set Configuration.borderPositionOffset               = borderPositionOffset(Spell.LEVEL)
            set Configuration.filterExpr                         = Filter(function thistype.targetFilterFunc)
            static if thistype.onTorrentExplode.exists then
                set Configuration.torrentExplosionHandler        = function thistype.explosionHandler
            endif
            static if thistype.onTorrentVanish.exists then
                set Configuration.torrentVanishHandler           = function thistype.vanishHandler
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            call TorrentArray.create(SPELL_ID, SPELL_EVENT_TYPE, function thistype.configurationFunction)
            static if LIBRARY_ResourcePreloader then
                call PreloadAbility(SPELL_ID)
                call PreloadEffect(SUMMON_SFX_MODEL)
                call PreloadEffect(EXPLOSION_SFX_MODEL)
                call PreloadEffect(BORDER_SFX_MODEL)
                call PreloadEffect(TOSSED_TARGET_SFX_MODEL)
                call PreloadEffect(LANDED_TARGET_SFX_MODEL)
            endif
        endmethod

    endmodule


endlibrary
TorrentArray library requirements
library TorrentSystem /* v2.0b


    */
uses /*

    */
StunSystem               /*  https://www.hiveworkshop.com/threads/system-stun.196749/
    */
Table                    /*  https://www.hiveworkshop.com/threads/snippet-new-table.188084/
    */
UnitDex                  /*  https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
    */
optional DummyRecycler   /*  https://www.hiveworkshop.com/threads/dummy-recycler-v1-25.277659/
    */
optional WorldBounds     /*  https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
    */
optional AutoFly         /*  https://www.hiveworkshop.com/threads/snippet-autofly.249422/


    Resource Link: https://www.hiveworkshop.com/threads/torrentsystem-v2-0b.305301/

    */
//! novjass

    |=========|
    | Credits |
    |=========|
    /*
        Author:
            - AGD
        Dependendies:
            - iAyanami (StunSystem)
            - Nestharus (WorldBounds)
            - Bribe (Table)
            - Flux (DummyRecycler)
            - TriggerHappy (UnitDex, AutoFly)
        Other:
            - Aniki (Finding bugs)
            - Vexorian (dummy.mdx)
            - IceFrog (Original Concept)

    */

    |===========|
    | Importing |
    |===========|
    /*
        1. Import the dummy.mdx model into your map.
        2. Copy the "TorrentSystem" library into your map.
        3. Configure the necessary data in the system configuration below.

    */

    |=====|
    | API |
    |=====|

        struct Torrent/*

          */
readonly static Torrent triggerInstance /*  The exploding/vanishing torrent instance (Only use inside a torrent event handler)
          */
readonly group targetGroup              /*  The group containing the targets of the <triggerInstance> (Only use inside a torrent event handler)
          */
unit caster                             /*  The unit considered as the owner of the Torrent
          */
real height                             /*  Height of the tossed targets
          */
real duration                           /*  Duration of the Torrent's explosion
          */
real explosionDamage                    /*  Initial damage caused by the Torrent
          */
real damagePerSecond                    /*  Damage dealt per second to the tossed targets
          */
attacktype attackType                   /*  AttackType of the damage from the Torrent
          */
damagetype damageType                   /*  DamageType of the damage from the Torrent
          */
weapontype weaponType                   /*  WeaponType of the damage from the Torrent
          */
boolexpr filterExpr                     /*  The boolexpr for filtering target units
          */
boolean pauseTargets                    /*  Determines if tossed targets are paused (Normally, you should set this to true)
          */
string targetStaticSfxModel             /*  Sfx model attached to the targets while being tossed
          */
string targetStaticSfxAttachPoint       /*  Sfx model attachment point for <targetStaticSfxModel>
          */
string landedTargetSfxModel             /*  Sfx model attached to the landed targets
          */
string landedTargetSfxAttachPoint       /*  Sfx model attachment point for <landedTargetSfxModel>
          */
string tossedTargetAnimation            /*  Animation played by the tossed units

          */
static method create takes real centerX, real centerY, real radius, integer borderCount, string model, string borderSfxModel, string onSummonSfxModel returns thistype/*
            - Creates a new Torrent instance
            - Arguments:
                * centerX - The x-coordinate of the Torrent's center
                * centerY - The y-coordinate of the Torrent's center
                * radius - The radius of the Torrent
                * borderCount - number of special effects created in the Torrent's circumference
                * model - Sfx model for the Torrent's explosion
                * borderSfxModel - Sfx model for the Torrent's borders
                * onSummonSfxModel - Sfx model for the Torrent's appearance/creation

          */
method destroy takes nothing returns nothing/*
            - Destroys a Torrent instance

          */
method explode takes boolean tossUnits returns nothing/*
            - Explodes a Torrent instance, dealing initial damage to the units within its area, and tossing them while damaging them over time if <tossUnits>
              is true
            - The Torrent instance is then destroyed afterwards

          */
method registerExplosionHandler takes boolexpr expr returns nothing/*
            - Registers a boolexpr that will run upon the Torrent's explosion (when explode() is called)

          */
method registerVanishHandler takes boolexpr expr returns nothing/*
            - Registers a boolexpr that will run upon the Torrent's disappearance, after tossed units are landed
            - This will not run when <tossUnits> is false


    */
//! endnovjass


    native UnitAlive takes unit u returns boolean

    /*
    This serves as the default target filter flags whenever a new
    Torrent instance is created. You can delete this module if
    it's not needed. By default, all units except the caster are
    allowed as targets.                                         */

    private module DefaultTargetFilters
        private static method targetFilter takes nothing returns boolean
            return GetFilterUnit() != Torrent.triggerInstance.caster
        endmethod
    endmodule

    private module TorrentModule

        /*================ SYSTEM CONFIGURATION ================*/
        /*
        The static timer timeout in real seconds
        Default value: 1.00/32.00 (32 FPS)                      */

        private static constant real TIMEOUT = 0.031250000
        /*
        The delay before dummy units are removed
        Make sure there's enough time for the special
        effects to play its death animation
        Default value: 2.00                                     */

        private static constant real DUMMY_DURATION = 2.00
        /*
        The owner of the dummy units
        Default value: Player(14)                               */

        private static constant player DUMMY_OWNER = Player(14)
        /*
        The rawcode of the dummy units                          */

        private static constant integer DUMMY_ID = 'h000'
        /*================ END OF CONFIGURATION ================*/

        static if not LIBRARY_WorldBounds then
            private static real mapMinX
            private static real mapMaxX
            private static real mapMinY
            private static real mapMaxY
        endif

        private static TableArray table
        private static timer staticTimer = CreateTimer()
        private static group tempGroup = CreateGroup()
        private static group refreshGroup
        private static boolean clearGroup
        private trigger onExplodeTrigger
        private trigger onVanishTrigger
        private integer borderCount
        private real radius
        private real centerX
        private real centerY
        private real elapsed
        private real damagePerInterval
        private group tossedGroup
        private string model
        private string borderModel
        private thistype prev
        private thistype next
        readonly static thistype triggerInstance = 0
        readonly group targetGroup
        boolexpr filterExpr
        boolean pauseTargets
        real height
        real duration
        real explosionDamage
        attacktype attackType
        damagetype damageType
        weapontype weaponType
        string tossedTargetSfxModel
        string landedTargetSfxModel
        string tossedTargetSfxAttachPoint
        string landedTargetSfxAttachPoint
        string tossedTargetAnimation
        unit caster

        private static method getBoundedValue takes real value, real min, real max returns real
            if value < min then
                return min
            elseif value > max then
                return max
            endif
            return value
        endmethod

        private method evaluate takes trigger whichTrigger returns nothing
            local thistype prevInstance = triggerInstance
            set triggerInstance = this
            call TriggerEvaluate(whichTrigger)
            set triggerInstance = prevInstance
        endmethod

        private method destroyBorders takes nothing returns nothing
            local integer count
            if .borderModel != null then
                set .borderModel = null
                /* Iterate through all border special effects and destroy
                   each one                                               */

                set count = .borderCount
                loop
                    exitwhen count == 0
                    call DestroyEffect(table[this].effect[count])
                    set count = count - 1
                endloop
            endif
            call DestroyEffect(table[this].effect[0])
            set .borderCount = 0
        endmethod

        method destroy takes nothing returns nothing
            /* Check if this torrent tosses its targets
               If yes, make sure it has finished its business
               before destroying                                */

            if .tossedGroup == null or .elapsed >= .duration then
                /* If the torrent borders haven't been destroyed yet, do so now */
                call .destroyBorders()
                call table[this].flush()
                static if LIBRARY_DummyRecycler then
                    call DummyAddRecycleTimer(GetUnitById(this), DUMMY_DURATION)
                else
                    call UnitApplyTimedLife(GetUnitById(this), 'BTLF', DUMMY_DURATION)
                endif
                call DestroyTrigger(.onExplodeTrigger)
                call DestroyTrigger(.onVanishTrigger)
                call DestroyGroup(.targetGroup)
                call DestroyGroup(.tossedGroup)
                set .pauseTargets = false
                set .caster = null
                set .centerX = 0.00
                set .centerY = 0.00
                set .radius = 0.00
                set .height = 0.00
                set .duration = 0.00
                set .explosionDamage = 0.00
                set .damagePerSecond = 0.00
                set .attackType = null
                set .damageType = null
                set .weaponType = null
                set .model = null
                set .tossedTargetSfxModel = null
                set .landedTargetSfxModel = null
                set .tossedTargetSfxAttachPoint = null
                set .landedTargetSfxAttachPoint = null
                set .filterExpr = null
                set .onExplodeTrigger = null
                set .onVanishTrigger = null
                set .targetGroup = null
                if .tossedGroup != null then
                    call DestroyGroup(.tossedGroup)
                    set .tossedGroup = null
                    set .elapsed = 0.00
                    set .next.prev = .prev
                    set .prev.next = .next
                    if thistype(0).next == 0 then
                        call PauseTimer(staticTimer)
                    endif
                endif
            endif
        endmethod

        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            local integer id
            local effect sfx
            local group g
            local unit u
            local unit dummy
            local real time
            local real speed
            local player owner
            local boolean recreate
            local boolean staticSfx
            /* Iterate through all currently exploding torrents and
               do actions                                           */

            loop
                exitwhen this == 0
                set .elapsed = .elapsed + TIMEOUT
                set owner = GetOwningPlayer(.caster)
                if .elapsed < .duration then
                    set time = 2.00*.elapsed/.duration
                    set speed = 2.00*.height
                    set dummy = GetUnitById(this)
                    /* Adjust units' fly height and deal damage over time */
                    loop
                        set u = FirstOfGroup(.tossedGroup)
                        exitwhen u == null
                        call GroupRemoveUnit(.tossedGroup, u)
                        call GroupAddUnit(tempGroup, u)
                        if speed > 0.00 then
                            call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u) + speed*time - 0.5*speed*time*time, 0)
                        endif
                        if .caster == null then
                            call UnitDamageTarget(dummy, u, .damagePerInterval, true, false, .attackType, .damageType, .weaponType)
                        else
                            call UnitDamageTarget(.caster, u, .damagePerInterval, true, false, .attackType, .damageType, .weaponType)
                        endif
                        if .tossedTargetSfxModel != null and not UnitAlive(u) then
                            set id = GetHandleId(u)
                            set sfx = table[this].effect[id]
                            if sfx != null then
                                call DestroyEffect(sfx)
                                call table[this].effect.remove(id)
                            endif
                        endif
                        if .pauseTargets then
                            call Stun.apply(u, 0.01, false)
                        endif
                    endloop
                    set g = tempGroup
                    set tempGroup = .tossedGroup
                    set .tossedGroup = g
                    set g = null
                    set dummy = null
                else
                    set recreate = .onVanishTrigger != null and .targetGroup == null
                    if recreate then
                        set .targetGroup = CreateGroup()
                    endif
                    loop
                        set u = FirstOfGroup(.tossedGroup)
                        exitwhen u == null
                        call GroupRemoveUnit(.tossedGroup, u)
                        if recreate then
                            call GroupAddUnit(.targetGroup, u)
                        endif
                        if .landedTargetSfxModel != null then
                            call DestroyEffect(AddSpecialEffectTarget(.landedTargetSfxModel, u, .landedTargetSfxAttachPoint))
                        endif
                        if .tossedTargetSfxModel != null then
                            set sfx = table[this].effect[GetHandleId(u)]
                            if sfx != null then
                                call DestroyEffect(sfx)
                                set sfx = null
                            endif
                        endif
                        /* Reset tossed units' fly height to default upon landing */
                        call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u), 0.00)
                    endloop
                    if recreate then
                        /* Fire torrent disappearance handler */
                        call .evaluate(.onVanishTrigger)
                    endif
                    call .destroy()
                endif
                set this = .next
            endloop
        endmethod

        method explode takes boolean tossUnits returns nothing
            local unit dummy = GetUnitById(this)
            local player owner = GetOwningPlayer(.caster)
            local boolean evaluateExplosion
            local unit u
            call .destroyBorders()
            set tossUnits = tossUnits and .duration >= 2.00*TIMEOUT
            if tossUnits then
                /* Add this torrent instance to the periodic loop actions */
                set .next = 0
                set .prev = thistype(0).prev
                set thistype(0).prev = this
                set .prev.next = this
                set .tossedGroup = CreateGroup()
                if .prev == 0 then
                    call TimerStart(staticTimer, TIMEOUT, true, function thistype.periodic)
                endif
            endif
            if .model != null then
                /* Creating torrent explosion SFX */
                call DestroyEffect(AddSpecialEffectTarget(.model, dummy, "origin"))
            endif
            set evaluateExplosion = .onExplodeTrigger != null
            if evaluateExplosion then
                set .targetGroup = CreateGroup()
            endif
            set triggerInstance = this
            call GroupEnumUnitsInRange(tempGroup, .centerX, .centerY, .radius, .filterExpr)
            set triggerInstance = 0
            loop
                set u = FirstOfGroup(tempGroup)
                exitwhen u == null
                call GroupRemoveUnit(tempGroup, u)
                if evaluateExplosion then
                    call GroupAddUnit(.targetGroup, u)
                endif
                if tossUnits then
                    call GroupAddUnit(.tossedGroup, u)
                    static if not LIBRARY_AutoFly then
                        if UnitAddAbility(u, 'Arav') and UnitRemoveAbility(u, 'Arav') then
                        endif
                    endif
                    call SetUnitAnimation(u, .tossedTargetAnimation)
                    if .tossedTargetSfxModel != null then
                        set table[this].effect[GetHandleId(u)] = AddSpecialEffectTarget(.tossedTargetSfxModel, u, .tossedTargetSfxAttachPoint)
                    endif
                elseif .tossedTargetSfxModel != null then
                    call DestroyEffect(AddSpecialEffectTarget(.tossedTargetSfxModel, u, .tossedTargetSfxAttachPoint))
                endif
                if .caster == null then
                    call UnitDamageTarget(dummy, u, .explosionDamage, true, false, .attackType, .damageType, .weaponType)
                else
                    call UnitDamageTarget(.caster, u, .explosionDamage, true, false, .attackType, .damageType, .weaponType)
                endif
            endloop
            if evaluateExplosion then
                /* Fire the torrent's explosion handler */
                call .evaluate(.onExplodeTrigger)
                call GroupClear(.targetGroup)
            endif
            if not tossUnits then
                /* If <tossUnits> is false, destroy instantly */
                call .destroy()
            endif
            set owner = null
            set dummy = null
        endmethod

        static method create takes real centerX, real centerY, real radius, integer borderCount, string model, string borderSfxModel, string onSummonSfxModel returns thistype
            static if LIBRARY_DummyRecycler then
                local unit dummy = GetRecycledDummyAnyAngle(centerX, centerY, 0)
            else
                local unit dummy = CreateUnit(DUMMY_OWNER, DUMMY_ID, centerX, centerY, 0)
            endif
            local thistype this = GetUnitId(dummy)
            local real deltaAngle = 2*bj_PI/borderCount
            local real angle
            /* Initialize default target filters */
            static if thistype.targetFilter.exists then
                set .filterExpr = Filter(function thistype.targetFilter)
            endif
            set .pauseTargets   = true
            set .centerX        = centerX
            set .centerY        = centerY
            set .borderCount    = borderCount
            set .radius         = radius
            set .model          = model
            set .borderModel    = borderSfxModel
            if borderSfxModel != null then
                /* Adjust the radius a bit for the border effects' location
                   to fit with the radius                                   */

                set radius = radius - 20
                loop
                    set angle = deltaAngle*borderCount
                    /* Implement map bounds */
                    static if LIBRARY_WorldBounds then
                        set table[this].effect[borderCount] = AddSpecialEffect(borderSfxModel, getBoundedValue(centerX + radius*Cos(angle), WorldBounds.minX, WorldBounds.maxX), getBoundedValue(centerY + radius*Sin(angle), WorldBounds.minY, WorldBounds.maxY))
                    else
                        set table[this].effect[borderCount] = AddSpecialEffect(borderSfxModel, getBoundedValue(centerX + radius*Cos(angle), minX, maxX), getBoundedValue(centerY + radius*Sin(angle), minY, maxY))
                    endif
                    set borderCount = borderCount - 1
                    exitwhen borderCount == 0
                endloop
            endif
            call SetUnitScale(dummy, radius*0.01 , 0, 0)
            set table[this].effect[0] = AddSpecialEffectTarget(onSummonSfxModel, dummy, "origin")
            set .caster = dummy
            set dummy = null
            return this
        endmethod

        method registerExplosionHandler takes boolexpr expr returns nothing
            if expr != null then
                if .onExplodeTrigger == null then
                    set .onExplodeTrigger = CreateTrigger()
                endif
                call TriggerAddCondition(.onExplodeTrigger, expr)
            endif
        endmethod

        method registerVanishHandler takes boolexpr expr returns nothing
            if expr != null then
                if .onVanishTrigger == null then
                    set .onVanishTrigger = CreateTrigger()
                endif
                call TriggerAddCondition(.onVanishTrigger, expr)
            endif
        endmethod

        method operator damagePerSecond= takes real dps returns nothing
            set .damagePerInterval = dps*TIMEOUT
        endmethod

        method operator damagePerSecond takes nothing returns real
            return .damagePerInterval/TIMEOUT
        endmethod

        /* Clean groups from removed units which is more efficient than
           regularly cleaning groups per tossing period                    */

        private static method onDeindex takes nothing returns boolean
            local thistype this = thistype(0).next
            local unit deindexed = GetIndexedUnit()
            loop
                exitwhen this == 0
                call GroupRemoveUnit(.tossedGroup, deindexed)
                set this = .next
            endloop
            set deindexed = null
            return false
        endmethod

        private static method onInit takes nothing returns nothing
            /* Setup map bounds */
            static if not LIBRARY_WorldBounds then
                local rect bounds = GetWorldBounds()
                set mapMinX = GetRectMinX(bounds)
                set mapMinY = GetRectMinY(bounds)
                set mapMaxX = GetRectMaxX(bounds)
                set mapMaxY = GetRectMaxY(bounds)
                call RemoveRect(bounds)
                set bounds = null
            endif
            set table = TableArray[0x2000]
            call OnUnitDeindex(function thistype.onDeindex)
        endmethod

    endmodule

    struct Torrent extends array
        implement TorrentModule
    endstruct


endlibrary
library SpellEvent /* v1.01


    */
uses /*

    */
Table                             /*  https://www.hiveworkshop.com/threads/snippet-new-table.188084/
    */
optional ErrorMessage             /*  https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j
    */
optional RegisterPlayerUnitEvent  /*  https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
    */
optional ResourcePreloader        /*  https://www.hiveworkshop.com/threads/snippet-resource-preloader.287358/


    */
//! novjass

    /*
        Pros:
            - Minimizes handle creation when registering a spell event handler
            - Constant time complexity 'O(1)' when executing a spell event
            - Shortens spell registration code
            - Shortens and gives better structure and readability by making a template for
              your spell struct through the use of modules

    */


    |=========|
    | Credits |
    |=========|
    /*
        - AGD (Author)
        - Bribe (SpellEffectEvent concept)

    */

    |=========|
    | Structs |
    |=========|
    /*

      */
struct Spell extends array/*

        Event Responses:
          */
readonly static integer ABILITY_ID      /*
          */
readonly static integer LEVEL           /*
          */
readonly static player  USER            /*
          */
readonly static unit    CASTER          /*
          */
readonly static unit    TARGET          /*
          */
readonly static real    TARGET_X        /*  Returns the x-coordinate of the caster if the spell is a 'No-target' ability
          */
readonly static real    TARGET_Y        /*  Returns the y-coordinate of the caster if the spell is a 'No-target' ability

        Methods:

          */
static method   operator []                 takes integer spellId                                           returns Spell/*
                - Returns a Spell instance based on the given spellId for event handler
                  registrations

          */
method          registerEventHandler        takes SpellEventType eventType, code handler                    returns nothing/*
          */
method          unregisterEventHandler      takes SpellEventType eventType, code handler                    returns nothing/*
          */
method          clearEventHandlers          takes SpellEventType eventType                                  returns nothing/*
          */
method          clearHandlers               takes nothing                                                   returns nothing/*
                - Manages spell event handlers


      */
struct SpellEventType extends array/*

        Event Types:
          */
static constant SpellEventType CAST      /*
          */
static constant SpellEventType CHANNEL   /*
          */
static constant SpellEventType EFFECT    /*
          */
static constant SpellEventType ENDCAST   /*
          */
static constant SpellEventType FINISH    /*


    */

    |===========|
    | Variables |
    |===========|
    /*
        Spell Event Types

      */
constant SpellEventType EVENT_SPELL_CAST/*
      */
constant SpellEventType EVENT_SPELL_CHANNEL/*
      */
constant SpellEventType EVENT_SPELL_EFFECT/*
      */
constant SpellEventType EVENT_SPELL_ENDCAST/*
      */
constant SpellEventType EVENT_SPELL_FINISH/*

    */

    |===========|
    | Functions |
    |===========|
    /*
        Equivalent functions for the methods above

      */
constant function   GetEventSpellId             takes nothing                                                   returns integer/*
      */
constant function   GetEventSpellLevel          takes nothing                                                   returns integer/*
      */
constant function   GetEventSpellUser           takes nothing                                                   returns player/*
      */
constant function   GetEventSpellCaster         takes nothing                                                   returns unit/*
      */
constant function   GetEventSpellTargetUnit     takes nothing                                                   returns unit/*
      */
constant function   GetEventSpellTargetX        takes nothing                                                   returns real/*
      */
constant function   GetEventSpellTargetY        takes nothing                                                   returns real/*

      */
function            SpellRegisterEventHandler   takes integer spellId, SpellEventType eventType, code handler   returns nothing/*
      */
function            SpellUnregisterEventHandler takes integer spellId, SpellEventType eventType, code handler   returns nothing/*
      */
function            SpellClearEventHandlers     takes integer spellId, SpellEventType eventType                 returns nothing/*
      */
function            SpellClearHandlers          takes integer spellId                                           returns nothing/*

    */

    |=========|
    | Modules |
    |=========|
    /*
        Automates spell event registration at map init
        Implement either of these two modules at the bottom of your struct

      */
module SpellEvent/*

            > Uses a single timer for all active spell instances. Standard module designed for
              periodic spells with high-frequency timeout (<= 0.5 seconds)

        Fields:

          */
readonly thistype prev/*
          */
readonly thistype next/*
                - Spell instances links
                - Readonly attribute is only effective outside the implementing struct, though
                  users are also not supposed to change these values

        Member interfaces:
            - Should be declared above the module implementation

          */
static integer SPELL_ID             /*  Ability rawcode
          */
static integer SPELL_EVENT_TYPE     /*  Spell event type
          */
static real    SPELL_PERIOD         /*  Spell periodic actions execution period

          */
method onSpellStart takes nothing returns nothing/*
                - Runs right after the spell event fires
          */
method onSpellPeriodic takes nothing returns boolean/*
                - Runs periodically after the spell event fires until it returns false
          */
method onSpellEnd takes nothing returns nothing/*
                - Runs <SPELL_PERIOD> seconds after method onSpellPeriodic() returns false


      */
module SpellEventEx/*

            > Uses 1 timer for each active spell instance. A module specifically designed for
              periodic spells with low-frequency timeout (> 0.5 seconds) as it does not affect
              the accuracy of the first 'tick' of the periodic operations, and for spells where
              you need to manually design your spell instance allocation/deallocation.

        Member interfaces:
            - Should be declared above the module implementation

          */
static integer SPELL_ID             /*  Ability rawcode
          */
static integer SPELL_EVENT_TYPE     /*  Spell event type
          */
static real    SPELL_PERIOD         /*  Spell periodic actions execution period

          */
static method onSpellStart takes nothing returns thistype/*
                - Runs right after the spell event fires
                - User should manually allocate the spell instance and use it as a return value of this method
                - Returning zero or a negative value will not run the periodic operations for that instance
          */
method onSpellPeriodic takes nothing returns boolean/*
                - Runs periodically after the spell event fires until it returns false
          */
method onSpellEnd takes nothing returns nothing/*
                - Runs <SPELL_PERIOD> seconds after method onSpellPeriodic() returns false
                - User must manually deallocate the spell instance inside this method


    */
//! endnovjass

    /*=================================== SYSTEM CODE ===================================*/

    /*
    *   Arbitrary values are used so that users are forced to use the constant globals
    *   instead of manually typing the integers ;D
    */

    globals
        constant integer EVENT_SPELL_CAST      = 0x1234 + 0x123 * 1
        constant integer EVENT_SPELL_CHANNEL   = 0x1234 + 0x123 * 2
        constant integer EVENT_SPELL_EFFECT    = 0x1234 + 0x123 * 3
        constant integer EVENT_SPELL_ENDCAST   = 0x1234 + 0x123 * 4
        constant integer EVENT_SPELL_FINISH    = 0x1234 + 0x123 * 5
    endglobals

    struct SpellEventType extends array
        static constant integer CAST      = EVENT_SPELL_CAST
        static constant integer CHANNEL   = EVENT_SPELL_CHANNEL
        static constant integer EFFECT    = EVENT_SPELL_EFFECT
        static constant integer ENDCAST   = EVENT_SPELL_ENDCAST
        static constant integer FINISH    = EVENT_SPELL_FINISH
    endstruct

    globals
        private keyword Init
        private TableArray handlerTable
        private Table table
    endglobals

    private function GetEventIndex takes integer eventType returns integer
        return (eventType - 0x1234)/0x123
    endfunction

    struct Spell extends array

        readonly static integer ABILITY_ID      = 0
        readonly static integer LEVEL           = 0
        readonly static player  USER            = null
        readonly static unit    CASTER          = null
        readonly static unit    TARGET          = null
        readonly static real    TARGET_X        = 0.00
        readonly static real    TARGET_Y        = 0.00

        private static integer spellCount = 0
        private static trigger array handlerTrigger
        private static integer array handlerCount

        static method operator [] takes integer abilId returns thistype
            local thistype this = table[-abilId]
            if this == 0 then
                static if LIBRARY_ErrorMessage then
                    debug call ThrowError(spellCount > 1000, "SpellEvent", "Spell[]", "thistype", 0, "Overflow")
                endif
                set spellCount = spellCount + 1
                set table[-abilId] = spellCount
                return spellCount
            endif
            return this
        endmethod

        method registerEventHandler takes SpellEventType eventType, code handler returns nothing
            local integer index = (this - 1)*5 + GetEventIndex(eventType)
            local boolexpr expr = Filter(handler)
            static if LIBRARY_ErrorMessage then
                debug call ThrowError((this) < 1 or (this) > spellCount, "SpellEvent", "registerEventHandler()", "thistype", this, "Attempted to use an invalid Spell instance")
                debug call ThrowError(GetEventIndex(eventType) < 1 or GetEventIndex(eventType) > 5, "SpellEvent", "registerEventHandler()", "thistype", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
                debug call ThrowError(handlerTable[index].handle.has(GetHandleId(expr)), "SpellEvent", "registerEventHandler()", "thistype", this, "EventType(" + I2S(eventType) + "): Attempted to register an already registered code")
            endif
            if handlerTrigger[index] == null then
                set handlerTrigger[index] = CreateTrigger()
            endif
            set handlerCount[index] = handlerCount[index] + 1
            set handlerTable[index].triggercondition[GetHandleId(expr)] = TriggerAddCondition(handlerTrigger[index], expr)
            set expr = null
        endmethod
        method unregisterEventHandler takes SpellEventType eventType, code handler returns nothing
            local integer index = (this - 1)*5 + GetEventIndex(eventType)
            local integer count = handlerCount[index] - 1
            local integer handleId = GetHandleId(Filter(handler))
            static if LIBRARY_ErrorMessage then
                debug call ThrowError((this) < 1 or (this) > spellCount, "SpellEvent", "unregisterEventHandler()", "thistype", this, "Attempted to use an invalid Spell instance")
                debug call ThrowError(GetEventIndex(eventType) < 1 or GetEventIndex(eventType) > 5, "SpellEvent", "unregisterEventHandler()", "thistype", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
                debug call ThrowError(not handlerTable[index].handle.has(handleId), "SpellEvent", "registerEventHandler()", "thistype", this, "EventType(" + I2S(eventType) + "): Attempted to unregister an unregistered code")
            endif
            call TriggerRemoveCondition(handlerTrigger[index], handlerTable[index].triggercondition[handleId])
            call handlerTable[index].triggercondition.remove(handleId)
            set handlerCount[index] = handlerCount[index] - 1
            if handlerCount[index] == 0 then
                call DestroyTrigger(handlerTrigger[index])
                set handlerTrigger[index] = null
            endif
        endmethod
        method clearEventHandlers takes SpellEventType eventType returns nothing
            local integer index = (this - 1)*5 + GetEventIndex(eventType)
            static if LIBRARY_ErrorMessage then
                debug call ThrowError((this) < 1 or (this) > spellCount, "SpellEvent", "clearEventHandlers()", "thistype", this, "Attempted to use an invalid Spell instance")
                debug call ThrowError(GetEventIndex(eventType) < 1 or GetEventIndex(eventType) > 5, "SpellEvent", "clearEventHandlers()", "thistype", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
            endif
            call handlerTable[index].flush()
            call DestroyTrigger(handlerTrigger[index])
            set handlerTrigger[index] = null
            set handlerCount[index] = 0
        endmethod
        method clearHandlers takes nothing returns nothing
            static if LIBRARY_ErrorMessage then
                debug call ThrowError((this) < 1 or (this) > spellCount, "SpellEvent", "clearHandlers()", "thistype", this, "Attempted to use an invalid Spell instance")
            endif
            call this.clearEventHandlers(SpellEventType.CAST)
            call this.clearEventHandlers(SpellEventType.CHANNEL)
            call this.clearEventHandlers(SpellEventType.EFFECT)
            call this.clearEventHandlers(SpellEventType.ENDCAST)
            call this.clearEventHandlers(SpellEventType.FINISH)
        endmethod

        private static method executeEventHandler takes trigger eventHandlerTrigger, integer currentId returns nothing

            local integer prevId    = ABILITY_ID
            local player prevUser   = USER
            local unit prevCaster   = CASTER
            local unit prevTarget   = TARGET
            local real prevTargetX  = TARGET_X
            local real prevTargetY  = TARGET_Y
            local integer prevLevel = LEVEL
            local location tempLoc

            set ABILITY_ID          = currentId
            set USER                = GetTriggerPlayer()
            set CASTER              = GetTriggerUnit()
            set TARGET              = GetSpellTargetUnit()
            set LEVEL               = GetUnitAbilityLevel(CASTER, ABILITY_ID)

            if TARGET != null then
                set TARGET_X        = GetUnitX(TARGET)
                set TARGET_Y        = GetUnitY(TARGET)
            else
                set tempLoc = GetSpellTargetLoc()
                if tempLoc == null then
                /* Special Case (for some no-target spells) */
                    set TARGET_X    = GetUnitX(CASTER)
                    set TARGET_Y    = GetUnitY(CASTER)
                else
                    set TARGET_X    = GetSpellTargetX()
                    set TARGET_Y    = GetSpellTargetY()
                    call RemoveLocation(tempLoc)
                    set tempLoc = null
                endif
            endif

            call TriggerEvaluate(eventHandlerTrigger)

            set ABILITY_ID          = prevId
            set USER                = prevUser
            set CASTER              = prevCaster
            set TARGET              = prevTarget
            set TARGET_X            = prevTargetX
            set TARGET_Y            = prevTargetY
            set LEVEL               = prevLevel

            set prevUser            = null
            set prevCaster          = null
            set prevTarget          = null

        endmethod

        private static method onSpellEvent takes integer eventIndex returns nothing
            local integer id = GetSpellAbilityId()
            local trigger tempTrigger = handlerTrigger[(table[-id] - 1)*5 + eventIndex]
            if tempTrigger != null then
                call executeEventHandler(tempTrigger, id)
                set tempTrigger = null
            endif
        endmethod

        private static method onSpellCast takes nothing returns nothing
            call onSpellEvent(1)
        endmethod
        private static method onSpellChannel takes nothing returns nothing
            call onSpellEvent(2)
        endmethod
        private static method onSpellEffect takes nothing returns nothing
            call onSpellEvent(3)
        endmethod
        private static method onSpellEndcast takes nothing returns nothing
            call onSpellEvent(4)
        endmethod
        private static method onSpellFinish takes nothing returns nothing
            call onSpellEvent(5)
        endmethod

        private static method registerEvent takes playerunitevent whichEvent, code handler returns nothing
            static if RPUE_VERSION_NEW then
                call RegisterAnyPlayerUnitEvent(whichEvent, handler)
            elseif LIBRARY_RegisterPlayerUnitEvent then
                call RegisterPlayerUnitEvent(whichEvent, handler)
            else
                local trigger t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, whichEvent)
                call TriggerAddCondition(t, Filter(handler))
                set t = null
            endif
        endmethod

        private static method init takes nothing returns nothing
            set handlerTable = TableArray[1000*5]
            set table = handlerTable[0]
            call registerEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function thistype.onSpellCast)
            call registerEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onSpellChannel)
            call registerEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onSpellEffect)
            call registerEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onSpellEndcast)
            call registerEvent(EVENT_PLAYER_UNIT_SPELL_FINISH, function thistype.onSpellFinish)
        endmethod
        implement Init

    endstruct

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

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

    constant function GetEventSpellId takes nothing returns integer
        return Spell.ABILITY_ID
    endfunction
    constant function GetEventSpellLevel takes nothing returns integer
        return Spell.LEVEL
    endfunction
    constant function GetEventSpellUser takes nothing returns player
        return Spell.USER
    endfunction
    constant function GetEventSpellCaster takes nothing returns unit
        return Spell.CASTER
    endfunction
    constant function GetEventSpellTargetUnit takes nothing returns unit
        return Spell.TARGET
    endfunction
    constant function GetEventSpellTargetX takes nothing returns real
        return Spell.TARGET_X
    endfunction
    constant function GetEventSpellTargetY takes nothing returns real
        return Spell.TARGET_Y
    endfunction

    function SpellRegisterEventHandler takes integer abilId, SpellEventType eventType, code handler returns nothing
        static if LIBRARY_ErrorMessage then
            debug local integer eventIndex = GetEventIndex(eventType)
            debug call ThrowError(eventIndex < 1 or eventIndex > 5, "SpellEvent", "SpellRegisterEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
            debug call ThrowError(handlerTable[(Spell[abilId] - 1)*5 + eventIndex].handle.has(GetHandleId(Filter(handler))), "SpellEvent", "SpellRegisterEventHandler()", "", 0, "Spell(" + I2S(abilId) + "), EventType(" + I2S(eventType) + "): Attempted to register an already registered code")
        endif
        call Spell[abilId].registerEventHandler(eventType, handler)
    endfunction
    function SpellUnregisterEventHandler takes integer abilId, SpellEventType eventType, code handler returns nothing
        static if LIBRARY_ErrorMessage then
            debug local integer eventIndex = GetEventIndex(eventType)
            debug call ThrowError(eventIndex < 1 or eventIndex > 5, "SpellEvent", "SpellUnregisterEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
            debug call ThrowError(not handlerTable[(Spell[abilId] - 1)*5 + eventIndex].handle.has(GetHandleId(Filter(handler))), "SpellEvent", "SpellUnregisterEventHandler()", "", 0, "Spell(" + I2S(abilId) + "), EventType(" + I2S(eventType) + "): Attempted to unregister an unregistered code")
        endif
        call Spell[abilId].unregisterEventHandler(eventType, handler)
    endfunction
    function SpellClearEventHandlers takes integer abilId, SpellEventType eventType returns nothing
        static if LIBRARY_ErrorMessage then
            debug local integer eventIndex = GetEventIndex(eventType)
            debug call ThrowError(eventIndex < 1 or eventIndex > 5, "SpellEvent", "SpellClearEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
        endif
        call Spell[abilId].clearEventHandlers(eventType)
    endfunction
    function SpellClearHandlers takes integer abilId returns nothing
        call Spell[abilId].clearHandlers()
    endfunction

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

    private function DestroyTimerEx takes timer whichTimer returns nothing
        call PauseTimer(whichTimer)
        call DestroyTimer(whichTimer)
    endfunction

    module SpellEvent

        readonly thistype prev
        readonly thistype next
        private thistype recycler

        private static method onPeriodic takes nothing returns nothing
            local thistype node = thistype(0).next
            loop
                exitwhen node == 0
                if not node.onSpellPeriodic() then
                    call node.onSpellEnd()
                    set node.next.prev = node.prev
                    set node.prev.next = node.next
                    set node.recycler = thistype(0).recycler
                    set thistype(0).recycler = node
                    if thistype(0).next == 0 then
                        call DestroyTimerEx(GetExpiredTimer())
                    endif
                endif
                set node = node.next
            endloop
        endmethod

        private static method onSpellEvent takes nothing returns nothing
            local thistype last = thistype(0).prev
            local thistype node = thistype(0).recycler
            set thistype(0).recycler = node.recycler
            set thistype(0).prev = node
            set last.next = node
            set node.prev = last
            set node.next = 0
            call node.onSpellStart()
            if last == 0 then
                call TimerStart(CreateTimer(), SPELL_PERIOD, true, function thistype.onPeriodic)
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            local thistype node = 0
            loop
                exitwhen node == 8190
                set node.recycler = node + 1
                set node = node + 1
            endloop
            set node.recycler = 0
            call Spell[SPELL_ID].registerEventHandler(SPELL_EVENT_TYPE, function thistype.onSpellEvent)
            static if LIBRARY_ResourcePreloader then
                call PreloadAbility(SPELL_ID)
            endif
        endmethod

    endmodule

    module SpellEventEx

        private static method onPeriodic takes nothing returns nothing
            local timer expired = GetExpiredTimer()
            local integer handleId = GetHandleId(expired)
            local thistype node = table[handleId]
            if not node.onSpellPeriodic() then
                call node.onSpellEnd()
                call table.remove(handleId)
                call DestroyTimerEx(expired)
            endif
            set expired = null
        endmethod

        private static method onSpellEvent takes nothing returns nothing
            local timer periodicTimer
            local thistype node = onSpellStart()
            if node > 0 then
                set periodicTimer = CreateTimer()
                set table[GetHandleId(periodicTimer)] = node
                call TimerStart(periodicTimer, SPELL_PERIOD, true, function thistype.onPeriodic)
                set periodicTimer = null
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            call Spell[SPELL_ID].registerEventHandler(SPELL_EVENT_TYPE, function thistype.onSpellEvent)
            static if LIBRARY_ResourcePreloader then
                call PreloadAbility(SPELL_ID)
            endif
        endmethod

    endmodule


endlibrary
library UniqueList /* v1.0.0.2
************************************************************************************
*
*   */
uses/*
*
*       */
ErrorMessage /*         hiveworkshop.com/forums/submissions-414/snippet-error-message-239210/
*
************************************************************************************
*
*   module UniqueList
*
*       Description
*       -------------------------
*
*           Node Properties:
*
*               Unique
*               Allocated
*               Not 0
*
*       Fields
*       -------------------------
*
*           readonly static integer sentinel
*
*           readonly thistype list
*
*           readonly thistype first
*           readonly thistype last
*
*           readonly thistype next
*           readonly thistype prev
*
*       Methods
*       -------------------------
*
*           static method create takes nothing returns thistype
*           method destroy takes nothing returns nothing
*               - May only destroy lists
*
*           method push takes thistype node returns nothing
*           method enqueue takes thistype node returns nothing
*
*           method pop takes nothing returns nothing
*           method dequeue takes nothing returns nothing
*
*           method remove takes nothing returns nothing
*
*           method clear takes nothing returns nothing
*
*           debug static method calculateMemoryUsage takes nothing returns integer
*           debug static method getAllocatedMemoryAsString takes nothing returns string
*
************************************************************************************/

    module UniqueList
        private static thistype collectionCount = 0
        debug private boolean isNode
        debug private boolean isCollection
       
        private thistype _list
        method operator list takes nothing returns thistype
            debug call ThrowError(this == 0,    "UniqueList", "list", "thistype", this, "Attempted To Read Null Node.")
            debug call ThrowError(not isNode,   "UniqueList", "list", "thistype", this, "Attempted To Read Invalid Node.")
            return _list
        endmethod
       
        private thistype _next
        method operator next takes nothing returns thistype
            debug call ThrowError(this == 0,    "UniqueList", "next", "thistype", this, "Attempted To Go Out Of Bounds.")
            debug call ThrowError(not isNode,   "UniqueList", "next", "thistype", this, "Attempted To Read Invalid Node.")
            return _next
        endmethod
       
        private thistype _prev
        method operator prev takes nothing returns thistype
            debug call ThrowError(this == 0,    "UniqueList", "prev", "thistype", this, "Attempted To Go Out Of Bounds.")
            debug call ThrowError(not isNode,   "UniqueList", "prev", "thistype", this, "Attempted To Read Invalid Node.")
            return _prev
        endmethod
       
        private thistype _first
        method operator first takes nothing returns thistype
            debug call ThrowError(this == 0,        "UniqueList", "first", "thistype", this, "Attempted To Read Null List.")
            debug call ThrowError(not isCollection, "UniqueList", "first", "thistype", this, "Attempted To Read Invalid List.")
            return _first
        endmethod
       
        private thistype _last
        method operator last takes nothing returns thistype
            debug call ThrowError(this == 0,        "UniqueList", "last", "thistype", this, "Attempted To Read Null List.")
            debug call ThrowError(not isCollection, "UniqueList", "last", "thistype", this, "Attempted To Read Invalid List.")
            return _last
        endmethod
       
        static method operator sentinel takes nothing returns integer
            return 0
        endmethod
       
        private static method allocateCollection takes nothing returns thistype
            local thistype this = thistype(0)._first
           
            if (0 == this) then
                debug call ThrowError(collectionCount == 8191, "UniqueList", "allocateCollection", "thistype", 0, "Overflow.")
               
                set this = collectionCount + 1
                set collectionCount = this
            else
                set thistype(0)._first = _first
            endif
           
            return this
        endmethod
       
        static method create takes nothing returns thistype
            local thistype this = allocateCollection()    
           
            debug set isCollection = true
           
            set _first = 0
           
            return this
        endmethod
        method push takes thistype node returns nothing
            debug call ThrowError(this == 0,            "UniqueList", "push", "thistype", this, "Attempted To Push (" + I2S(node) + ") On To Null List.")
            debug call ThrowError(not isCollection,     "UniqueList", "push", "thistype", this, "Attempted To Push (" + I2S(node) + ") On To Invalid List.")
            debug call ThrowError(node == 0,            "UniqueList", "push", "thistype", this, "Attempted To Push Null Node.")
            debug call ThrowError(node.isNode,          "UniqueList", "push", "thistype", this, "Attempted To Push Owned Node (" + I2S(node) + ").")
           
            debug set node.isNode = true
           
            set node._list = this
       
            if (_first == 0) then
                set _first = node
                set _last = node
                set node._next = 0
            else
                set _first._prev = node
                set node._next = _first
                set _first = node
            endif
           
            set node._prev = 0
        endmethod
        method enqueue takes thistype node returns nothing
            debug call ThrowError(this == 0,            "UniqueList", "enqueue", "thistype", this, "Attempted To Enqueue (" + I2S(node) + ") On To Null List.")
            debug call ThrowError(not isCollection,     "UniqueList", "enqueue", "thistype", this, "Attempted To Enqueue (" + I2S(node) + ") On To Invalid List.")
            debug call ThrowError(node == 0,            "UniqueList", "enqueue", "thistype", this, "Attempted To Enqueue Null Node.")
            debug call ThrowError(node.isNode,          "UniqueList", "enqueue", "thistype", this, "Attempted To Enqueue Owned Node (" + I2S(node) + ").")
           
            debug set node.isNode = true
           
            set node._list = this
       
            if (_first == 0) then
                set _first = node
                set _last = node
                set node._prev = 0
            else
                set _last._next = node
                set node._prev = _last
                set _last = node
            endif
           
            set node._next = 0
        endmethod
        method pop takes nothing returns nothing
            debug call ThrowError(this == 0,        "UniqueList", "pop", "thistype", this, "Attempted To Pop Null List.")
            debug call ThrowError(not isCollection, "UniqueList", "pop", "thistype", this, "Attempted To Pop Invalid List.")
            debug call ThrowError(_first == 0,      "UniqueList", "pop", "thistype", this, "Attempted To Pop Empty List.")
           
            debug set _first.isNode = false
           
            set _first._list = 0
           
            set _first = _first._next
            if (_first == 0) then
                set _last = 0
            else
                set _first._prev = 0
            endif
        endmethod
        method dequeue takes nothing returns nothing
            debug call ThrowError(this == 0,        "UniqueList", "dequeue", "thistype", this, "Attempted To Dequeue Null List.")
            debug call ThrowError(not isCollection, "UniqueList", "dequeue", "thistype", this, "Attempted To Dequeue Invalid List.")
            debug call ThrowError(_last == 0,       "UniqueList", "dequeue", "thistype", this, "Attempted To Dequeue Empty List.")
           
            debug set _last.isNode = false
           
            set _last._list = 0
       
            set _last = _last._prev
            if (_last == 0) then
                set _first = 0
            else
                set _last._next = 0
            endif
        endmethod
        method remove takes nothing returns nothing
            local thistype node = this
            set this = node._list
           
            debug call ThrowError(node == 0,        "UniqueList", "remove", "thistype", this, "Attempted To Remove Null Node.")
            debug call ThrowError(not node.isNode,  "UniqueList", "remove", "thistype", this, "Attempted To Remove Invalid Node (" + I2S(node) + ").")
           
            debug set node.isNode = false
           
            set node._list = 0
       
            if (0 == node._prev) then
                set _first = node._next
            else
                set node._prev._next = node._next
            endif
            if (0 == node._next) then
                set _last = node._prev
            else
                set node._next._prev = node._prev
            endif
        endmethod
        method clear takes nothing returns nothing
            debug local thistype node = _first
       
            debug call ThrowError(this == 0,            "UniqueList", "clear", "thistype", this, "Attempted To Clear Null List.")
            debug call ThrowError(not isCollection,     "UniqueList", "clear", "thistype", this, "Attempted To Clear Invalid List.")
           
            static if DEBUG_MODE then
                loop
                    exitwhen node == 0
                    set node.isNode = false
                    set node = node._next
                endloop
            endif
           
            if (_first == 0) then
                return
            endif
           
            set _first = 0
            set _last = 0
        endmethod
        method destroy takes nothing returns nothing
            debug call ThrowError(this == 0,            "UniqueList", "destroy", "thistype", this, "Attempted To Destroy Null List.")
            debug call ThrowError(not isCollection,     "UniqueList", "destroy", "thistype", this, "Attempted To Destroy Invalid List.")
           
            static if DEBUG_MODE then
                debug call clear()
               
                debug set isCollection = false
            else
                if (_first != 0) then
                    set _last._next = thistype(0)._next
                    set thistype(0)._next = _first
                   
                    set _last = 0
                endif
            endif
           
            set _first = thistype(0)._first
            set thistype(0)._first = this
        endmethod
       
        static if DEBUG_MODE then
            static method calculateMemoryUsage takes nothing returns integer
                local thistype start = 1
                local thistype end = 8191
                local integer count = 0
               
                loop
                    exitwhen integer(start) > integer(end)
                    if (integer(start) + 500 > integer(end)) then
                        return count + checkRegion(start, end)
                    else
                        set count = count + checkRegion(start, start + 500)
                        set start = start + 501
                    endif
                endloop
               
                return count
            endmethod
             
            private static method checkRegion takes thistype start, thistype end returns integer
                local integer count = 0
           
                loop
                    exitwhen integer(start) > integer(end)
                    if (start.isNode) then
                        set count = count + 1
                    endif
                    if (start.isCollection) then
                        set count = count + 1
                    endif
                    set start = start + 1
                endloop
               
                return count
            endmethod
           
            static method getAllocatedMemoryAsString takes nothing returns string
                local thistype start = 1
                local thistype end = 8191
                local string memory = null
               
                loop
                    exitwhen integer(start) > integer(end)
                    if (integer(start) + 500 > integer(end)) then
                        if (memory != null) then
                            set memory = memory + ", "
                        endif
                        set memory = memory + checkRegion2(start, end)
                        set start = end + 1
                    else
                        if (memory != null) then
                            set memory = memory + ", "
                        endif
                        set memory = memory + checkRegion2(start, start + 500)
                        set start = start + 501
                    endif
                endloop
               
                return memory
            endmethod
             
            private static method checkRegion2 takes thistype start, thistype end returns string
                local string memory = null
           
                loop
                    exitwhen integer(start) > integer(end)
                    if (start.isNode) then
                        if (memory == null) then
                            set memory = I2S(start)
                        else
                            set memory = memory + ", " + I2S(start) + "N"
                        endif
                    endif
                    if (start.isCollection) then
                        if (memory == null) then
                            set memory = I2S(start)
                        else
                            set memory = memory + ", " + I2S(start) + "C"
                        endif
                    endif
                    set start = start + 1
                endloop
               
                return memory
            endmethod
        endif
    endmodule
endlibrary
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.

    One map, one hashtable. Welcome to NewTable 4.1.0.1

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

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

    API

    ------------
    struct Table
    | static method create takes nothing returns Table
    |     create a new Table
    |
    | method destroy takes nothing returns nothing
    |     destroy it
    |
    | method flush takes nothing returns nothing
    |     flush all stored values inside of it
    |
    | method remove takes integer key returns nothing
    |     remove the value at index "key"
    |
    | method operator []= takes integer key, $TYPE$ value returns nothing
    |     assign "value" to index "key"
    |
    | method operator [] takes integer key returns $TYPE$
    |     load the value at index "key"
    |
    | method has takes integer key returns boolean
    |     whether or not the key was assigned
    |
    ----------------
    struct TableArray
    | static method operator [] takes integer array_size returns TableArray
    |     create a new array of Tables of size "array_size"
    |
    | method destroy takes nothing returns nothing
    |     destroy it
    |
    | method flush takes nothing returns nothing
    |     flush and destroy it
    |
    | method operator size takes nothing returns integer
    |     returns the size of the TableArray
    |
    | method operator [] takes integer key returns Table
    |     returns a Table accessible exclusively to index "key"
*/

   
globals
    private integer less = 0    //Index generation for TableArrays (below 0).
    private integer more = 8190 //Index generation for Tables.
    //Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
   
    private hashtable ht = InitHashtable()
    private key sizeK
    private key listK
endglobals

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

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

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

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

//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
   
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
   
struct Table extends array
   
    // Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
    implement realm
    implement integerm
    implement booleanm
    implement stringm
    implement playerm
    implement widgetm
    implement destructablem
    implement itemm
    implement unitm
    implement abilitym
    implement timerm
    implement triggerm
    implement triggerconditionm
    implement triggeractionm
    implement eventm
    implement forcem
    implement groupm
    implement locationm
    implement rectm
    implement boolexprm
    implement soundm
    implement effectm
    implement unitpoolm
    implement itempoolm
    implement questm
    implement questitemm
    implement defeatconditionm
    implement timerdialogm
    implement leaderboardm
    implement multiboardm
    implement multiboarditemm
    implement trackablem
    implement dialogm
    implement buttonm
    implement texttagm
    implement lightningm
    implement imagem
    implement ubersplatm
    implement regionm
    implement fogstatem
    implement fogmodifierm
    implement hashtablem

    method operator handle takes nothing returns handles
        return this
    endmethod

    method operator agent takes nothing returns agents
        return this
    endmethod

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

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

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

    //call tb.remove(294080)
    method remove takes integer key returns nothing
        call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
    endmethod
   
    //Remove all data from a Table instance
    method flush takes nothing returns nothing
        call FlushChildHashtable(ht, this)
    endmethod

    //local Table tb = Table.create()
    static method create takes nothing returns Table
        local Table this = dex.list[0]

        if this == 0 then
            set this = more + 1
            set more = this
        else
            set dex.list[0] = dex.list[this]
            call dex.list.remove(this) //Clear hashed memory
        endif

        debug set dex.list[this] = -1
        return this
    endmethod

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

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

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

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

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

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

endstruct

endlibrary
library WorldBounds /* v2.0.0.0
************************************************************************************
*
*   struct WorldBounds extends array
*
*       Fields
*       -------------------------
*
*           readonly static integer maxX
*           readonly static integer maxY
*           readonly static integer minX
*           readonly static integer minY
*
*           readonly static integer centerX
*           readonly static integer centerY
*
*           readonly static rect world
*           readonly static region worldRegion
*
************************************************************************************/

    private module WorldBoundInit
        private static method onInit takes nothing returns nothing
            set world=GetWorldBounds()

            set maxX = R2I(GetRectMaxX(world))
            set maxY = R2I(GetRectMaxY(world))
            set minX = R2I(GetRectMinX(world))
            set minY = R2I(GetRectMinY(world))
           
            set centerX = R2I((maxX + minX)/2)
            set centerY = R2I((minY + maxY)/2)
           
            set 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 ResourcePreloader /* v1.4c


    */
uses /*

    */
optional Table        /*   https://www.hiveworkshop.com/threads/snippet-new-table.188084/
    */
optional BJObjectId   /*   https://www.hiveworkshop.com/threads/bjobjectid.287128/
    */
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 SetUnitY(dummy, GetRectMaxY(world) + 1000)
            call UnitAddAbility(dummy, 'AInv')
            call UnitAddAbility(dummy, 'Avul')
            call UnitRemoveAbility(dummy, 'Amov')
            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
Requirements of the requirements of the TorrentArray library
library DummyRecycler /*
 
//                      DummyRecycler v1.25
//                          by Flux
//
//  A system that recycles dummy units while considering their facing angle.
//  It can be used as attachment dummies for visual effects 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 there are reports that will always leave a permanent tiny bit of
//      memory (0.04 KB).
//      On average, retrieving a pending Dummy is approximately 4x faster compared
//      to creating a new one and recycling a Dummy compared to removing it is
//      approximately 1.3x faster.
//      Furthermore, if you're using a lot of "Unit has entered map" events,
//      using this system will even result to even more better performance
//      because retrieving Dummy units does not cause that event to run.


    */
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.
//
//  function ShowDummy takes unit u, boolean flag returns nothing
//      - Shows/hides Dummy Unit without conflicting with the Locust ability.
//
//--------------------
//      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 = 'h000'
     
        //The owner of the Dummy Unit
        private constant player OWNER = Player(14)
     
        //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)
        call ShowDummy(bj_lastCreatedUnit, true)
    //! 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 ShowDummy takes unit u, boolean flag returns nothing
        if IsUnitHidden(u) == flag then
            call ShowUnit(u, flag)
            if flag and GetUnitTypeId(u) == DUMMY_ID then
                call UnitRemoveAbility(u, 'Aloc')
                call UnitAddAbility(u, 'Aloc')
            endif
        endif
    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 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 AutoFly initializer onInit requires UnitDex
/***************************************************************
*
*   v1.0, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   AutoFly adds & removes crow of 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 Alloc /* v1.3.1.1
*************************************************************************************
*
*   */
uses /*
*
*       */
optional ErrorMessage    /*      github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*       */
optional MemoryAnalysis  /*      
*
*************************************************************************************
*
*   Minimizes code generation and global variables while maintaining
*   excellent performance.
*
*       local thistype this = recycler[0]
*
*       if (recycler[this] == 0) then
*           set recycler[0] = this + 1
*       else
*           set recycler[0] = recycler[this]
*       endif
*
************************************************************************************
*
*   module Alloc
*
*       static method allocate takes nothing returns thistype
*       method deallocate takes nothing returns nothing
*
*       The Following Require Error Message To Be In The Map
*       --------------------------------------------------------
*
*           debug readonly boolean allocated
*
*       The Following Require Memory Analysis To Be In The Map
*       --------------------------------------------------------
*
*           debug readonly integer monitorCount
*           -   the amount of global memory being monitored by this
*           debug readonly integer monitorString
*           -   gets a string representation of all global memory being monitored by this
*           debug readonly integer address
*           -   global memory address for debugging
*           -   used with monitor and stopMonitor
*
*           debug static method calculateMemoryUsage takes nothing returns integer
*           debug static method getAllocatedMemoryAsString takes nothing returns string
*
*           debug method monitor takes string label, integer address returns nothing
*           -   monitor a global memory address with a label
*           -   used to identify memory leaks
*           -   should be memory that ought to be destroyed by the time this is destroyed
*           debug method stopMonitor takes integer address returns nothing
*           -   stops monitoring global memory
*           debug method stopMonitorValue takes handle monitoredHandle returns nothing
*           -   stops monitoring handle values
*
*           The Following Are Used To Monitor Handle Values
*
*               debug method monitor_widget             takes string label, widget              handleToTrack returns nothing
*               debug method monitor_destructable       takes string label, destructable        handleToTrack returns nothing
*               debug method monitor_item               takes string label, item                handleToTrack returns nothing
*               debug method monitor_unit               takes string label, unit                handleToTrack returns nothing
*               debug method monitor_timer              takes string label, timer               handleToTrack returns nothing
*               debug method monitor_trigger            takes string label, trigger             handleToTrack returns nothing
*               debug method monitor_triggercondition   takes string label, triggercondition    handleToTrack returns nothing
*               debug method monitor_triggeraction      takes string label, triggeraction       handleToTrack returns nothing
*               debug method monitor_force              takes string label, force               handleToTrack returns nothing
*               debug method monitor_group              takes string label, group               handleToTrack returns nothing
*               debug method monitor_location           takes string label, location            handleToTrack returns nothing
*               debug method monitor_rect               takes string label, rect                handleToTrack returns nothing
*               debug method monitor_boolexpr           takes string label, boolexpr            handleToTrack returns nothing
*               debug method monitor_effect             takes string label, effect              handleToTrack returns nothing
*               debug method monitor_unitpool           takes string label, unitpool            handleToTrack returns nothing
*               debug method monitor_itempool           takes string label, itempool            handleToTrack returns nothing
*               debug method monitor_quest              takes string label, quest               handleToTrack returns nothing
*               debug method monitor_defeatcondition    takes string label, defeatcondition     handleToTrack returns nothing
*               debug method monitor_timerdialog        takes string label, timerdialog         handleToTrack returns nothing
*               debug method monitor_leaderboard        takes string label, leaderboard         handleToTrack returns nothing
*               debug method monitor_multiboard         takes string label, multiboard          handleToTrack returns nothing
*               debug method monitor_multiboarditem     takes string label, multiboarditem      handleToTrack returns nothing
*               debug method monitor_dialog             takes string label, dialog              handleToTrack returns nothing
*               debug method monitor_button             takes string label, button              handleToTrack returns nothing
*               debug method monitor_texttag            takes string label, texttag             handleToTrack returns nothing
*               debug method monitor_lightning          takes string label, lightning           handleToTrack returns nothing
*               debug method monitor_image              takes string label, image               handleToTrack returns nothing
*               debug method monitor_ubersplat          takes string label, ubersplat           handleToTrack returns nothing
*               debug method monitor_region             takes string label, region              handleToTrack returns nothing
*               debug method monitor_fogmodifier        takes string label, fogmodifier         handleToTrack returns nothing
*               debug method monitor_hashtable          takes string label, hashtable           handleToTrack returns nothing
*
************************************************************************************/

    module Alloc
        /*
        *   stack
        */

        private static integer array recycler
       
        static if LIBRARY_MemoryAnalysis then
            debug private MemoryMonitor globalAddress
           
            debug method operator address takes nothing returns integer
                debug call ThrowError(recycler[this] != -1, "Alloc", "address", "thistype", this, "Attempted To Access Null Instance.")
                debug return globalAddress
            debug endmethod
        endif
       
        /*
        *   allocation
        */

        static method allocate takes nothing returns thistype
            local thistype this = recycler[0]
           
            static if LIBRARY_ErrorMessage then
                debug call ThrowError(this == 8192, "Alloc", "allocate", "thistype", 0, "Overflow.")
            endif
           
            if (recycler[this] == 0) then
                set recycler[0] = this + 1
            else
                set recycler[0] = recycler[this]
            endif
           
            static if LIBRARY_ErrorMessage then
                debug set recycler[this] = -1
            endif
           
            static if LIBRARY_MemoryAnalysis then
                debug set globalAddress = MemoryMonitor.allocate("thistype")
            endif
           
            return this
        endmethod
       
        method deallocate takes nothing returns nothing
            static if LIBRARY_ErrorMessage then
                debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
            endif
           
            static if LIBRARY_MemoryAnalysis then
                debug call globalAddress.deallocate()
                debug set globalAddress = 0
            endif
           
            set recycler[this] = recycler[0]
            set recycler[0] = this
        endmethod
       
        static if LIBRARY_MemoryAnalysis then
            debug method monitor takes string label, integer address returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor(label, address)
            debug endmethod
            debug method stopMonitor takes integer address returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitor", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.stopMonitor(address)
            debug endmethod
            debug method stopMonitorValue takes handle monitoredHandle returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitorValue", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.stopMonitorValue(monitoredHandle)
            debug endmethod
           
            debug method operator monitorCount takes nothing returns integer
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitorCount", "thistype", this, "Attempted To Access Null Instance.")
                debug return globalAddress.monitorCount
            debug endmethod
            debug method operator monitorString takes nothing returns string
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitorString", "thistype", this, "Attempted To Access Null Instance.")
                debug return globalAddress.monitorString
            debug endmethod
           
            debug method monitor_widget             takes string label, widget              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_widget", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_widget(label, handleToTrack)
            debug endmethod
            debug method monitor_destructable       takes string label, destructable        handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_destructable", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_destructable(label, handleToTrack)
            debug endmethod
            debug method monitor_item               takes string label, item                handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_item", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_item(label, handleToTrack)
            debug endmethod
            debug method monitor_unit               takes string label, unit                handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unit", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_unit(label, handleToTrack)
            debug endmethod
            debug method monitor_timer              takes string label, timer               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timer", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_timer(label, handleToTrack)
            debug endmethod
            debug method monitor_trigger            takes string label, trigger             handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_trigger", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_trigger(label, handleToTrack)
            debug endmethod
            debug method monitor_triggercondition   takes string label, triggercondition    handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggercondition", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_triggercondition(label, handleToTrack)
            debug endmethod
            debug method monitor_triggeraction      takes string label, triggeraction       handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggeraction", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_triggeraction(label, handleToTrack)
            debug endmethod
            debug method monitor_force              takes string label, force               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_force", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_force(label, handleToTrack)
            debug endmethod
            debug method monitor_group              takes string label, group               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_group", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_group(label, handleToTrack)
            debug endmethod
            debug method monitor_location           takes string label, location            handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_location", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_location(label, handleToTrack)
            debug endmethod
            debug method monitor_rect               takes string label, rect                handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_rect", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_rect(label, handleToTrack)
            debug endmethod
            debug method monitor_boolexpr           takes string label, boolexpr            handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_boolexpr", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_boolexpr(label, handleToTrack)
            debug endmethod
            debug method monitor_effect             takes string label, effect              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_effect", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_effect(label, handleToTrack)
            debug endmethod
            debug method monitor_unitpool           takes string label, unitpool            handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unitpool", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_unitpool(label, handleToTrack)
            debug endmethod
            debug method monitor_itempool           takes string label, itempool            handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_itempool", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_itempool(label, handleToTrack)
            debug endmethod
            debug method monitor_quest              takes string label, quest               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_quest", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_quest(label, handleToTrack)
            debug endmethod
            debug method monitor_defeatcondition    takes string label, defeatcondition     handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_defeatcondition", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_defeatcondition(label, handleToTrack)
            debug endmethod
            debug method monitor_timerdialog        takes string label, timerdialog         handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timerdialog", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_timerdialog(label, handleToTrack)
            debug endmethod
            debug method monitor_leaderboard        takes string label, leaderboard         handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_leaderboard", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_leaderboard(label, handleToTrack)
            debug endmethod
            debug method monitor_multiboard         takes string label, multiboard          handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboard", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_multiboard(label, handleToTrack)
            debug endmethod
            debug method monitor_multiboarditem     takes string label, multiboarditem      handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboarditem", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_multiboarditem(label, handleToTrack)
            debug endmethod
            debug method monitor_dialog             takes string label, dialog              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_dialog", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_dialog(label, handleToTrack)
            debug endmethod
            debug method monitor_button             takes string label, button              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_button", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_button(label, handleToTrack)
            debug endmethod
            debug method monitor_texttag            takes string label, texttag             handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_texttag", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_texttag(label, handleToTrack)
            debug endmethod
            debug method monitor_lightning          takes string label, lightning           handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_lightning", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_lightning(label, handleToTrack)
            debug endmethod
            debug method monitor_image              takes string label, image               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_image", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_image(label, handleToTrack)
            debug endmethod
            debug method monitor_ubersplat          takes string label, ubersplat           handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_ubersplat", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_ubersplat(label, handleToTrack)
            debug endmethod
            debug method monitor_region             takes string label, region              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_region", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_region(label, handleToTrack)
            debug endmethod
            debug method monitor_fogmodifier        takes string label, fogmodifier         handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_fogmodifier", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_fogmodifier(label, handleToTrack)
            debug endmethod
            debug method monitor_hashtable          takes string label, hashtable           handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_hashtable", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_hashtable(label, handleToTrack)
            debug endmethod
           
            static if DEBUG_MODE then
                //! runtextmacro optional MEMORY_ANALYSIS_STATIC_FIELD_NEW("recycler")
               
                static method calculateMemoryUsage takes nothing returns integer
                    return calculateAllocatedMemory__recycler()
                endmethod
               
                static method getAllocatedMemoryAsString takes nothing returns string
                    return allocatedMemoryString__recycler()
                endmethod
            endif
        endif
       
        /*
        *   analysis
        */

        static if LIBRARY_ErrorMessage then
            debug method operator allocated takes nothing returns boolean
                debug return recycler[this] == -1
            debug endmethod
        endif
       
        /*
        *   initialization
        */

        private static method onInit takes nothing returns nothing
            set recycler[0] = 1
        endmethod
    endmodule
endlibrary
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 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-opem 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 = thistype(0).prev.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
        else
            if this.dur < dur then
                set this.dur = dur
            endif
        endif
    endmethod

    implement Init
endstruct

endlibrary
/*****************************************************************************
*
*    RegisterNativeEvent v1.0.1.0
*       by Bannar
*
*    Storage of trigger handles for native events.
*
******************************************************************************
*
*    Optional requirements:
*
*       Table by Bribe
*          hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
******************************************************************************
*
*    Important:
*
*       Avoid using TriggerSleepAction within functions registered.
*       Destroy native event trigger on your own responsibility.
*
******************************************************************************
*
*    Functions:
*
*       function IsNativeEventRegistered takes integer whichIndex, integer eventId returns boolean
*          whether index whichIndex has already been attached to event with id eventId
*
*       function RegisterNativeEvent takes integer whichIndex, integer eventId returns boolean
*          attaches index whichIndex to eventId if it hasn't been attached already and creates new trigger handle if needed
*
*       function GetPlayerIdNativeEventTrigger takes integer playerId, integer eventId returns trigger
*          retrieves trigger handle for event with id eventId specific to player with id playerId
*
*       function GetNativeEventTrigger takes integer eventId returns trigger
*          retrieves trigger handle for event with id eventId
*
*****************************************************************************/

library RegisterNativeEvent uses optional Table

globals
    private trigger array triggers
endglobals

private module NativeEventInit
    private static method onInit takes nothing returns nothing
        static if LIBRARY_Table then
            set table = TableArray[122] // + 700
        endif
    endmethod
endmodule

private struct NativeEvent extends array
    static if LIBRARY_Table then
        static TableArray table
    else
        static hashtable table = InitHashtable()
    endif

    static method operator[] takes integer eventId returns integer
        if ( eventId > 286 ) then
            return eventId - 174
        elseif ( eventId > 259 ) then
            return eventId - 165
        elseif ( eventId > 91 ) then
            return eventId - 164
        endif
        return eventId
    endmethod

    implement NativeEventInit
endstruct

private function RegisterEvent takes integer whichIndex, integer eventId returns nothing
    static if LIBRARY_Table then
        set NativeEvent.table[NativeEvent[eventId]].integer[whichIndex] = eventId
    else
        call SaveInteger(NativeEvent.table, NativeEvent[eventId], whichIndex, eventId)
    endif
endfunction

private function GetNativeEventTriggerId takes integer whichIndex, integer eventId returns integer
    if ( whichIndex >= 16 ) then
        return eventId
    endif
    return 16 * eventId + whichIndex
endfunction

function IsNativeEventRegistered takes integer whichIndex, integer eventId returns boolean
    static if LIBRARY_Table then
        return NativeEvent.table[NativeEvent[eventId]].has(whichIndex)
    else
        return HaveSavedInteger(NativeEvent.table, NativeEvent[eventId], whichIndex)
    endif
endfunction

function RegisterNativeEvent takes integer whichIndex, integer eventId returns boolean
    local integer id = GetNativeEventTriggerId(whichIndex, eventId)
    if not IsNativeEventRegistered(whichIndex, eventId) then
        call RegisterEvent(whichIndex, eventId)
        if ( triggers[id] == null ) then
            set triggers[id] = CreateTrigger()
        endif
        return true
    endif
    return false
endfunction

function GetPlayerIdNativeEventTrigger takes integer playerId, integer eventId returns trigger
    return triggers[GetNativeEventTriggerId(playerId, eventId)]
endfunction

function GetNativeEventTrigger takes integer eventId returns trigger
    return GetPlayerIdNativeEventTrigger(16, eventId)
endfunction

endlibrary
/*****************************************************************************
*
*    RegisterPlayerUnitEvent v1.0.2.0
*       by Bannar
*
*    Register version of TriggerRegisterPlayerUnitEvent.
*
******************************************************************************
*
*    Requirements:
*
*       RegisterNativeEvent by Bannar
*          hiveworkshop.com/forums/submissions-414/snippet-registerevent-pack-250266/
*
******************************************************************************
*
*    constant boolean RPUE_VERSION_NEW
*       Defines API style. Choose between compatibility with standard RPUE or Blizzard alike interface
*
*
*    Functions:
*
*       function Register(Any)PlayerUnitEvent takes playerunitevent whichEvent, code cb returns nothing
*          registers generic playerunitevent whichEvent adding code cb as callback
*
*       function RegisterPlayerUnitEvent(ForPlayer) takes player whichPlayer, playerunitevent whichEvent, code cb returns nothing
*          registers playerunitevent whichEvent for player whichPlayer adding code cb as callback
*
*       function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
*          retrieves trigger handle for playerunitevent whichEvent
*
*       function GetPlayerUnitEventTriggerForPlayer takes player whichPlayer, playerunitevent whichEvent returns trigger
*          retrieves trigger handle for playerunitevent whichEvent specific to player whichPlayer
*
*****************************************************************************/

library RegisterPlayerUnitEvent requires RegisterNativeEvent

globals
    constant boolean RPUE_VERSION_NEW = false
endglobals

//! textmacro_once DEFINE_REGISTER_PLAYER_UNIT_EVENT takes GENERIC, SPECIFIC
function Register$GENERIC$PlayerUnitEvent takes playerunitevent whichEvent, code cb returns nothing
    local integer eventId = GetHandleId(whichEvent)
    local integer index = 0
    local trigger t = null

    if RegisterNativeEvent(16, eventId) then
        set t = GetNativeEventTrigger(eventId)
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(index), whichEvent, null)
            set index = index + 1
            exitwhen index == 16
        endloop
        set t = null
    endif

    call TriggerAddCondition(GetNativeEventTrigger(eventId), Condition(cb))
endfunction

function RegisterPlayerUnitEvent$SPECIFIC$ returns nothing
    local integer playerId = GetPlayerId(whichPlayer)
    local integer eventId = GetHandleId(whichEvent)

    if RegisterNativeEvent(playerId, eventId) then
        call TriggerRegisterPlayerUnitEvent(GetPlayerIdNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent, null)
    endif

    call TriggerAddCondition(GetPlayerIdNativeEventTrigger(playerId, eventId), Condition(cb))
endfunction
//! endtextmacro

static if RPUE_VERSION_NEW then
    //! runtextmacro DEFINE_REGISTER_PLAYER_UNIT_EVENT("Any", " takes player whichPlayer, playerunitevent whichEvent, code cb")
else
    //! runtextmacro DEFINE_REGISTER_PLAYER_UNIT_EVENT("", "ForPlayer takes playerunitevent whichEvent, code cb, player whichPlayer")
endif

function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
    return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction

function GetPlayerUnitEventTriggerForPlayer takes player whichPlayer, playerunitevent whichEvent returns trigger
    return GetPlayerIdNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction

endlibrary