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

Trigger Viewer

Attack System v1.1.w3x
Variables
AttackSystem
Main System
AttackSystem
GUI Support
AttackSystemGUI
AttackSystemGUI Documentation
Add-on
EvasionSystem
AttackModifier
AttackMissileUtils
Requirements
UnitDex
Table
GetClosestWidget
ErrorMessage
RegisterNativeEvent
RegisterPlayerUnitEvent
OrderIds
NxList
ImageStruct
ImageTools
TestMap
Test Setup
TestInit
AttackModifierTest
HealAllies
EvasionTest
GUI Support Demo
Registration Demo
EventHandler Demo
TestMap Requirements
StunSystem
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.

		
Name Type Is Array Initial Value
AttackSystem__DamageAmount real No
AttackSystem__Event real No
AttackSystem__IsAttackMain boolean No
AttackSystem__IsAttackMelee boolean No
AttackSystem__TargetDest destructable No
AttackSystem__TargetItem item No
AttackSystem__TargetPoint location No
AttackSystem__TargetUnit unit No
AttackSystem__TriggerAttack integer No
AttackSystem__TriggerUnit unit No
AttackSystem_Acceleration real No
AttackSystem_AcquireRange real No
AttackSystem_Arc real No
AttackSystem_Attack integer No
AttackSystem_AttackType attacktype No
AttackSystem_BounceRange real No
AttackSystem_CombatSound weapontype No
AttackSystem_Cooldown real No
AttackSystem_DamagePoint real No
AttackSystem_DamageType damagetype No
AttackSystem_DESTROY_ATTACK integer No
AttackSystem_DISABLE_UNIT integer No
AttackSystem_ENABLE_UNIT integer No
AttackSystem_ImpactZ real No
AttackSystem_LaunchX real No
AttackSystem_LaunchY real No
AttackSystem_LaunchZ real No
AttackSystem_MaxBounces integer No
AttackSystem_MaxDamage real No
AttackSystem_MaxSpeed real No
AttackSystem_MaxTargets integer No
AttackSystem_Melee boolean No
AttackSystem_MinDamage real No
AttackSystem_MinSpeed real No
AttackSystem_Model string No
AttackSystem_Operation integer No
AttackSystem_OperationTrigger trigger No
AttackSystem_Range real No
AttackSystem_REGISTER_UNIT integer No
AttackSystem_Scale real No
AttackSystem_ShowShadow boolean No
AttackSystem_Speed real No
AttackSystem_Unit unit No
AttackSystem_UNREGISTER_UNIT integer No
AttackSystem_WantDestroyAttack boolean No
u = x cosA + y sinA
v = y cosA - x sinA

z = 4 * h * x * (d -x) / (d * d) + x * (zd-z0) / d + z0
library AttackSystem /* v1.1


    */
uses /*

    */
OrderIds                          /*    https://www.hiveworkshop.com/threads/290882/
    */
Table                             /*    https://www.hiveworkshop.com/threads/188084/
    */
UnitDex                           /*    https://www.hiveworkshop.com/threads/248209/
    */
GetClosestWidget                  /*    https://www.hiveworkshop.com/threads/204217/
    */
ImageStruct                       /*    https://www.hiveworkshop.com/threads/271099/

    */
optional RegisterPlayerUnitEvent  /*    https://www.hiveworkshop.com/threads/250266/
    */
optional WorldBounds              /*    https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j/
    */
optional ErrorMessage             /*    https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage/main.j/


    [Resource Thread] :     https://www.hiveworkshop.com/threads/307417/


    */
//! novjass

    |-------------|
    | Description |
    |-------------|
    /*
        A fully functional alternative attack system made to closely mimic the behavior of the
        default Warcraft 3 attack system. Below are some pros and cons of this system compared
        to the game's default attack system:

        Pros:
            - All fields can be changed dynamically (range, acquisition range, projectile speed, etc.)
            - Full control over the lauched attacks' stats (damage, attacktype, damagetype, Missile,
              etc., which is OP :D)
            - Events for the various phases in the attacking process
            - Can retrieve the 'raw', unaltered damage of an attack
            - Attacks can have negative damage amount, in which case, the target will be healed
            - Projectiles of ranged attacks can have > 1 special effects attached

        Cons:
            - The synchronization of the unit's attack animation and the attack release is not as
              flawless as the default attack system
            - Retrieving the current order of an actively attacking unit does not return "attack"
              nor "channel" (which is the order of the dummy attack ability used)
            - You need to manually set the units' attack cooldown

        Known Issues:
            - Ethereal units, by the time they lose their ethereal status, will only auto-attack/
              auto-acquire targets after the next order given to them is finished

        Common practical issues that can easily be solved using this system:
            - Custom Evasion
            - Custom Critical Strike / Bash (without redundantly triggering a damage event)
            - Easily configuring a split shot and bounce (like Moon Glaive) attacks
            - Combination of split shot and bounce attacks (Ex: Three projectiles, each bouncing twice
              per attack)
            - Differentiation between melee and ranged attacks

    */


    |---------|
    | Credits |
    |---------|
    /*
        AGD         - Author
        Wareditor   - Special effects 3D facing algorithm
        MyPad       - Allocation Algorithm

    */


    |-----------|
    | Importing |
    |-----------|
    /*
        1. Install all the required libraries listed above
        2. Copy all the necessary object data for this system
            - Attack (AttackSystem)             - Ability
        3. Paste this library into your map
        4. Read the API documentation and configure the system settings below (Important!)

    */


    |-----|
    | API |
    |-----|
    /*
        Attack Event Types

      */
constant integer EVENT_ATTACK_START/*
        - Fires when a unit begins its attack animation
      */
constant integer EVENT_ATTACK_FINISH/*
        - Fires when a unit lands its attack on the target (for melee) or when a unit releases its main projectile (for ranged)
      */
constant integer EVENT_ATTACK_MISSILE_LAUNCH/*
        - Fires when a ranged unit releases its projectile (the event EVENT_ATTACK_FINISH fires before this one)
      */
constant integer EVENT_ATTACK_MISSILE_IMPACT/*
        - Fires when a projectile impacts the target (Including those bounce targets)
      */
constant integer EVENT_ATTACK_MISSILE_BOUNCE/*
        - Fires when a projectile bounces off a unit (the event EVENT_ATTACK_MISSILE_IMPACT fires before this one)
      */
constant integer EVENT_ATTACK_DESTROY/*
        - Fires when an attack instance is destroyed, whether explicitly or implicitly

      */
struct CustomAttack extends array/*

          */
readonly static CustomAttack triggerInstance/*  Event response
          */
readonly static Attack triggerAttack        /*  Event response

          */
readonly unit unit                          /*  The unit corresponding to this CustomAttack instance
          */
readonly widget target                      /*  The current target widget
          */
readonly unit targetUnit                    /*  The current target unit
          */
readonly item targetItem                    /*  The current target item
          */
readonly destructable targetDestructable    /*  The current target destructable
          */
readonly real targetX                       /*  The x-coordinate of the current target point
          */
readonly real targetY                       /*  The y-coordinate of the current target point
          */
boolean enabled                             /*  Determines if the unit's attack is enabled
          */
boolean melee                               /*  Determines if the unit's attacks are melee
          */
boolean showShadow                          /*  Determines if the unit's attacks' missiles' shadow is visible
          */
integer maxTargets                          /*  The maximum number of targets when attacking (for split-shot)
          */
integer maxBounces                          /*  The maximum number of bounces the unit's attacks
          */
real acquisitionRange                       /*  The target acquisition range
          */
real range                                  /*  The attack range
          */
real bounceRange                            /*  The projectile bounce range
          */
real cooldown                               /*  The attack cooldown in seconds (cooldown countdown starts after EVENT_ATTACK_FINISH fires)
          */
real damagePoint                            /*  The attack damage point in seconds
          */
real launchX                                /*  The x-offset of the projectile launch location on the unit's rotating coordinate system
          */
real launchY                                /*  The y-offset of the projectile launch location on the unit's rotating coordinate system
          */
real launchZ                                /*  The z-offset of the projectile launch location from the unit's origin
          */
real impactZ                                /*  The z-offset of the projectile impact location from the target's origin
          */
real arc                                    /*  The launch angle of a projectile from the ground (in radians)
          */
real speed                                  /*  The speed of the projectile
          */
real minSpeed                               /*  The minimum speed of a projectile
          */
real maxSpeed                               /*  The maximum speed of a projectile
          */
real acceleration                           /*  The acceleration of a projectile
          */
real scale                                  /*  The scale of the projectile
          */
real minDamage                              /*  The low-bound of the attack damage
          */
real maxDamage                              /*  The high-bound of the attack damage
          */
attacktype attacktype                       /*  The unit's attacktype
          */
damagetype damagetype                       /*  The unit's damagetype
          */
weapontype weapontype                       /*  The unit's weapontype
          */
string model                                /*  The model of the projectile

          */
static method   operator []             takes unit whichUnit                            returns CustomAttack/*
            - Returns the CustomAttack instance of a unit

          */
static method   isUnitRegistered        takes unit whichUnit                            returns boolean/*

          */
static method   register                takes unit whichUnit                            returns nothing/*
          */
static method   unregister              takes unit whichUnit                            returns nothing/*
            - Registers/Unregisters a unit to/from the system

          */
static method   registerEventHandler    takes integer whichEvent, code handlerFunc      returns nothing/*
          */
static method   unregisterEventHandler  takes integer whichEvent, code handlerFunc      returns nothing/*
          */
static method   clearEventHandlers      takes integer whichEvent                        returns nothing/*
            - Event handler methods

          */
method          addAttackSfx            takes string model                              returns this/*
          */
method          removeAttackSfx         takes string model                              returns this/*
          */
method          clearAttackSfx          takes nothing                                   returns this/*
            - Allows you to dynamically add as many number of special effects as you like to the projectile of a ranged attacker


      */
struct Attack extends array/*

          */
readonly boolean flag                       /*  Returns false if this Attack is destroyed/inactive
          */
readonly boolean main                       /*  Determines if this is an attack to the main target (Returns false for the Attacks for other split-shot targets)
          */
readonly boolean landed                     /*  Determines if the Attack's target successfully passes the filter condition at the time the Attack impacts the target
          */
readonly boolean melee                      /*  Determines if this is a melee Attack
          */
readonly boolean ranged                     /*  Determines if this is a ranged Attack
          */
readonly integer currentBounces             /*  The current number of bounces of the projectile
          */
readonly real x                             /*  The current x-coordinate
          */
readonly real y                             /*  The current y-coordinate
          */
readonly real z                             /*  The current z-coordinate (absolute)
          */
readonly unit source                        /*  The source unit of this Attack
          */
widget target                               /*  The target widget of this Attack
          */
unit targetUnit                             /*  The target unit of this Attack
          */
item targetItem                             /*  The target item of this Attack
          */
destructable targetDestructable             /*  The target destructable of this Attack
          */
real targetX                                /*  The target x-coordinate (You can only edit this value if target == null)
          */
real targetY                                /*  The target y-coordinate (You can only edit this value if target == null)
          */
real targetZ                                /*  The target z-coordinate (absolute) (You can only edit this value if target == null)
          */
real damage                                 /*  The raw damage amount of this Attack
          */
integer maxBounces                          /*  The maximum number of projectile bounces
          */
real bounceRange                            /*  The projectile bounce range
          */
real speed                                  /*  The projectile speed of a ranged Attack
          */
real minSpeed                               /*  The minimum possible speed
          */
real maxSpeed                               /*  The maximum possible speed
          */
real acceleration                           /*  The acceleration of a ranged Attack
          */
real scale                                  /*  The scale value of a ranged Attack
          */
real arc                                    /*  The arcing movement of a ranged Attack
          */
attacktype attacktype                       /*  The attacktype
          */
damagetype damagetype                       /*  The damagetype
          */
weapontype weapontype                       /*  The weapontype
          */
boolean showShadow                          /*  Determines if an Attack's shadow is visible

          */
static method   create      takes unit source, widget target, boolean isMainAttack, boolean fireEvent   returns Attack/*
            - Manually allocate an Attack without the need to check for the source unit's attack cooldown and range limitations
            - Also works for units' whose custom attacks are disabled
            - Does not play the source unit's attack animation nor interrupts the unit's current order
          */
method          destroy     takes nothing                                                               returns nothing/*
            - Force destroys an Attack

          */
method          addSfx      takes string model                                                          returns this/*
          */
method          removeSfx   takes string model                                                          returns this/*
          */
method          clearSfx    takes nothing                                                               returns this/*
            - Adds/Removes a model to/from a ranged Attack


        Wrapper Functions

          */
function GetEventAttackDamage               takes nothing                                       returns real/*
          */
function GetEventAttackSource               takes nothing                                       returns unit/*
          */
function GetEventAttackTarget               takes nothing                                       returns widget/*
          */
function GetEventAttackMaxBounces           takes nothing                                       returns integer/*
          */
function GetEventAttackCurrentBounces       takes nothing                                       returns integer/*
          */
function GetEventAttackTargetUnit           takes nothing                                       returns unit/*
          */
function GetEventAttackTargetItem           takes nothing                                       returns item/*
          */
function GetEventAttackTargetDestructable   takes nothing                                       returns destructable/*
          */
function GetEventAttackTargetX              takes nothing                                       returns real/*
          */
function GetEventAttackTargetY              takes nothing                                       returns real/*
          */
function GetEventAttackTargetZ              takes nothing                                       returns real/*
          */
function GetEventAttackMissileSpeed         takes nothing                                       returns real/*
          */
function GetEventAttackMissileAcceleration  takes nothing                                       returns real/*
          */
function GetEventAttackMissileScale         takes nothing                                       returns real/*
          */
function GetEventAttackMissileArc           takes nothing                                       returns real/*
          */
function GetEventAttackType                 takes nothing                                       returns attacktype/*
          */
function GetEventDamageType                 takes nothing                                       returns damagetype/*
          */
function GetEventWeaponType                 takes nothing                                       returns weapontype/*
          */
function IsEventAttackMelee                 takes nothing                                       returns boolean/*
          */
function IsEventAttackRanged                takes nothing                                       returns boolean/*
          */
function IsEventAttackMain                  takes nothing                                       returns boolean/*

          */
function DestroyEventAttack                 takes nothing                                       returns nothing/*
          */
function SetEventAttackDamage               takes real damage                                   returns nothing/*
          */
function SetEventAttackTarget               takes widget target                                 returns nothing/*
          */
function SetEventAttackTargetUnit           takes unit target                                   returns nothing/*
          */
function SetEventAttackTargetItem           takes item target                                   returns nothing/*
          */
function SetEventAttackTargetDestructable   takes destructable target                           returns nothing/*
          */
function SetEventAttackTargetX              takes real targetX                                  returns nothing/*
          */
function SetEventAttackTargetY              takes real targetY                                  returns nothing/*
          */
function SetEventAttackTargetZ              takes real targetZ                                  returns nothing/*
          */
function SetEventAttackMissileSpeed         takes real speed                                    returns nothing/*
          */
function SetEventAttackMissileMinSpeed      takes real speed                                    returns nothing/*
          */
function SetEventAttackMissileMaxSpeed      takes real speed                                    returns nothing/*
          */
function SetEventAttackMissileAcceleration  takes real acceleration                             returns nothing/*
          */
function SetEventAttackMissileScale         takes real scale                                    returns nothing/*
          */
function SetEventAttackMissileArc           takes real arc                                      returns nothing/*
          */
function SetEventAttackType                 takes attacktype whichAttackType                    returns nothing/*
          */
function SetEventDamageType                 takes damagetype whichDamageType                    returns nothing/*
          */
function SetEventWeaponType                 takes weapontype whichWeaponType                    returns nothing/*

          */
function GetUnitAttackDamageMin             takes unit whichUnit                                returns real/*
          */
function GetUnitAttackDamageMax             takes unit whichUnit                                returns real/*
          */
function GetUnitAttackType                  takes unit whichUnit                                returns attacktype/*
          */
function GetUnitDamageType                  takes unit whichUnit                                returns damagetype/*
          */
function GetUnitWeaponType                  takes unit whichUnit                                returns weapontype/*
          */
function GetUnitAttackModel                 takes unit whichUnit                                returns string/*
          */
function GetUnitAttackDamagePoint           takes unit whichUnit                                returns real/*
          */
function GetUnitAttackCooldown              takes unit whichUnit                                returns real/*
          */
function GetUnitAttackRange                 takes unit whichUnit                                returns real/*
          */
function GetUnitAttackAcquireRange          takes unit whichUnit                                returns real/*
          */
function GetUnitAttackMissileSpeed          takes unit whichUnit                                returns real/*
          */
function GetUnitAttackMissileMinSpeed       takes unit whichUnit                                returns real/*
          */
function GetUnitAttackMissileMaxSpeed       takes unit whichUnit                                returns real/*
          */
function GetUnitAttackLaunchX               takes unit whichUnit                                returns real/*
          */
function GetUnitAttackLaunchY               takes unit whichUnit                                returns real/*
          */
function GetUnitAttackLaunchZ               takes unit whichUnit                                returns real/*
          */
function GetUnitAttackImpactZ               takes unit whichUnit                                returns real/*
          */
function GetUnitAttackMaxTargets            takes unit whichUnit                                returns integer/*
          */
function GetUnitAttackMaxBounces            takes unit whichUnit                                returns integer/*
          */
function GetUnitAttackTarget                takes unit whichUnit                                returns widget/*
          */
function GetUnitAttackTargetUnit            takes unit whichUnit                                returns unit/*
          */
function GetUnitAttackTargetItem            takes unit whichUnit                                returns item/*
          */
function GetUnitAttackTargetDestructable    takes unit whichUnit                                returns destructable/*
          */
function IsUnitAttackShadowVisible          takes unit whichUnit                                returns boolean/*
          */
function IsUnitAttackMelee                  takes unit whichUnit                                returns boolean/*
          */
function IsUnitAttackRanged                 takes unit whichUnit                                returns boolean/*

          */
function SetUnitAttackDamageMin             takes unit whichUnit, real amount                   returns nothing/*
          */
function SetUnitAttackDamageMax             takes unit whichUnit, real amount                   returns nothing/*
          */
function SetUnitAttackType                  takes unit whichUnit, attacktype whichAttackType    returns nothing/*
          */
function SetUnitDamageType                  takes unit whichUnit, damagetype whichDamageType    returns nothing/*
          */
function SetUnitWeaponType                  takes unit whichUnit, weapontype whichWeaponType    returns nothing/*
          */
function SetUnitAttackModel                 takes unit whichUnit, string modelPath              returns nothing/*
          */
function SetUnitAttackDamagePoint           takes unit whichUnit, real damagePoint              returns nothing/*
          */
function SetUnitAttackCooldown              takes unit whichUnit, real cooldown                 returns nothing/*
          */
function SetUnitAttackRange                 takes unit whichUnit, real range                    returns nothing/*
          */
function SetUnitAttackAcquireRange          takes unit whichUnit, real acquireRange             returns nothing/*
          */
function SetUnitAttackMissileSpeed          takes unit whichUnit, real missileSpeed             returns nothing/*
          */
function SetUnitAttackMissileMinSpeed       takes unit whichUnit, real missileSpeed             returns nothing/*
          */
function SetUnitAttackMissileMaxSpeed       takes unit whichUnit, real missileSpeed             returns nothing/*
          */
function SetUnitAttackLaunchX               takes unit whichUnit, real launchX                  returns nothing/*
          */
function SetUnitAttackLaunchY               takes unit whichUnit, real launchY                  returns nothing/*
          */
function SetUnitAttackLaunchZ               takes unit whichUnit, real launchZ                  returns nothing/*
          */
function SetUnitAttackImpactZ               takes unit whichUnit, real impactZ                  returns nothing/*
          */
function SetUnitAttackMaxTargets            takes unit whichUnit, integer maxTargets            returns nothing/*
          */
function SetUnitAttackMaxBounces            takes unit whichUnit, integer maxBounces            returns nothing/*
          */
function SetUnitAttackShadowVisible         takes unit whichUnit, boolean visible               returns nothing/*
          */
function SetUnitAttackMelee                 takes unit whichUnit                                returns nothing/*
          */
function SetUnitAttackRanged                takes unit whichUnit                                returns nothing/*
          */
function UnitAddAttackEffect                takes unit whichUnit, string model                  returns nothing/*
          */
function UnitRemoveAttackEffect             takes unit whichUnit, string model                  returns nothing/*
          */
function UnitClearAttackEffect              takes unit whichUnit                                returns nothing/*

          */
function UnitCustomAttackEnable             takes unit whichUnit                                returns nothing/*
          */
function UnitCustomAttackDisable            takes unit whichUnit                                returns nothing/*
          */
function IsUnitCustomAttackEnabled          takes unit whichUnit                                returns boolean/*

          */
function RegisterAttackEventHandler         takes integer whichEvent, code handler              returns nothing/*
          */
function UnregisterAttackEventHandler       takes integer whichEvent, code handler              returns nothing/*

          */
function UnitCustomAttackRegister           takes unit whichUnit                                returns nothing/*
          */
function UnitCustomAttackUnregister         takes unit whichUnit                                returns nothing/*
          */
function IsUnitCustomAttackRegistered       takes unit whichUnit                                returns boolean/*


    */
//! endnovjass

    /*==============================================================================*/
    /*                             SYSTEM CONFIGURATION                             */
    /*==============================================================================*/
    private module SystemConstants
        /*
        *   Rawcode of the attack ability
        */

        static constant integer ATTACK_ABIL_ID                  = 'Atk1'
        /*
        *   Order id of the attack ability
        */

        static constant integer ATTACK_ORDER_ID                 = ORDER_channel
        /*
        *   The maximum deviation of the attacker's facing where it is still able to
        *   attack the target
        */

        static constant real ATTACK_ANGLE_TOLERANCE             = 10.00
        /*
        *   The periodic timer timeout
        */

        static constant real PERIOD                             = 1.00/32.00
        /*
        *   The image path for the attack missiles' shadow
        */

        static constant string MISSILE_SHADOW                   = "ReplaceableTextures\\Shadows\\Shadow.blp"
        /*
        *   The diameter of the attack missiles' shadow
        */

        static constant real MISSILE_SHADOW_SIZE                = 70.00
        /*
        *   The attack missiles' alpha value
        */

        static constant integer MISSILE_SHADOW_ALPHA            = 0x60
        /*
        *   The attack missiles' red value
        */

        static constant integer MISSILE_SHADOW_RED              = 0xFF
        /*
        *   The attack missiles' green value
        */

        static constant integer MISSILE_SHADOW_GREEN            = 0xFF
        /*
        *   The attack missiles' blue value
        */

        static constant integer MISSILE_SHADOW_BLUE             = 0xFF
    endmodule
    /*
    *   This set of values are automatically applied when a unit is registered to the system
    *   This module is optional and you can delete it if you don't need it
    */

    private module SystemDefaults
        set this.melee                                  = true
        set this.showShadow                             = false
        set this.maxTargets                             = 1
        set this.maxBounces                             = 0
        set this.acquisitionRange                       = 1000.00
        set this.range                                  = 600.00
        set this.bounceRange                            = 500.00
        set this.cooldown                               = 1.00
        set this.damagePoint                            = 0.35
        set this.launchX                                = 0.00
        set this.launchY                                = 20.00
        set this.launchZ                                = 60.00
        set this.impactZ                                = 60.00
        set this.arc                                    = 0.00
        set this.speed                                  = 1000000.00
        set this.minSpeed                               = 0.00
        set this.maxSpeed                               = 1000000.00
        set this.acceleration                           = 0.00
        set this.scale                                  = 1.00
        set this.model                                  = ""
        set this.attacktype                             = ATTACK_TYPE_HERO
        set this.damagetype                             = DAMAGE_TYPE_NORMAL
        set this.weapontype                             = WEAPON_TYPE_WHOKNOWS
        set this.minDamage                              = 0.00
        set this.maxDamage                              = 0.00
    endmodule
    /*
    *   This value is used to set the animation scale of an attacking unit.
    *   Ideally, this value should vary for different unit types so you might
    *   want to store those values into a hashtable/Table and make this function
    *   work this those values.
    *
    *   Example: return animationTable[GetUnitTypeId(this.unit)]/Pow(this.damagePoint, 1.00/1.18)
    */

    private function GetAttackAnimationScale takes CustomAttack this returns real
        return 0.35/Pow(this.damagePoint, 1.00/1.18)    // Be careful when changing this
    endfunction
    /*==============================================================================*/
    /*                         END OF SYSTEM CONFIGURATION                          */
    /*==============================================================================*/


    /*================================ System Code =================================*/
    native UnitAlive takes unit u returns boolean

    globals

        constant integer EVENT_ATTACK_START             = 0xABC + 0xAB*0
        constant integer EVENT_ATTACK_FINISH            = 0xABC + 0xAB*1
        constant integer EVENT_ATTACK_MISSILE_LAUNCH    = 0xABC + 0xAB*2
        constant integer EVENT_ATTACK_MISSILE_IMPACT    = 0xABC + 0xAB*3
        constant integer EVENT_ATTACK_MISSILE_BOUNCE    = 0xABC + 0xAB*4
        constant integer EVENT_ATTACK_DESTROY           = 0xABC + 0xAB*5

        private constant real LOOKAT_OFFSET             = 100.00
        private constant location loc                   = Location(0.00, 0.00)
        private timer missileTimer                      = CreateTimer()
        private CustomAttack eventInstance              = 0
        private Attack eventAttack                      = 0
        private TableArray sfxTable
        private key KEY
        private timer tempTimer
        private code onPeriodCode
        private unit tempUnit
        private item tempItem
        private destructable tempDest
        private integer array sfxCount
        private boolean array doFireEvent
        private unit dummyUnit

    endglobals

    private keyword Initializer
    private keyword Node

    static if DEBUG_MODE then
        private function DebugError takes boolean condition, string libraryName, string methodName, string objectName, integer object, string message returns nothing
            static if LIBRARY_ErrorMessage then
                call ThrowError(condition, libraryName, methodName, objectName, object, message)
            else
                if condition then
                    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[" + libraryName + "] ERROR: " + message)
                    if 1/0 == 0 then
                    endif
                endif
            endif
        endfunction

        private function DebugWarning takes boolean condition, string libraryName, string methodName, string objectName, integer object, string message returns nothing
            static if LIBRARY_ErrorMessage then
                call ThrowWarning(condition, libraryName, methodName, objectName, object, message)
            else
                if condition then
                    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[" + libraryName + "] WARNING: " + message)
                endif
            endif
        endfunction
    endif

    private function GetAngle takes real dA, real dB, real dz returns real
        local real iB
        if dA == 0.00 then
            return 0.00
        endif
        set iB = RAbsBJ(dB)
        return Atan(dz/dA)*(1-(iB/(RAbsBJ(dA)+iB)))
    endfunction

    private function SetEffectFacingXYZ takes effect e, real targetX, real targetY, real targetZ returns nothing
        local real dx = targetX - BlzGetLocalSpecialEffectX(e)
        local real dy = targetY - BlzGetLocalSpecialEffectY(e)
        local real dz = targetZ - BlzGetLocalSpecialEffectZ(e)
        call BlzSetSpecialEffectOrientation(e, GetAngle(dy, dx, dz), -GetAngle(dx, dy, dz), Atan2(dy,dx))
    endfunction

    private function GetSurfaceZ takes real x, real y returns real
        call MoveLocation(loc, x, y)
        return GetLocationZ(loc)
    endfunction

    private function GetWidgetCollision takes widget w returns real
        local integer index
        set Table(KEY).widget[0] = w
        set index = GetUnitId(Table(KEY).unit[0])
        call Table(KEY).handle.remove(0)
        return BlzGetUnitCollisionSize(GetUnitById(index))
    endfunction

    private function IsWidgetRemoved takes widget w returns boolean
        set Table(KEY).widget[0] = w
        set tempUnit = Table(KEY).unit[0]
        set tempItem = Table(KEY).item[0]
        set tempDest = Table(KEY).destructable[0]
        call Table(KEY).handle.remove(0)
        if tempUnit != null then
            return GetUnitTypeId(tempUnit) == 0
        elseif tempItem != null then
            return GetItemTypeId(tempItem) == 0
        elseif tempDest != null then
            return GetDestructableTypeId(tempDest) == 0
        endif
        return true
    endfunction

    private function TargetUnitFilter takes unit attacker, unit target returns boolean
        return attacker != target                                                                   and/*
            */
UnitAlive(target)                                                                    and/*
            */
GetUnitAbilityLevel(target, 'Aloc') == 0                                             and/*
            */
(IsUnitVisible(target, GetOwningPlayer(attacker)) or GetUnitTypeId(attacker) == 0)   and/*
            */
not IsUnitType(target, UNIT_TYPE_ETHEREAL)                                           and/*
            */
not BlzIsUnitInvulnerable(target)
    endfunction

    private function TargetItemFilter takes unit attacker, item target returns boolean
        return IsItemVisible(target) and not IsItemOwned(target) and not IsItemInvulnerable(target) and GetWidgetLife(target) >= 0.405
    endfunction

    private function TargetDestructableFilter takes unit attacker, destructable target returns boolean
        return not IsDestructableInvulnerable(target) and GetDestructableLife(target) >= 0.405
    endfunction

    private function DealDamage takes Attack attack, widget target, boolean ranged returns nothing
        local unit u = attack.source
        local boolean sourceRemoved = GetUnitTypeId(u) == 0
        local player prevOwner
        /*
        *   If the original source unit was removed, set the source to another valid unit. Otherwise, no damage will be dealt.
        */

        if sourceRemoved then
            set u = dummyUnit
            set prevOwner = GetOwningPlayer(u)
            call SetUnitOwner(u, Player(Node(attack).ownerId), false)
        endif
        if attack.damage < 0.00 then
            call SetWidgetLife(target, GetWidgetLife(target) - attack.damage + 0.001)
            /*
            *   If the damage is negative, it's still needed to deal damage to the target to maintain the desired
            *   behavior of an attacked target (ex: units with 'build' ability should retreat from the attacker)
            */

            call UnitDamageTarget(u, target, 0.001, true, ranged, attack.attacktype, attack.damagetype, attack.weapontype)
        else
            call UnitDamageTarget(u, target, attack.damage, true, ranged, attack.attacktype, attack.damagetype, attack.weapontype)
        endif
        if sourceRemoved then
            call SetUnitOwner(u, prevOwner, false)
            set prevOwner = null
        endif
        set u = null
    endfunction

    private function GetTimer takes integer data returns timer
        set tempTimer = CreateTimer()
        set Table(KEY)[GetHandleId(tempTimer)] = data
        return tempTimer
    endfunction
    private function DeleteTimer takes timer t returns integer
        local integer id = GetHandleId(t)
        local integer data = Table(KEY)[id]
        call PauseTimer(t)
        call DestroyTimer(t)
        call Table(KEY).remove(id)
        return data
    endfunction

    private function 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
    endfunction

    static if not LIBRARY_WorldBounds then
        private struct MapBounds extends array

            static real minX = 0.00
            static real minY = 0.00
            static real maxX = 0.00
            static real maxY = 0.00

            static method init takes nothing returns nothing
                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
            endmethod

        endstruct
    endif

    /*
    *   Event triggers are only created when there are handlers registered to the event.
    */

    private struct EventHandler extends array

        private static TableArray table
        private integer handlerCount
        private trigger handlerTrigger
        debug private string eventName

        static method operator [] takes thistype this returns trigger
            return thistype((this - 0xABC)/0xAB).handlerTrigger
        endmethod

        method register takes code c returns nothing
            local boolexpr expr = Filter(c)
            set this = (this - 0xABC)/0xAB
            debug call DebugError((this) < 0 or (this) > 5, "AttackSystem", "registerEventHandler()", "null", this, "Invalid input event type")
            debug call DebugError(c == null, "AttackSystem", "registerEventHandler()", this.eventName, 0, "Attempted to register a null code")
            debug call DebugError(table[this].handle.has(GetHandleId(expr)), "AttackSystem", "registerEventHandler()", this.eventName, 0, "Attemped to register a code twice")
            if this.handlerCount == 0 then
                set this.handlerTrigger = CreateTrigger()
            endif
            set this.handlerCount = this.handlerCount + 1
            set table[this].triggercondition[GetHandleId(expr)] = TriggerAddCondition(this.handlerTrigger, expr)
            set expr = null
        endmethod
        method unregister takes code c returns nothing
            local boolexpr expr = Filter(c)
            local integer exprId
            set this = (this - 0xABC)/0xAB
            debug call DebugError((this) < 0 or (this) > 5, "AttackSystem", "unregisterEventHandler()", "null", this, "Invalid input event type")
            debug call DebugError(c == null, "AttackSystem", "unregisterEventHandler()", this.eventName, 0, "Attempted to unregister a null code")
            debug call DebugError(not table[this].handle.has(GetHandleId(expr)), "AttackSystem", "unregisterEventHandler()", this.eventName, 0, "Attemped to unregister a code twice")
            set this.handlerCount = this.handlerCount - 1
            if this.handlerCount == 0 then
                call table[this].handle.remove(GetHandleId(expr))
                call DestroyTrigger(this.handlerTrigger)
                set this.handlerTrigger = null
            else
                set exprId = GetHandleId(expr)
                call TriggerRemoveCondition(this.handlerTrigger, table[this].triggercondition[exprId])
                call table[this].handle.remove(exprId)
            endif
            set expr = null
        endmethod
        method clear takes nothing returns nothing
            set this = (this - 0xABC)/0xAB
            debug call DebugError((this) < 0 or (this) > 5, "AttackSystem", "clearEventHandlers()", "null", this, "Invalid input event type")
            call DestroyTrigger(this.handlerTrigger)
            call table[this].flush()
            set this.handlerTrigger = null
            set this.handlerCount = 0
        endmethod

        static method init takes nothing returns nothing
            set table = TableArray[6]
            debug set thistype(0).eventName = "EVENT_ATTACK_START"
            debug set thistype(1).eventName = "EVENT_ATTACK_FINISH"
            debug set thistype(2).eventName = "EVENT_ATTACK_MISSILE_LAUNCH"
            debug set thistype(3).eventName = "EVENT_ATTACK_MISSILE_IMPACT"
            debug set thistype(4).eventName = "EVENT_ATTACK_MISSILE_BOUNCE"
            debug set thistype(5).eventName = "EVENT_ATTACK_DESTROY"
        endmethod

    endstruct

    private function FireHandler takes integer eventType, CustomAttack instance, Attack attack returns nothing
        local Attack prevAttack = eventAttack
        local CustomAttack prevInstance = eventInstance
        set eventInstance = instance
        set eventAttack = attack
        call TriggerEvaluate(EventHandler[eventType])
        set eventAttack = prevAttack
        set eventInstance = prevInstance
    endfunction

    private struct Node extends array

        private static integer array recycler
        static TableArray table
        thistype prev
        thistype next
        boolean flag
        boolean main
        boolean landed
        boolean melee
        unit source
        unit targetUnit
        item targetItem
        destructable targetDestructable
        integer currentBounces
        real arc
        real x
        real y
        real z
        real oX
        real oY
        real oZ
        real targetX
        real targetY
        real targetZ
        real lookAtX
        real lookAtY
        real lookAtZ
        real travelled
        real scale
        real speed
        real minSpeed
        real maxSpeed
        real acceleration
        real timeScale
        integer alpha
        integer red
        integer green
        integer blue
        integer ownerId
        Image shadow
        boolean shadowFlag

        method operator target takes nothing returns widget
            if this.targetUnit != null then
                return this.targetUnit
            elseif this.targetItem != null then
                return this.targetItem
            elseif this.targetDestructable != null then
                return this.targetDestructable
            endif
            return null
        endmethod
        method operator target= takes widget whichWidget returns nothing
            if whichWidget == null then
                set this.targetUnit = null
                set this.targetItem = null
                set this.targetDestructable = null
            else
                set Table(KEY).widget[0] = whichWidget
                set this.targetUnit = Table(KEY).unit[0]
                set this.targetItem = Table(KEY).item[0]
                set this.targetDestructable = Table(KEY).destructable[0]
                call Table(KEY).handle.remove(0)
            endif
        endmethod

        method getParabolaZ takes nothing returns real
            local real dx = this.targetX - this.x
            local real dy = this.targetY - this.y
            local real dz = this.targetZ - this.z
            local real d = SquareRoot(dx*dx + dy*dy) + this.travelled
            return (d*Tan(this.arc))*this.travelled*(d - this.travelled)/(d*d) + this.travelled*(this.targetZ - this.oZ)/d + this.oZ
        endmethod

        method move takes real x, real y, real z returns nothing
            local integer index = table[this][0]
            local effect sfx
            static if LIBRARY_WorldBounds then
                set x = GetBoundedValue(x, WorldBounds.minX, WorldBounds.maxX)
                set y = GetBoundedValue(y, WorldBounds.minY, WorldBounds.maxY)
            else
                set x = GetBoundedValue(x, MapBounds.minX, MapBounds.maxX)
                set y = GetBoundedValue(y, MapBounds.minY, MapBounds.maxY)
            endif
            loop
                exitwhen index == 0
                set sfx = table[this].effect[index]
                call BlzSetSpecialEffectPosition(sfx, x, y, z)
                call SetEffectFacingXYZ(sfx, this.lookAtX, this.lookAtY, this.lookAtZ)
                set sfx = null
                set index = index - 1
            endloop
            if this.shadow != 0 then
                call this.shadow.setPositionEx(x, y, 0.00)
            endif
            set this.x = x
            set this.y = y
            set this.z = z
        endmethod

        method addSfx takes string model returns nothing
            local integer key = StringHash(model)
            local integer index
            local effect sfx
            if not table[this].has(key) then
                set index = table[this][0] + 1
                set table[this][0] = index
                set sfx = AddSpecialEffect(model, this.x, this.y)
                call BlzSetSpecialEffectZ(sfx, this.z)
                call SetEffectFacingXYZ(sfx, this.lookAtX, this.lookAtY, this.lookAtZ)
                call BlzSetSpecialEffectScale(sfx, this.scale)
                call BlzSetSpecialEffectTimeScale(sfx, this.timeScale)
                call BlzSetSpecialEffectAlpha(sfx, this.alpha)
                call BlzSetSpecialEffectColor(sfx, this.red, this.green, this.blue)
                if this.shadow == 0 and this.shadowFlag then
                    set this.shadow = Image.create(CustomAttack.MISSILE_SHADOW, CustomAttack.MISSILE_SHADOW_SIZE, CustomAttack.MISSILE_SHADOW_SIZE, this.x - CustomAttack.MISSILE_SHADOW_SIZE*0.50, this.y - CustomAttack.MISSILE_SHADOW_SIZE*0.50, 0.00, IMAGE_TYPE_INDICATOR, true)
                    call this.shadow.wrap(true)
                    call SetImageColor(this.shadow.img, CustomAttack.MISSILE_SHADOW_RED, CustomAttack.MISSILE_SHADOW_GREEN, CustomAttack.MISSILE_SHADOW_BLUE, CustomAttack.MISSILE_SHADOW_ALPHA)
                endif
                set table[this].effect[index] = sfx
                set table[this][key] = index
                set sfx = null
            endif
        endmethod
        method removeSfx takes string model returns nothing
            local integer key = StringHash(model)
            local integer index = table[this][key]
            local integer count
            if index != 0 then
                set count = table[this][0]
                set table[this][0] = count - 1
                call DestroyEffect(table[this].effect[index])
                set table[this].effect[index] = table[this].effect[count]
                call table[this].handle.remove(count)
                call table[this].remove(key)
                if count == 1 then
                    if this.shadow != 0 then
                        call this.shadow.destroy()
                        set this.shadow = 0
                    endif
                endif
            endif
        endmethod
        method clearSfx takes nothing returns nothing
            local integer index = table[this][0]
            loop
                exitwhen index == 0
                call DestroyEffect(table[this].effect[index])
                set index = index - 1
            endloop
            call table[this].flush()
        endmethod

        static method create takes boolean ranged, real x, real y, real z, real scale, real arc, widget target returns thistype
            local thistype this = recycler[0]
            local real angle
            local real xy
            debug call DebugError(this == JASS_MAX_ARRAY_SIZE - 2, "AttackSystem", "create()", "Attack", 0, "Overflow")
            if recycler[this] == 0 then
                set this = this + 1
                set recycler[0] = this
            else
                set recycler[0] = recycler[this]
                set recycler[this] = 0
            endif
            set this.target = target
            set this.flag = true
            set this.melee = not ranged
            if ranged then
                if thistype(0).next == 0 then
                    call TimerStart(missileTimer, CustomAttack.PERIOD, true, onPeriodCode)
                endif
                set this.next = 0
                set this.prev = thistype(0).prev
                set thistype(0).prev.next = this
                set thistype(0).prev = this
                set this.alpha = 0xFF
                set this.red = 0xFF
                set this.green = 0xFF
                set this.blue = 0xFF
                set this.timeScale = 1.00
                set this.oX = x
                set this.oY = y
                set this.oZ = z
                set this.x = x
                set this.y = y
                set this.z = z
                set this.scale = scale
                set this.arc = arc
                set angle = Atan2(GetWidgetY(target) - y, GetWidgetX(target) - x)
                set xy = LOOKAT_OFFSET*Cos(arc)
                set this.lookAtX = x + xy*Cos(angle)
                set this.lookAtY = y + xy*Sin(angle)
                set this.lookAtZ = z + LOOKAT_OFFSET*Sin(arc)
            endif
            return this
        endmethod

        method destroy takes nothing returns nothing
            call FireHandler(EVENT_ATTACK_DESTROY, GetUnitId(this.source), this)
            if not this.melee then
                call this.clearSfx()
                if this.shadow != 0 then
                    call this.shadow.destroy()
                    set this.shadow = 0
                endif
                set this.shadowFlag = false
                set this.travelled = 0.00
                set this.next.prev = this.prev
                set this.prev.next = this.next
                if thistype(0).next == 0 then
                    call PauseTimer(missileTimer)
                endif
            endif
            set this.source = null
            set this.targetUnit = null
            set this.targetItem = null
            set this.targetDestructable = null
            set this.landed = false
            set this.flag = false
            set recycler[this] = recycler[0]
            set recycler[0] = this
        endmethod

        static method init takes nothing returns nothing
            set table = TableArray[JASS_MAX_ARRAY_SIZE - 1]
        endmethod

    endstruct

    /*
    *   An Attack's data is set by the time it is launched. By that time, it becomes
    *   independent from the stats of its source unit.
    */

    struct Attack extends array

        real damage
        real bounceRange
        integer maxBounces
        attacktype attacktype
        damagetype damagetype
        weapontype weapontype

        method operator flag takes nothing returns boolean
            return Node(this).flag
        endmethod

        method operator main takes nothing returns boolean
            return Node(this).main
        endmethod

        method operator landed takes nothing returns boolean
            return Node(this).landed
        endmethod

        method operator melee takes nothing returns boolean
            return Node(this).melee
        endmethod
        method operator ranged takes nothing returns boolean
            return not Node(this).melee
        endmethod

        method operator x takes nothing returns real
            return Node(this).x
        endmethod
        method operator y takes nothing returns real
            return Node(this).y
        endmethod
        method operator z takes nothing returns real
            return Node(this).z
        endmethod

        method operator targetX= takes real x returns nothing
            if Node(this).target == null then
                set Node(this).targetX = x
            endif
        endmethod
        method operator targetX takes nothing returns real
            return Node(this).targetX
        endmethod

        method operator targetY= takes real y returns nothing
            if Node(this).target == null then
                set Node(this).targetY = y
            endif
        endmethod
        method operator targetY takes nothing returns real
            return Node(this).targetY
        endmethod

        method operator targetZ= takes real z returns nothing
            if Node(this).target == null then
                set Node(this).targetZ = z
            endif
        endmethod
        method operator targetZ takes nothing returns real
            return Node(this).targetZ
        endmethod

        method operator source takes nothing returns unit
            return Node(this).source
        endmethod

        method operator targetUnit= takes unit whichUnit returns nothing
            set Node(this).targetUnit = whichUnit
        endmethod
        method operator targetUnit takes nothing returns unit
            return Node(this).targetUnit
        endmethod

        method operator targetItem= takes item whichItem returns nothing
            set Node(this).targetItem = whichItem
        endmethod
        method operator targetItem takes nothing returns item
            return Node(this).targetItem
        endmethod

        method operator targetDestructable= takes destructable whichDestructable returns nothing
            set Node(this).targetDestructable = whichDestructable
        endmethod
        method operator targetDestructable takes nothing returns destructable
            return Node(this).targetDestructable
        endmethod

        method operator target= takes widget whichWidget returns nothing
            set Node(this).target = whichWidget
        endmethod
        method operator target takes nothing returns widget
            return Node(this).target
        endmethod

        method operator speed= takes real value returns nothing
            set Node(this).speed = value
        endmethod
        method operator speed takes nothing returns real
            return Node(this).speed
        endmethod

        method operator minSpeed= takes real value returns nothing
            set Node(this).minSpeed = value
        endmethod
        method operator minSpeed takes nothing returns real
            return Node(this).minSpeed
        endmethod

        method operator maxSpeed= takes real value returns nothing
            set Node(this).maxSpeed = value
        endmethod
        method operator maxSpeed takes nothing returns real
            return Node(this).maxSpeed
        endmethod

        method operator acceleration= takes real value returns nothing
            set Node(this).acceleration = value
        endmethod
        method operator acceleration takes nothing returns real
            return Node(this).acceleration
        endmethod

        method operator scale= takes real scale returns nothing
            local integer index = Node.table[this][0]
            loop
                exitwhen index == 0
                call BlzSetSpecialEffectScale(Node.table[this].effect[index], scale)
                set index = index - 1
            endloop
            set Node(this).scale = scale
        endmethod
        method operator scale takes nothing returns real
            return Node(this).scale
        endmethod

        method operator arc= takes real value returns nothing
            set Node(this).arc = value
        endmethod
        method operator arc takes nothing returns real
            return Node(this).arc
        endmethod

        method operator currentBounces takes nothing returns integer
            return Node(this).currentBounces
        endmethod

        method operator showShadow= takes boolean show returns nothing
            if show then
                if not Node(this).shadowFlag and Node.table[this][0] > 0 then
                    set Node(this).shadow = Image.create(CustomAttack.MISSILE_SHADOW, CustomAttack.MISSILE_SHADOW_SIZE, CustomAttack.MISSILE_SHADOW_SIZE, this.x - CustomAttack.MISSILE_SHADOW_SIZE*0.50, this.y - CustomAttack.MISSILE_SHADOW_SIZE*0.50, 0.00, IMAGE_TYPE_INDICATOR, true)
                    call Node(this).shadow.wrap(true)
                    call SetImageColor(Node(this).shadow.img, CustomAttack.MISSILE_SHADOW_RED, CustomAttack.MISSILE_SHADOW_GREEN, CustomAttack.MISSILE_SHADOW_BLUE, CustomAttack.MISSILE_SHADOW_ALPHA)
                endif
            elseif Node(this).shadowFlag and Node(this).shadow != 0 then
                call Node(this).shadow.destroy()
                set Node(this).shadow = 0
            endif
            set Node(this).shadowFlag = show
        endmethod
        method operator showShadow takes nothing returns boolean
            return Node(this).shadowFlag
        endmethod

        method setColor takes integer alpha, integer red, integer green, integer blue returns thistype
            local integer index = Node.table[this][0]
            local effect sfx
            loop
                exitwhen index == 0
                set sfx = Node.table[this].effect[index]
                call BlzSetSpecialEffectAlpha(sfx, alpha)
                call BlzSetSpecialEffectColor(sfx, red, green, blue)
                set sfx = null
                set index = index - 1
            endloop
            set Node(this).alpha = alpha
            set Node(this).red = red
            set Node(this).green = green
            set Node(this).blue = blue
            return this
        endmethod

        method setTimeScale takes real value returns thistype
            local integer index = Node.table[this][0]
            loop
                exitwhen index == 0
                call BlzSetSpecialEffectTimeScale(Node.table[this].effect[index], value)
                set index = index - 1
            endloop
            set Node(this).timeScale = value
            return this
        endmethod

        method addSfx takes string model returns thistype
            call Node(this).addSfx(model)
            return this
        endmethod
        method removeSfx takes string model returns thistype
            call Node(this).removeSfx(model)
            return this
        endmethod
        method clearSfx takes nothing returns thistype
            call Node(this).clearSfx()
            return this
        endmethod

        private static method onMeleeAttackLand takes nothing returns nothing
            local Node attack = DeleteTimer(GetExpiredTimer())
            local Attack prevAttack
            local CustomAttack prevInstance
            if attack.targetUnit != null then
                set attack.landed = TargetUnitFilter(attack.source, attack.targetUnit)
            elseif attack.targetItem != null then
                set attack.landed = TargetItemFilter(attack.source, attack.targetItem)
            elseif attack.targetDestructable != null then
                set attack.landed = TargetDestructableFilter(attack.source, attack.targetDestructable)
            endif
            set prevInstance = eventInstance
            set prevAttack = eventAttack
            set eventInstance = GetUnitId(attack.source)
            set eventAttack = attack
            if doFireEvent[attack] then
                call TriggerEvaluate(EventHandler[EVENT_ATTACK_FINISH])
            endif
            if attack.flag then
                if attack.landed and attack.target != null then
                    call DealDamage(attack, attack.target, false)
                endif
                call attack.destroy()
            endif
            set eventAttack = prevAttack
            set eventInstance = prevInstance
            set doFireEvent[attack] = false
        endmethod

        static method create takes unit source, widget target, boolean mainAttack, boolean fireEvent returns thistype
            local CustomAttack instance = GetUnitId(source)
            local real deltaAngle = bj_PI/2.00 - GetUnitFacing(source)*bj_DEGTORAD
            local integer count = 0
            local boolean success = true
            local Node attack
            set Table(KEY).widget[0] = target
            if instance.melee then
                set attack = Node.create(false, 0.00, 0.00, 0.00, 0.00, 0.00, null)
                set attack.main = true
                set attack.ownerId = GetPlayerId(GetOwningPlayer(source))
                set attack.source = source
                set attack.targetUnit = Table(KEY).unit[0]
                set attack.targetItem = Table(KEY).item[0]
                set attack.targetDestructable = Table(KEY).destructable[0]
                set Attack(attack).damage = GetRandomReal(instance.minDamage, instance.maxDamage)
                set Attack(attack).attacktype = instance.attacktype
                set Attack(attack).damagetype = instance.damagetype
                set Attack(attack).weapontype = instance.weapontype
                set doFireEvent[attack] = fireEvent
                call TimerStart(GetTimer(attack), 0.00, false, function thistype.onMeleeAttackLand)
            else
                set tempUnit = Table(KEY).unit[0]
                set tempItem = Table(KEY).item[0]
                set tempDest = Table(KEY).destructable[0]
                if not mainAttack or TargetUnitFilter(source, tempUnit) then
                    set attack = Node.create(true, GetUnitX(source) + instance.launchX*Cos(deltaAngle) + instance.launchY*Sin(deltaAngle), GetUnitY(source) + instance.launchY*Cos(deltaAngle) - instance.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(source) + instance.launchZ, instance.scale, instance.arc, tempUnit)
                    set attack.targetUnit = tempUnit
                elseif TargetItemFilter(source, tempItem) then
                    set attack = Node.create(true, GetUnitX(source) + instance.launchX*Cos(deltaAngle) + instance.launchY*Sin(deltaAngle), GetUnitY(source) + instance.launchY*Cos(deltaAngle) - instance.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(source) + instance.launchZ, instance.scale, instance.arc, tempItem)
                    set attack.targetItem = tempItem
                elseif TargetDestructableFilter(source, tempDest) then
                    set attack = Node.create(true, GetUnitX(source) + instance.launchX*Cos(deltaAngle) + instance.launchY*Sin(deltaAngle), GetUnitY(source) + instance.launchY*Cos(deltaAngle) - instance.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(source) + instance.launchZ, instance.scale, instance.arc, tempDest)
                    set attack.targetDestructable = tempDest
                else
                    set success = false
                endif
                if success then
                    set attack.main = mainAttack
                    set Attack(attack).showShadow = instance.showShadow
                    set attack.ownerId = GetPlayerId(GetOwningPlayer(source))
                    set attack.source = source
                    set attack.currentBounces = 0
                    set Attack(attack).damage = GetRandomReal(instance.minDamage, instance.maxDamage)
                    set Attack(attack).bounceRange = instance.bounceRange
                    set Attack(attack).maxBounces = instance.maxBounces
                    set Attack(attack).attacktype = instance.attacktype
                    set Attack(attack).damagetype = instance.damagetype
                    set Attack(attack).weapontype = instance.weapontype
                    set attack.speed = instance.speed
                    set attack.minSpeed = instance.minSpeed
                    set attack.maxSpeed = instance.maxSpeed
                    set attack.acceleration = instance.acceleration
                    call attack.addSfx(instance.model)
                    set count = sfxCount[instance]
                    loop
                        exitwhen count == 0
                        call attack.addSfx(sfxTable[instance].string[count])
                        set count = count - 1
                    endloop
                    if fireEvent then
                        if mainAttack then
                            call FireHandler(EVENT_ATTACK_FINISH, instance, attack)
                        endif
                        call FireHandler(EVENT_ATTACK_MISSILE_LAUNCH, instance, attack)
                    endif
                endif
            endif
            return attack
        endmethod
        method destroy takes nothing returns nothing
            debug call DebugError(Node(this).flag, "AttackSystem", "destroy()", "thistype", 0, "Double-free")
            call Node(this).destroy()
        endmethod

    endstruct

    /*
    *   Main system struct
    */

    struct CustomAttack extends array

        implement SystemConstants

        private static boolean orderHandlerEnabled = true
        private static boolean updateAttackOrderType = true
        private static boolean internalSmartOrder = false
        private static unit filterUnit = null
        private static group ENUM_GROUP = CreateGroup()
        private static timer staticTimer = CreateTimer()
        private static real maxCollision = 0.00
        private thistype prev
        private thistype next
        private timer timer
        private timer chaseTimer
        private timer cooldownTimer
        private timer reattackTimer
        private integer attackOrderType
        private real startPosX
        private real startPosY
        private boolean registered
        readonly real targetX
        readonly real targetY
        readonly unit targetUnit
        readonly item targetItem
        readonly destructable targetDestructable
        boolean melee
        boolean showShadow
        integer maxTargets
        integer maxBounces
        real bounceRange
        real acquisitionRange
        real range
        real cooldown
        real damagePoint
        real launchX
        real launchY
        real launchZ
        real impactZ
        real arc
        real speed
        real minSpeed
        real maxSpeed
        real acceleration
        real scale
        real minDamage
        real maxDamage
        attacktype attacktype
        damagetype damagetype
        weapontype weapontype
        string model

        private method targetWidgetFilter takes nothing returns boolean
            if this.targetUnit != null then
                return TargetUnitFilter(GetUnitById(this), this.targetUnit)
            elseif this.targetItem != null then
                return TargetItemFilter(GetUnitById(this), this.targetItem)
            elseif this.targetDestructable != null then
                return TargetDestructableFilter(GetUnitById(this), this.targetDestructable)
            endif
            return false
        endmethod

        static method operator triggerInstance takes nothing returns thistype
            return eventInstance
        endmethod
        static method operator triggerAttack takes nothing returns Attack
            return eventAttack
        endmethod

        static method operator [] takes unit u returns thistype
            return GetUnitId(u)
        endmethod

        static method isUnitRegistered takes unit u returns boolean
            return thistype[u].registered
        endmethod

        method operator unit takes nothing returns unit
            return GetUnitById(this)
        endmethod

        method operator target takes nothing returns widget
            if this.targetUnit != null then
                return this.targetUnit
            elseif this.targetItem != null then
                return this.targetItem
            elseif this.targetDestructable != null then
                return this.targetDestructable
            endif
            return null
        endmethod

        private static method stopUnit takes nothing returns nothing
            local thistype this = DeleteTimer(GetExpiredTimer())
            local unit attacker = GetUnitById(this)
            local unit target = this.targetUnit
            set this.targetUnit = null
            set orderHandlerEnabled = false
            call IssueImmediateOrderById(attacker, ORDER_stop)
            set orderHandlerEnabled = true
            call SetUnitFacing(attacker, Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
            set this.targetUnit = target
            set attacker = null
            set target = null
        endmethod

        private static method faceTarget takes nothing returns nothing
            local thistype this = DeleteTimer(GetExpiredTimer())
            local unit attacker = GetUnitById(this)
            local widget target = this.target
            set orderHandlerEnabled = false
            call IssueImmediateOrderById(GetUnitById(this), ORDER_holdposition)
            set orderHandlerEnabled = true
            call SetUnitFacing(attacker, Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
            set attacker = null
            set target = null
        endmethod

        private static method animateAttack takes nothing returns nothing
            local thistype this = DeleteTimer(GetExpiredTimer())
            local unit attacker = GetUnitById(this)
            local widget target = this.target
            set orderHandlerEnabled = false
            call IssueImmediateOrderById(attacker, ORDER_holdposition)
            set orderHandlerEnabled = true
            call SetUnitFacing(attacker, Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
            call SetUnitAnimation(attacker, "attack")
            call SetUnitTimeScale(attacker, GetAttackAnimationScale(this))
            set attacker = null
            set target = null
        endmethod

        private static method resetAttackAnimationScale takes nothing returns nothing
            call SetUnitTimeScale(GetUnitById(DeleteTimer(GetExpiredTimer())), 1.00)
        endmethod

        private method cancelCurrentAttack takes nothing returns nothing
            call SetUnitTimeScale(this.unit, 1.00)
            set this.targetUnit = null
            set this.targetItem = null
            set this.targetDestructable = null
            set this.targetX = 0.00
            set this.targetY = 0.00
            call TimerStart(this.timer, 0.00, false, null)
            call TimerStart(this.reattackTimer, 0.00, false, null)
            call TimerStart(this.chaseTimer, 0.00, false, null)
        endmethod

        private method attackOrderTypeUpdate takes integer newType returns nothing
            if updateAttackOrderType then
                set this.attackOrderType = newType
            endif
        endmethod

        private method getClosestTargetInRange takes real x, real y, real radius returns unit
            local unit closest
            local unit attacker = GetUnitById(this)
            call GroupEnumUnitsInRange(ENUM_GROUP, x, y, radius + maxCollision, null)
            set radius = radius + BlzGetUnitCollisionSize(attacker)*0.50
            loop
                set closest = GetClosestUnitInGroup(x, y, ENUM_GROUP)
                exitwhen closest == null
                call GroupRemoveUnit(ENUM_GROUP, closest)
                if closest != attacker and closest != filterUnit and IsUnitInRangeXY(closest, x, y, radius) and IsUnitEnemy(closest, GetOwningPlayer(attacker)) and TargetUnitFilter(attacker, closest) then
                    set this = GetUnitId(closest)
                    call GroupClear(ENUM_GROUP)
                    set closest = null
                    set attacker = null
                    return GetUnitById(this)
                endif
            endloop
            set attacker = null
            return null
        endmethod

        private method attackClosestInRange takes real radius returns nothing
            local unit attacker = GetUnitById(this)
            set this.targetUnit = this.getClosestTargetInRange(GetUnitX(attacker), GetUnitY(attacker), radius)
            if this.targetUnit != null then
                set internalSmartOrder = true
                set updateAttackOrderType = false
                call IssueTargetOrderById(attacker, ATTACK_ORDER_ID, this.targetUnit)
                set updateAttackOrderType = true
                set internalSmartOrder = false
            endif
            set attacker = null
        endmethod

        private static method onCooldown takes nothing returns nothing
            local timer expired = GetExpiredTimer()
            local thistype this = Table(KEY)[GetHandleId(expired)]
            call PauseTimer(expired)
            if this.attackOrderType == 1 then
                if this.targetWidgetFilter() then
                    set internalSmartOrder = true
                    call IssueTargetOrderById(GetUnitById(this), ATTACK_ORDER_ID, this.target)
                    set internalSmartOrder = false
                else
                    call IssueImmediateOrderById(GetUnitById(this), ORDER_stop)
                endif
            elseif this.attackOrderType == 2 then
                call this.attackClosestInRange(this.range)
            elseif this.attackOrderType == 3 then
                call this.attackClosestInRange(this.acquisitionRange)
            elseif this.attackOrderType == 4 and this.targetWidgetFilter() then
                set updateAttackOrderType = false
                set internalSmartOrder = true
                call IssueTargetOrderById(GetUnitById(this), ATTACK_ORDER_ID, this.targetUnit)
                set internalSmartOrder = false
                set updateAttackOrderType = true
            endif
            set expired = null
        endmethod

        private static method onAttackLand takes Node attack returns boolean
            local Attack prevAttack = triggerAttack
            local thistype prevInstance = triggerInstance
            local widget target
            if attack.targetUnit != null then
                set attack.landed = TargetUnitFilter(attack.source, attack.targetUnit)
                set target = attack.targetUnit
            elseif attack.targetItem != null then
                set attack.landed = TargetItemFilter(attack.source, attack.targetItem)
                set target = attack.targetItem
            elseif attack.targetDestructable != null then
                set attack.landed = TargetDestructableFilter(attack.source, attack.targetDestructable)
                set target = attack.targetDestructable
            else
                set attack.landed = false
                set target = null
            endif
            set eventInstance = thistype[attack.source]
            set eventAttack = attack
            call TriggerEvaluate(EventHandler[EVENT_ATTACK_MISSILE_IMPACT])
            if attack.landed and attack.target != null then
                call DealDamage(attack, target, true)
            endif
            set target = null
            if attack.targetUnit != null and attack.landed and attack.currentBounces < Attack(attack).maxBounces then
                set attack.currentBounces = attack.currentBounces + 1
                set filterUnit = attack.targetUnit
                set attack.targetUnit = thistype[attack.source].getClosestTargetInRange(attack.x, attack.y, Attack(attack).bounceRange)
                set filterUnit = null
                if attack.targetUnit != null then
                    set attack.travelled = 0.00
                    set attack.oX = attack.x
                    set attack.oY = attack.y
                    set attack.oZ = attack.z
                    set attack.targetX = GetUnitX(attack.targetUnit)
                    set attack.targetY = GetUnitY(attack.targetUnit)
                    set attack.targetZ = BlzGetLocalUnitZ(attack.targetUnit)
                    call TriggerEvaluate(EventHandler[EVENT_ATTACK_MISSILE_BOUNCE])
                    set eventAttack = prevAttack
                    set eventInstance = prevInstance
                    return false
                endif
            endif
            set eventAttack = prevAttack
            set eventInstance = prevInstance
            return true
        endmethod

        /*
        *   This periodic method handles all missile movement
        */

        private static method onPeriod takes nothing returns nothing
            local Node node = Node(0).next
            local widget target
            local real prevX
            local real prevY
            local real prevZ
            local real dx
            local real dy
            local real dz
            local real xy
            local real angle
            local real zAngle
            local real offset
            local real speed
            local real cos
            local real sin
            loop
                exitwhen node == 0
                set target = node.target
                set prevX = node.x
                set prevY = node.y
                set prevZ = node.z
                if target != null and not IsWidgetRemoved(target) then
                    set node.targetX = GetWidgetX(target)
                    set node.targetY = GetWidgetY(target)
                endif
                set dx = node.targetX - prevX
                set dy = node.targetY - prevY
                set angle = Atan2(dy, dx)
                if target != null then
                    set offset = GetWidgetCollision(target)*0.50
                    set node.targetX = node.targetX - offset*Cos(angle)
                    set node.targetY = node.targetY - offset*Sin(angle)
                    if node.targetUnit == null then
                        set node.targetZ = BlzGetLocalUnitZ(node.targetUnit) + thistype[node.source].impactZ
                    else
                        set node.targetZ = GetSurfaceZ(node.targetX, node.targetY) + thistype[node.source].impactZ
                    endif
                else
                    set node.targetZ = GetSurfaceZ(node.targetX, node.targetY)
                endif
                set dz = node.targetZ - prevZ
                set node.speed = GetBoundedValue(node.speed + node.acceleration*PERIOD, node.minSpeed, node.maxSpeed)
                set speed = node.speed*PERIOD
                if dx*dx + dy*dy < speed*speed then
                    call node.move(node.targetX, node.targetY, node.targetZ)
                    set node.travelled = node.travelled + speed
                    if onAttackLand(node) then
                        call node.destroy()
                    endif
                else
                    set cos = Cos(angle)
                    set sin = Sin(angle)
                    set node.x = prevX + speed*cos
                    set node.y = prevY + speed*sin
                    set node.z = node.getParabolaZ()
                    set node.travelled = node.travelled + speed
                    set zAngle = Atan2(node.z - prevZ, speed)
                    set xy = LOOKAT_OFFSET*Cos(zAngle)
                    set node.lookAtX = node.x + xy*cos
                    set node.lookAtY = node.y + xy*sin
                    set node.lookAtZ = node.z + LOOKAT_OFFSET*Sin(zAngle)
                    call node.move(node.x, node.y, node.z)
                endif
                set target = null
                set node = node.next
            endloop
        endmethod

        private method launchProjectile takes unit targetUnit, item targetItem, destructable targetDestructable, boolean isMainAttack returns boolean
            local unit attacker = GetUnitById(this)
            local real deltaAngle = bj_PI/2.00 - GetUnitFacing(attacker)*bj_DEGTORAD
            local boolean success = true
            local integer count
            local real x
            local real y
            local Node attack
            if not isMainAttack or TargetUnitFilter(attacker, targetUnit) then
                set attack = Node.create(true, GetUnitX(attacker) + this.launchX*Cos(deltaAngle) + this.launchY*Sin(deltaAngle), GetUnitY(attacker) + this.launchY*Cos(deltaAngle) - this.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(attacker) + this.launchZ, this.scale, this.arc, targetUnit)
                set attack.targetUnit = targetUnit
            elseif TargetItemFilter(attacker, targetItem) then
                set attack = Node.create(true, GetUnitX(attacker) + this.launchX*Cos(deltaAngle) + this.launchY*Sin(deltaAngle), GetUnitY(attacker) + this.launchY*Cos(deltaAngle) - this.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(attacker) + this.launchZ, this.scale, this.arc, targetItem)
                set attack.targetItem = targetItem
            elseif TargetDestructableFilter(attacker, targetDestructable) then
                set attack = Node.create(true, GetUnitX(attacker) + this.launchX*Cos(deltaAngle) + this.launchY*Sin(deltaAngle), GetUnitY(attacker) + this.launchY*Cos(deltaAngle) - this.launchX*Sin(deltaAngle), BlzGetLocalUnitZ(attacker) + this.launchZ, this.scale, this.arc, targetDestructable)
                set attack.targetDestructable = targetDestructable
            else
                set success = false
            endif
            if success then
                set attack.main = isMainAttack
                set Attack(attack).showShadow = this.showShadow
                set attack.source = attacker
                set attack.currentBounces = 0
                set Attack(attack).damage = GetRandomReal(this.minDamage, this.maxDamage)
                set Attack(attack).bounceRange = this.bounceRange
                set Attack(attack).maxBounces = this.maxBounces
                set Attack(attack).attacktype = this.attacktype
                set Attack(attack).damagetype = this.damagetype
                set Attack(attack).weapontype = this.weapontype
                set attack.speed = this.speed
                set attack.minSpeed = this.minSpeed
                set attack.maxSpeed = this.maxSpeed
                set attack.acceleration = this.acceleration
                call attack.addSfx(this.model)
                set count = sfxCount[this]
                loop
                    exitwhen count == 0
                    call attack.addSfx(sfxTable[this].string[count])
                    set count = count - 1
                endloop
                if isMainAttack then
                    call FireHandler(EVENT_ATTACK_FINISH, this, attack)
                endif
                call FireHandler(EVENT_ATTACK_MISSILE_LAUNCH, this, attack)
            endif
            set attacker = null
            return success
        endmethod

        private static method onAttackLaunch takes nothing returns nothing
            local timer expired = GetExpiredTimer()
            local thistype this = Table(KEY)[GetHandleId(expired)]
            local unit attacker = GetUnitById(this)
            local real cooldown = RMaxBJ(this.cooldown - this.damagePoint, 0.00)
            local unit picked
            local integer count
            local real damage
            local real x
            local real y
            local thistype prevInstance
            local Node prevAttack
            local Node attack
            if this.melee then
                set attack = Node.create(false, 0.00, 0.00, 0.00, 0.00, 0.00, null)
                set attack.main = true
                set attack.source = attacker
                set attack.targetUnit = this.targetUnit
                set attack.targetItem = this.targetItem
                set attack.targetDestructable = this.targetDestructable
                set Attack(attack).damage = GetRandomReal(this.minDamage, this.maxDamage)
                set Attack(attack).attacktype = this.attacktype
                set Attack(attack).damagetype = this.damagetype
                set Attack(attack).weapontype = this.weapontype
                if attack.targetUnit != null then
                    set attack.landed = TargetUnitFilter(attacker, attack.targetUnit)
                elseif attack.targetItem != null then
                    set attack.landed = TargetItemFilter(attacker, attack.targetItem)
                elseif attack.targetDestructable != null then
                    set attack.landed = TargetDestructableFilter(attacker, attack.targetDestructable)
                endif
                set prevInstance = eventInstance
                set prevAttack = eventAttack
                set eventInstance = this
                set eventAttack = attack
                call TriggerEvaluate(EventHandler[EVENT_ATTACK_FINISH])
                if attack.flag then
                    if attack.landed and attack.target != null then
                        call DealDamage(attack, attack.target, false)
                    endif
                    call attack.destroy()
                endif
                set eventAttack = prevAttack
                set eventInstance = prevInstance
            else
                set count = this.maxTargets
                if count > 0 then
                    if this.launchProjectile(this.targetUnit, this.targetItem, this.targetDestructable, true) then
                        set count = count - 1
                        if count > 0 and this.targetUnit != null then
                            set x = GetUnitX(attacker)
                            set y = GetUnitY(attacker)
                            call GroupEnumUnitsInRange(ENUM_GROUP, x, y, this.range + maxCollision, null)
                            loop
                                set picked = GetClosestUnitInGroup(x, y, ENUM_GROUP)
                                exitwhen picked == null or count == 0
                                call GroupRemoveUnit(ENUM_GROUP, picked)
                                if picked != attacker and picked != this.targetUnit and IsUnitInRange(attacker, picked, this.range) and IsUnitEnemy(picked, GetOwningPlayer(attacker)) and TargetUnitFilter(attacker, picked) then
                                    set count = count - 1
                                    call this.launchProjectile(picked, null, null, false)
                                endif
                            endloop
                            if picked != null then
                                call GroupClear(ENUM_GROUP)
                                set picked = null
                            endif
                        endif
                    else
                        call this.cancelCurrentAttack()
                    endif
                endif
            endif
            call TimerStart(GetTimer(this), RMinBJ(this.damagePoint, cooldown), false, function thistype.resetAttackAnimationScale)
            call TimerStart(this.reattackTimer, cooldown, false, function thistype.onCooldown)
            call TimerStart(this.cooldownTimer, cooldown, false, null)
            call PauseTimer(expired)
            set expired = null
            set attacker = null
        endmethod

        private static method onTurnPeriodic takes nothing returns nothing
            local timer expired = GetExpiredTimer()
            local thistype this = Table(KEY)[GetHandleId(expired)]
            local unit attacker = GetUnitById(this)
            local widget target = this.target
            local real facing = GetUnitFacing(attacker)
            local real deltaAngle = RAbsBJ(GetUnitFacing(attacker) - Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
            if deltaAngle > 180.00 then
                set deltaAngle = 360.00 - deltaAngle
            endif
            if deltaAngle <= ATTACK_ANGLE_TOLERANCE then
                call FireHandler(EVENT_ATTACK_START, this, 0)
                set orderHandlerEnabled = false
                call IssueImmediateOrderById(attacker, ORDER_holdposition)
                set orderHandlerEnabled = true
                call SetUnitAnimation(attacker, "attack")
                call SetUnitTimeScale(attacker, GetAttackAnimationScale(this))
                call TimerStart(expired, this.damagePoint, false, function thistype.onAttackLaunch)
            else
                call SetUnitFacing(attacker, Atan2(GetWidgetY(target) - GetUnitY(attacker), GetWidgetX(target) - GetUnitX(attacker))*bj_RADTODEG)
            endif
            set attacker = null
            set target = null
            set expired = null
        endmethod

        private static method onChasePeriodic takes nothing returns nothing
            local thistype this = Table(KEY)[GetHandleId(GetExpiredTimer())]
            local unit attacker = GetUnitById(this)
            local unit closest
            local widget targetWidget
            local real dx
            local real dy
            local real range
            if GetUnitCurrentOrder(attacker) == ORDER_dropitem then
                call this.cancelCurrentAttack()
                call this.attackOrderTypeUpdate(0)
            endif
            if this.attackOrderType == 2 then
                if GetUnitCurrentOrder(attacker) == 0 then
                    set closest = this.getClosestTargetInRange(GetUnitX(attacker), GetUnitY(attacker), this.range)
                    if closest != null and closest != this.targetUnit then
                        call IssueImmediateOrderById(attacker, ORDER_holdposition)
                        set closest = null
                    endif
                endif
            elseif this.attackOrderType == 3 then
                if GetUnitCurrentOrder(attacker) == 0 then
                    set closest = this.getClosestTargetInRange(GetUnitX(attacker), GetUnitY(attacker), this.acquisitionRange)
                    if closest != null and closest != this.targetUnit then
                        call IssueImmediateOrderById(attacker, ORDER_stop)
                        set closest = null
                    endif
                endif
            endif
            if this.targetWidgetFilter() then
                set targetWidget = this.target
            else
                set targetWidget = null
            endif
            if targetWidget != null then
                set dx = GetWidgetX(targetWidget) - GetUnitX(attacker)
                set dy = GetWidgetY(targetWidget) - GetUnitY(attacker)
                set range = this.range + BlzGetUnitCollisionSize(attacker)/2.00 + GetWidgetCollision(targetWidget)/2.00
                if dx*dx + dy*dy <= range*range then
                    if TimerGetRemaining(this.cooldownTimer) == 0.00 then
                        set orderHandlerEnabled = false
                        call IssueImmediateOrderById(attacker, ORDER_holdposition)
                        set orderHandlerEnabled = true
                        call SetUnitFacing(attacker, Atan2(dy, dx)*bj_RADTODEG)
                        call TimerStart(this.timer, PERIOD, true, function thistype.onTurnPeriodic)
                        call PauseTimer(GetExpiredTimer())
                    elseif GetUnitCurrentOrder(attacker) == ATTACK_ORDER_ID then
                        set orderHandlerEnabled = false
                        call IssueImmediateOrderById(attacker, ORDER_stop)
                        set orderHandlerEnabled = true
                    endif
                elseif GetUnitCurrentOrder(attacker) == ORDER_stop then
                    set internalSmartOrder = true
                    set updateAttackOrderType = false
                    call IssueTargetOrderById(attacker, ATTACK_ORDER_ID, targetWidget)
                    set updateAttackOrderType = true
                    set internalSmartOrder = false
                endif
                set targetWidget = null
            endif
            set attacker = null
        endmethod

        private static method onPointTarget takes nothing returns nothing
            local timer expired = GetExpiredTimer()
            local thistype this = Table(KEY)[GetHandleId(expired)]
            local unit attacker = GetUnitById(this)
            local unit target
            if (GetUnitX(attacker) == this.targetX and GetUnitY(attacker) == this.targetY) or GetUnitCurrentOrder(attacker) == 0 then
                set this.targetUnit = null
                set this.targetItem = null
                set this.targetDestructable = null
                set this.attackOrderType = 3
                call PauseTimer(expired)
            else
                set target = this.getClosestTargetInRange(GetUnitX(attacker), GetUnitY(attacker), this.acquisitionRange)
                if target != null then
                    call PauseTimer(expired)
                    set internalSmartOrder = true
                    set updateAttackOrderType = false
                    call IssueTargetOrderById(attacker, ATTACK_ORDER_ID, target)
                    set updateAttackOrderType = true
                    set internalSmartOrder = false
                    set target = null
                endif
            endif
            set expired = null
            set attacker = null
        endmethod

        private method targetPoint takes real x, real y returns nothing
            set this.attackOrderType = 4
            set this.targetX = x
            set this.targetY = y
            set this.targetUnit = null
            set this.targetItem = null
            set this.targetDestructable = null
            set orderHandlerEnabled = false
            call IssuePointOrderById(GetUnitById(this), ORDER_smart, x, y)
            set orderHandlerEnabled = true
            call TimerStart(this.chaseTimer, PERIOD, true, function thistype.onPointTarget)
        endmethod

        private static method autoAcquireTargets takes nothing returns nothing
            local thistype this = thistype(0).next
            loop
                exitwhen this == 0
                if GetUnitCurrentOrder(GetUnitById(this)) == ORDER_dropitem and this.attackOrderType != 0 then
                    call this.cancelCurrentAttack()
                    call this.attackOrderTypeUpdate(0)
                endif
                if this.target == null then
                    if this.attackOrderType == 2 then
                        call this.attackClosestInRange(this.range)
                    elseif GetUnitCurrentOrder(GetUnitById(this)) == 0 and (this.attackOrderType == 3 or this.attackOrderType == 0) then
                        if this.attackOrderType == 0 then
                            set this.attackOrderType = 3
                        endif
                        set this.startPosX = GetUnitX(GetUnitById(this))
                        set this.startPosY = GetUnitY(GetUnitById(this))
                        call this.attackClosestInRange(this.acquisitionRange)
                    endif
                elseif this.targetUnit != null and not TargetUnitFilter(GetUnitById(this), this.targetUnit) then
                    if this.attackOrderType == 1 then
                        call IssueImmediateOrderById(GetUnitById(this), ORDER_stop)
                    elseif this.attackOrderType == 3 then
                        call this.targetPoint(this.startPosX, this.startPosY)
                    elseif this.attackOrderType == 4 then
                        call this.targetPoint(this.targetX, this.targetY)
                    endif
                endif
                set this = this.next
            endloop
        endmethod

        method addAttackSfx takes string model returns thistype
            local integer key = StringHash(model)
            if not sfxTable[this].has(key) then
                set sfxCount[this] = sfxCount[this] + 1
                set sfxTable[this].string[sfxCount[this]] = model
                set sfxTable[this][key] = sfxCount[this]
            debug else
                debug call DebugWarning(true, "AttackSystem", "addAttackSfx()", "thistype", this, "The specified model string was already added [" + model + "]")
            endif
            return this
        endmethod
        method removeAttackSfx takes string model returns thistype
            local integer key = StringHash(model)
            local integer index = sfxTable[this][key]
            if index > 0 then
                set sfxTable[this].string[index] = sfxTable[this].string[sfxCount[this]]
                call sfxTable[this].string.remove(sfxCount[this])
                call sfxTable[this].remove(key)
                set sfxCount[this] = sfxCount[this] - 1
            debug else
                debug call DebugWarning(true, "AttackSystem", "removeAttackSfx()", "thistype", this, "The specified model string was not yet added [" + model + "]")
            endif
            return this
        endmethod
        method clearAttackSfx takes nothing returns thistype
            set sfxCount[this] = 0
            call sfxTable[this].flush()
            return this
        endmethod

        method operator enabled takes nothing returns boolean
            return (this.prev.next) == (this)
        endmethod
        method operator enabled= takes boolean flag returns nothing
            if this.registered then
                if flag then
                    if not this.enabled then
                        if thistype(0).next == 0 then
                            call TimerStart(staticTimer, PERIOD, true, function thistype.autoAcquireTargets)
                        endif
                        set this.next = 0
                        set this.prev = thistype(0).prev
                        set thistype(0).prev.next = this
                        set thistype(0).prev = this
                        call BlzUnitDisableAbility(this.unit, ATTACK_ABIL_ID, false, false)
                    endif
                elseif this.enabled then
                    call BlzUnitDisableAbility(this.unit, ATTACK_ABIL_ID, true, false)
                    set this.next.prev = this.prev
                    set this.prev.next = this.next
                    if thistype(0).next == 0 then
                        call PauseTimer(staticTimer)
                    endif
                endif
            endif
        endmethod

        static method register takes unit u returns boolean
            local thistype this = GetUnitId(u)
            if not this.registered then
                call BlzUnitDisableAbility(u, 'Aatk', true, true)
                call UnitAddAbility(u, ATTACK_ABIL_ID)
                call BlzUnitDisableAbility(u, ATTACK_ABIL_ID, true, false)
                set this.registered = true
                set this.enabled = true
                set this.timer = GetTimer(this)
                set this.chaseTimer = GetTimer(this)
                set this.cooldownTimer = GetTimer(this)
                set this.reattackTimer = GetTimer(this)
                implement optional SystemDefaults
                return true
            endif
            return false
        endmethod
        static method unregister takes unit u returns boolean
            local thistype this = GetUnitId(u)
            if this.registered then
                set this.registered = false
                if this.enabled then
                    call UnitRemoveAbility(u, ATTACK_ABIL_ID)
                    set this.next.prev = this.prev
                    set this.prev.next = this.next
                    if thistype(0).next == 0 then
                        call PauseTimer(staticTimer)
                    endif
                endif
                call BlzUnitDisableAbility(u, 'Aatk', false, false)
                call this.clearAttackSfx()
                call DeleteTimer(this.timer)
                call DeleteTimer(this.chaseTimer)
                call DeleteTimer(this.cooldownTimer)
                call DeleteTimer(this.reattackTimer)
                set this.targetUnit = null
                set this.targetItem = null
                set this.targetDestructable = null
                set this.melee = false
                set this.maxTargets = 0
                set this.maxBounces = 0
                set this.acquisitionRange = 0.00
                set this.range = 0.00
                set this.bounceRange = 0.00
                set this.cooldown = 0.00
                set this.damagePoint = 0.00
                set this.launchX = 0.00
                set this.launchY = 0.00
                set this.launchZ = 0.00
                set this.impactZ = 0.00
                set this.arc = 0.00
                set this.speed = 0.00
                set this.minSpeed = 0.00
                set this.maxSpeed = 0.00
                set this.acceleration = 0.00
                set this.scale = 0.00
                set this.minDamage = 0.00
                set this.maxDamage = 0.00
                set this.attacktype = null
                set this.damagetype = null
                set this.weapontype = null
                set this.model = ""
                return true
            endif
            return false
        endmethod

        static method registerEventHandler takes EventHandler eventHandler, code handlerFunc returns nothing
            call eventHandler.register(handlerFunc)
        endmethod
        static method unregisterEventHandler takes EventHandler eventHandler, code handlerFunc returns nothing
            call eventHandler.unregister(handlerFunc)
        endmethod
        static method clearEventHandlers takes EventHandler eventHandler returns nothing
            call eventHandler.clear()
        endmethod

        private static method onAttackStart takes unit attacker, widget target returns boolean
            local thistype this = GetUnitId(attacker)
            local real dx = GetWidgetX(target) - GetUnitX(attacker)
            local real dy = GetWidgetY(target) - GetUnitY(attacker)
            local real range = this.range + BlzGetUnitCollisionSize(attacker)/2.00 + GetWidgetCollision(target)/2.00
            local real deltaAngle
            if dx*dx + dy*dy <= range*range then
                set deltaAngle = RAbsBJ(GetUnitFacing(attacker) - Atan2(dy, dx)*bj_RADTODEG)
                if deltaAngle > 180.00 then
                    set deltaAngle = 360.00 - deltaAngle
                endif
                if deltaAngle > ATTACK_ANGLE_TOLERANCE then
                    call TimerStart(GetTimer(this), 0.00, false, function thistype.faceTarget)
                    call TimerStart(this.timer, PERIOD, true, function thistype.onTurnPeriodic)
                else
                    call FireHandler(EVENT_ATTACK_START, this, 0)
                    call TimerStart(GetTimer(this), 0.00, false, function thistype.animateAttack)
                    call TimerStart(this.timer, this.damagePoint, false, function thistype.onAttackLaunch)
                endif
                return true
            endif
            return false
        endmethod

        private static method onOrder takes nothing returns nothing
            local unit u
            local unit targetUnit
            local widget target
            local widget tempTarget
            local integer id
            local thistype this
            local real dx
            local real dy
            local real range
            if orderHandlerEnabled then
                set u = GetOrderedUnit()
                if GetUnitAbilityLevel(u, ATTACK_ABIL_ID) > 0 then
                    set tempTarget = GetOrderTarget()
                    set id = GetIssuedOrderId()
                    set this = GetUnitId(u)
                    if id == ORDER_smart and tempTarget != null then
                        set targetUnit = GetOrderTargetUnit()
                        if targetUnit != null and IsUnitEnemy(targetUnit, GetOwningPlayer(u)) then
                            call IssueTargetOrderById(u, ATTACK_ORDER_ID, tempTarget)
                        else
                            call this.cancelCurrentAttack()
                            call this.attackOrderTypeUpdate(0)
                        endif
                        set targetUnit = null
                    elseif id == ORDER_holdposition then
                        call this.cancelCurrentAttack()
                        call this.attackOrderTypeUpdate(2)
                    elseif id == ORDER_stop or id == 0 then
                        call this.cancelCurrentAttack()
                        call this.attackOrderTypeUpdate(3)
                    elseif id == ATTACK_ORDER_ID then
                        if TimerGetRemaining(this.timer) > 0.00 and tempTarget != this.target then
                            call this.cancelCurrentAttack()
                        endif
                        if internalSmartOrder then
                            set this.targetUnit = GetOrderTargetUnit()
                            if this.targetWidgetFilter() then
                                set target = this.target
                            else
                                set target = null
                            endif
                        else// Manual attack order
                            if GetOrderTargetUnit() != null then
                                set this.targetUnit = GetOrderTargetUnit()
                                set this.targetItem = null
                                set this.targetDestructable = null
                                set target = this.targetUnit
                            elseif GetOrderTargetItem() != null then
                                set this.targetUnit = null
                                set this.targetItem = GetOrderTargetItem()
                                set this.targetDestructable = null
                                set target = this.targetItem
                            elseif GetOrderTargetDestructable() != null then
                                set this.targetUnit = null
                                set this.targetItem = null
                                set this.targetDestructable = GetOrderTargetDestructable()
                                set target = this.targetDestructable
                            else
                                set target = null
                            endif
                        endif
                        if target == null then
                            call this.targetPoint(GetOrderPointX(), GetOrderPointY())
                        else
                            if internalSmartOrder then
                                call this.attackOrderTypeUpdate(1)
                            else
                                set this.attackOrderType = 1
                                set this.targetX = 0.00
                                set this.targetY = 0.00
                            endif
                            if TimerGetRemaining(this.cooldownTimer) == 0.00 then
                                if not onAttackStart(u, target) then
                                    call TimerStart(this.chaseTimer, PERIOD, true, function thistype.onChasePeriodic)
                                endif
                            else
                                set dx = GetWidgetX(target) - GetUnitX(u)
                                set dy = GetWidgetY(target) - GetUnitY(u)
                                set range = this.range + BlzGetUnitCollisionSize(u)/2.00 + GetWidgetCollision(target)/2.00
                                if dx*dx + dy*dy <= range*range then
                                    call TimerStart(GetTimer(this), 0.00, false, function thistype.faceTarget)
                                endif
                                call TimerStart(this.chaseTimer, PERIOD, true, function thistype.onChasePeriodic)
                            endif
                            set target = null
                        endif
                    elseif (id < ORDER_moveslot1 or id > ORDER_useslot6) then
                        call this.cancelCurrentAttack()
                        call this.attackOrderTypeUpdate(0)
                    endif
                    set tempTarget = null
                endif
                set u = null
            endif
        endmethod

        private static method onIndex takes nothing returns nothing
            set maxCollision = RMaxBJ(BlzGetUnitCollisionSize(GetIndexedUnit()), maxCollision)
        endmethod
        private static method onDeindex takes nothing returns nothing
            call unregister(GetIndexedUnit())
        endmethod

        private static method registerEvent takes playerunitevent whichEvent, code handler returns nothing
            static if LIBRARY_RegisterPlayerUnitEvent then
                call RegisterAnyPlayerUnitEvent(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
            local code onIndex = function thistype.onIndex
            local code onDeindex = function thistype.onDeindex
            call OnUnitIndex(onIndex)
            call OnUnitDeindex(onDeindex)
            set onPeriodCode = function thistype.onPeriod
            call registerEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onOrder)
            call registerEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
            call registerEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
            set dummyUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'ncop', 0.00, 0.00, 0.00)
            call SetUnitVertexColor(dummyUnit, 0, 0, 0, 0)
            call UnitRemoveAbility(dummyUnit, 'Amov')
            call UnitRemoveAbility(dummyUnit, 'Aatk')
            call UnitAddAbility(dummyUnit, 'Aloc')
        endmethod
        implement Initializer

    endstruct

    private module Initializer
        private static method onInit takes nothing returns nothing
            set sfxTable = TableArray[JASS_MAX_ARRAY_SIZE - 1]
            static if not LIBRARY_WorldBounds then
                call MapBounds.init()
            endif
            call EventHandler.init()
            call Node.init()
            call CustomAttack.init()
        endmethod
    endmodule

    /*
    *   Wrapper Functions
    */

    function GetEventAttackDamage takes nothing returns real
        return CustomAttack.triggerAttack.damage
    endfunction
    function GetEventAttackSource takes nothing returns unit
        return CustomAttack.triggerAttack.source
    endfunction
    function GetEventAttackTarget takes nothing returns widget
        return CustomAttack.triggerAttack.target
    endfunction
    function GetEventAttackMaxBounces takes nothing returns integer
        return CustomAttack.triggerAttack.maxBounces
    endfunction
    function GetEventAttackCurrentBounces takes nothing returns integer
        return CustomAttack.triggerAttack.currentBounces
    endfunction
    function GetEventAttackTargetUnit takes nothing returns unit
        return CustomAttack.triggerAttack.targetUnit
    endfunction
    function GetEventAttackTargetItem takes nothing returns item
        return CustomAttack.triggerAttack.targetItem
    endfunction
    function GetEventAttackTargetDestructable takes nothing returns destructable
        return CustomAttack.triggerAttack.targetDestructable
    endfunction
    function GetEventAttackTargetX takes nothing returns real
        return CustomAttack.triggerAttack.targetX
    endfunction
    function GetEventAttackTargetY takes nothing returns real
        return CustomAttack.triggerAttack.targetY
    endfunction
    function GetEventAttackTargetZ takes nothing returns real
        return CustomAttack.triggerAttack.targetZ
    endfunction
    function GetEventAttackMissileSpeed takes nothing returns real
        return CustomAttack.triggerAttack.speed
    endfunction
    function GetEventAttackMissileMinSpeed takes nothing returns real
        return CustomAttack.triggerAttack.minSpeed
    endfunction
    function GetEventAttackMissileMaxSpeed takes nothing returns real
        return CustomAttack.triggerAttack.maxSpeed
    endfunction
    function GetEventAttackMissileAcceleration takes nothing returns real
        return CustomAttack.triggerAttack.acceleration
    endfunction
    function GetEventAttackMissileScale takes nothing returns real
        return CustomAttack.triggerAttack.scale
    endfunction
    function GetEventAttackMissileArc takes nothing returns real
        return CustomAttack.triggerAttack.arc
    endfunction
    function GetEventAttackType takes nothing returns attacktype
        return CustomAttack.triggerAttack.attacktype
    endfunction
    function GetEventDamageType takes nothing returns damagetype
        return CustomAttack.triggerAttack.damagetype
    endfunction
    function GetEventWeaponType takes nothing returns weapontype
        return CustomAttack.triggerAttack.weapontype
    endfunction
    function IsEventAttackMelee takes nothing returns boolean
        return CustomAttack.triggerAttack.melee
    endfunction
    function IsEventAttackRanged takes nothing returns boolean
        return CustomAttack.triggerAttack.ranged
    endfunction
    function IsEventAttackMain takes nothing returns boolean
        return CustomAttack.triggerAttack.main
    endfunction

    function DestroyEventAttack takes nothing returns nothing
        call CustomAttack.triggerAttack.destroy()
    endfunction
    function SetEventAttackDamage takes real damage returns nothing
        set CustomAttack.triggerAttack.damage = damage
    endfunction
    function SetEventAttackTarget takes widget target returns nothing
        set CustomAttack.triggerAttack.target = target
    endfunction
    function SetEventAttackTargetUnit takes unit target returns nothing
        set CustomAttack.triggerAttack.targetUnit = target
    endfunction
    function SetEventAttackTargetItem takes item target returns nothing
        set CustomAttack.triggerAttack.targetItem = target
    endfunction
    function SetEventAttackTargetDestructable takes destructable target returns nothing
        set CustomAttack.triggerAttack.targetDestructable = target
    endfunction
    function SetEventAttackTargetX takes real x returns nothing
        set CustomAttack.triggerAttack.targetX = x
    endfunction
    function SetEventAttackTargetY takes real y returns nothing
        set CustomAttack.triggerAttack.targetY = y
    endfunction
    function SetEventAttackTargetZ takes real z returns nothing
        set CustomAttack.triggerAttack.targetZ = z
    endfunction
    function SetEventAttackMissileSpeed takes real speed returns nothing
        set CustomAttack.triggerAttack.speed = speed
    endfunction
    function SetEventAttackMissileMinSpeed takes real speed returns nothing
        set CustomAttack.triggerAttack.minSpeed = speed
    endfunction
    function SetEventAttackMissileMaxSpeed takes real speed returns nothing
        set CustomAttack.triggerAttack.maxSpeed = speed
    endfunction
    function SetEventAttackMissileAcceleration takes real acceleration returns nothing
        set CustomAttack.triggerAttack.acceleration = acceleration
    endfunction
    function SetEventAttackMissileScale takes real scale returns nothing
        set CustomAttack.triggerAttack.scale = scale
    endfunction
    function SetEventAttackMissileArc takes real arc returns nothing
        set CustomAttack.triggerAttack.arc = arc
    endfunction
    function SetEventAttackType takes attacktype whichAttackType returns nothing
        set CustomAttack.triggerAttack.attacktype = whichAttackType
    endfunction
    function SetEventDamageType takes damagetype whichDamageType returns nothing
        set CustomAttack.triggerAttack.damagetype = whichDamageType
    endfunction
    function SetEventWeaponType takes weapontype whichWeaponType returns nothing
        set CustomAttack.triggerAttack.weapontype = whichWeaponType
    endfunction

    function GetUnitAttackDamage takes unit whichUnit returns real
        local CustomAttack unitAttack = CustomAttack[whichUnit]
        return (unitAttack.minDamage + unitAttack.maxDamage)*0.50
    endfunction
    function GetUnitAttackType takes unit whichUnit returns attacktype
        return CustomAttack[whichUnit].attacktype
    endfunction
    function GetUnitDamageType takes unit whichUnit returns damagetype
        return CustomAttack[whichUnit].damagetype
    endfunction
    function GetUnitWeaponType takes unit whichUnit returns weapontype
        return CustomAttack[whichUnit].weapontype
    endfunction
    function GetUnitAttackModel takes unit whichUnit returns string
        return CustomAttack[whichUnit].model
    endfunction
    function GetUnitAttackDamagePoint takes unit whichUnit returns real
        return CustomAttack[whichUnit].damagePoint
    endfunction
    function GetUnitAttackCooldown takes unit whichUnit returns real
        return CustomAttack[whichUnit].cooldown
    endfunction
    function GetUnitAttackRange takes unit whichUnit returns real
        return CustomAttack[whichUnit].range
    endfunction
    function GetUnitAttackAcquireRange takes unit whichUnit returns real
        return CustomAttack[whichUnit].acquisitionRange
    endfunction
    function GetUnitAttackMissileSpeed takes unit whichUnit returns real
        return CustomAttack[whichUnit].speed
    endfunction
    function GetUnitAttackMissileMinSpeed takes unit whichUnit returns real
        return CustomAttack[whichUnit].minSpeed
    endfunction
    function GetUnitAttackMissileMaxSpeed takes unit whichUnit returns real
        return CustomAttack[whichUnit].maxSpeed
    endfunction
    function GetUnitAttackLaunchX takes unit whichUnit returns real
        return CustomAttack[whichUnit].launchX
    endfunction
    function GetUnitAttackLaunchY takes unit whichUnit returns real
        return CustomAttack[whichUnit].launchY
    endfunction
    function GetUnitAttackLaunchZ takes unit whichUnit returns real
        return CustomAttack[whichUnit].launchZ
    endfunction
    function GetUnitAttackImpactZ takes unit whichUnit returns real
        return CustomAttack[whichUnit].impactZ
    endfunction
    function GetUnitAttackMaxTargets takes unit whichUnit returns integer
        return CustomAttack[whichUnit].maxTargets
    endfunction
    function GetUnitAttackMaxBounces takes unit whichUnit returns integer
        return CustomAttack[whichUnit].maxBounces
    endfunction
    function GetUnitAttackTarget takes unit whichUnit returns widget
        return CustomAttack[whichUnit].target
    endfunction
    function GetUnitAttackTargetUnit takes unit whichUnit returns unit
        return CustomAttack[whichUnit].targetUnit
    endfunction
    function GetUnitAttackTargetItem takes unit whichUnit returns item
        return CustomAttack[whichUnit].targetItem
    endfunction
    function GetUnitAttackTargetDestructable takes unit whichUnit returns destructable
        return CustomAttack[whichUnit].targetDestructable
    endfunction
    function IsUnitAttackShadowVisible takes unit whichUnit returns boolean
        return CustomAttack[whichUnit].showShadow
    endfunction
    function IsUnitAttackMelee takes unit whichUnit returns boolean
        return CustomAttack[whichUnit].melee
    endfunction
    function IsUnitAttackRanged takes unit whichUnit returns boolean
        return not CustomAttack[whichUnit].melee
    endfunction

    function SetUnitAttackDamageMin takes unit whichUnit, real amount returns nothing
        set CustomAttack[whichUnit].minDamage = amount
    endfunction
    function SetUnitAttackDamageMax takes unit whichUnit, real amount returns nothing
        set CustomAttack[whichUnit].maxDamage = amount
    endfunction
    function SetUnitAttackType takes unit whichUnit, attacktype whichAttackType returns nothing
        set CustomAttack[whichUnit].attacktype = whichAttackType
    endfunction
    function SetUnitDamageType takes unit whichUnit, damagetype whichDamageType returns nothing
        set CustomAttack[whichUnit].damagetype = whichDamageType
    endfunction
    function SetUnitWeaponType takes unit whichUnit, weapontype whichWeaponType returns nothing
        set CustomAttack[whichUnit].weapontype = whichWeaponType
    endfunction
    function SetUnitAttackModel takes unit whichUnit, string modelPath returns nothing
        set CustomAttack[whichUnit].model = modelPath
    endfunction
    function SetUnitAttackDamagePoint takes unit whichUnit, real damagePoint returns nothing
        set CustomAttack[whichUnit].damagePoint = damagePoint
    endfunction
    function SetUnitAttackCooldown takes unit whichUnit, real cooldown returns nothing
        set CustomAttack[whichUnit].cooldown = cooldown
    endfunction
    function SetUnitAttackRange takes unit whichUnit, real range returns nothing
        set CustomAttack[whichUnit].range = range
    endfunction
    function SetUnitAttackAcquireRange takes unit whichUnit, real acquireRange returns nothing
        set CustomAttack[whichUnit].acquisitionRange = acquireRange
    endfunction
    function SetUnitAttackMissileSpeed takes unit whichUnit, real missileSpeed returns nothing
        set CustomAttack[whichUnit].speed = missileSpeed
    endfunction
    function SetUnitAttackMissileMinSpeed takes unit whichUnit, real missileSpeed returns nothing
        set CustomAttack[whichUnit].minSpeed = missileSpeed
    endfunction
    function SetUnitAttackMissileMaxSpeed takes unit whichUnit, real missileSpeed returns nothing
        set CustomAttack[whichUnit].maxSpeed = missileSpeed
    endfunction
    function SetUnitAttackLaunchX takes unit whichUnit, real launchX returns nothing
        set CustomAttack[whichUnit].launchX = launchX
    endfunction
    function SetUnitAttackLaunchY takes unit whichUnit, real launchY returns nothing
        set CustomAttack[whichUnit].launchY = launchY
    endfunction
    function SetUnitAttackLaunchZ takes unit whichUnit, real launchZ returns nothing
        set CustomAttack[whichUnit].launchZ = launchZ
    endfunction
    function SetUnitAttackImpactZ takes unit whichUnit, real impactZ returns nothing
        set CustomAttack[whichUnit].impactZ = impactZ
    endfunction
    function SetUnitAttackMaxTargets takes unit whichUnit, integer maxTargets returns nothing
        set CustomAttack[whichUnit].maxTargets = maxTargets
    endfunction
    function SetUnitAttackMaxBounces takes unit whichUnit, integer maxBounces returns nothing
        set CustomAttack[whichUnit].maxBounces = maxBounces
    endfunction
    function SetUnitAttackShadowVisible takes unit whichUnit, boolean visible returns nothing
        set CustomAttack[whichUnit].showShadow = visible
    endfunction
    function SetUnitAttackMelee takes unit whichUnit returns nothing
        set CustomAttack[whichUnit].melee = true
    endfunction
    function SetUnitAttackRanged takes unit whichUnit returns nothing
        set CustomAttack[whichUnit].melee = false
    endfunction
    function UnitAddAttackEffect takes unit whichUnit, string model returns nothing
        call CustomAttack[whichUnit].addAttackSfx(model)
    endfunction
    function UnitRemoveAttackEffect takes unit whichUnit, string model returns nothing
        call CustomAttack[whichUnit].removeAttackSfx(model)
    endfunction
    function UnitClearAttackEffect takes unit whichUnit returns nothing
        call CustomAttack[whichUnit].clearAttackSfx()
    endfunction

    function RegisterAttackEventHandler takes integer whichEvent, code handler returns nothing
        call CustomAttack.registerEventHandler(whichEvent, handler)
    endfunction
    function UnregisterAttackEventHandler takes integer whichEvent, code handler returns nothing
        call CustomAttack.unregisterEventHandler(whichEvent, handler)
    endfunction

    function UnitCustomAttackEnable takes unit whichUnit returns nothing
        set CustomAttack[whichUnit].enabled = true
    endfunction
    function UnitCustomAttackDisable takes unit whichUnit returns nothing
        set CustomAttack[whichUnit].enabled = false
    endfunction
    function IsUnitCustomAttackEnabled takes unit whichUnit returns boolean
        return CustomAttack[whichUnit].enabled
    endfunction

    function UnitCustomAttackRegister takes unit whichUnit returns nothing
        call CustomAttack.register(whichUnit)
    endfunction
    function UnitCustomAttackUnregister takes unit whichUnit returns nothing
        call CustomAttack.unregister(whichUnit)
    endfunction
    function IsUnitCustomAttackRegistered takes unit whichUnit returns boolean
        return CustomAttack.isUnitRegistered(whichUnit)
    endfunction


endlibrary
library AttackSystemGUI initializer Init uses AttackSystem


    /*
    *   After registering a unit using the operation trigger, the variables will be reset
    *   to these values. You can remove this textmacro is the resets are not needed.
    */

    //! textmacro ATTACK_SYSTEM_GUI_VARS_RESET
        set udg_AttackSystem_Melee          =   true
        set udg_AttackSystem_ShowShadow     =   false
        set udg_AttackSystem_MaxTargets     =   1
        set udg_AttackSystem_MaxBounces     =   0
        set udg_AttackSystem_AcquireRange   =   1000.00
        set udg_AttackSystem_Range          =   600.00
        set udg_AttackSystem_BounceRange    =   500.00
        set udg_AttackSystem_Cooldown       =   1.00
        set udg_AttackSystem_DamagePoint    =   0.35
        set udg_AttackSystem_LaunchX        =   0.00
        set udg_AttackSystem_LaunchY        =   20.00
        set udg_AttackSystem_LaunchZ        =   60.00
        set udg_AttackSystem_ImpactZ        =   60.00
        set udg_AttackSystem_Arc            =   0.00
        set udg_AttackSystem_Speed          =   1000000.00
        set udg_AttackSystem_MinSpeed       =   0.00
        set udg_AttackSystem_MaxSpeed       =   1000000.00
        set udg_AttackSystem_Acceleration   =   0.00
        set udg_AttackSystem_Scale          =   1.00
        set udg_AttackSystem_MinDamage      =   0.00
        set udg_AttackSystem_MaxDamage      =   0.00
        set udg_AttackSystem_AttackType     =   ATTACK_TYPE_HERO
        set udg_AttackSystem_DamageType     =   DAMAGE_TYPE_NORMAL
        set udg_AttackSystem_CombatSound    =   WEAPON_TYPE_WHOKNOWS
        set udg_AttackSystem_Model          =   ""
    //! endtextmacro

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

    globals
        private constant location loc = Location(0.00, 0.00)
    endglobals

    private function FireEvent takes real whichEvent returns nothing
        local real prevAmount                           = udg_AttackSystem__DamageAmount
        local Attack prevAttack                         = udg_AttackSystem__TriggerAttack
        local unit prevSource                           = udg_AttackSystem__TriggerUnit
        local unit prevTargetUnit                       = udg_AttackSystem__TargetUnit
        local item prevTargetItem                       = udg_AttackSystem__TargetItem
        local destructable prevTargetDest               = udg_AttackSystem__TargetDest
        local location prevTargetPoint                  = udg_AttackSystem__TargetPoint
        local boolean prevMeleeFlag                     = udg_AttackSystem__IsAttackMelee
        local boolean prevMainFlag                      = udg_AttackSystem__IsAttackMain
        local boolean wantDestroy                       = udg_AttackSystem_WantDestroyAttack

        call MoveLocation(loc, GetEventAttackTargetX(), GetEventAttackTargetY())

        set udg_AttackSystem__DamageAmount              = GetEventAttackDamage()
        set udg_AttackSystem__TriggerAttack             = CustomAttack.triggerAttack
        set udg_AttackSystem__TriggerUnit               = GetEventAttackSource()
        set udg_AttackSystem__TargetUnit                = GetEventAttackTargetUnit()
        set udg_AttackSystem__TargetItem                = GetEventAttackTargetItem()
        set udg_AttackSystem__TargetDest                = GetEventAttackTargetDestructable()
        set udg_AttackSystem__TargetPoint               = loc
        set udg_AttackSystem__IsAttackMelee             = IsEventAttackMelee()
        set udg_AttackSystem__IsAttackMain              = IsEventAttackMain()

        set udg_AttackSystem__Event                     = 0.00
        set udg_AttackSystem__Event                     = whichEvent
        set udg_AttackSystem__Event                     = 0.00

        set udg_AttackSystem_WantDestroyAttack          = false

        if wantDestroy then
            call DestroyEventAttack()
        else
            call SetEventAttackDamage(udg_AttackSystem__DamageAmount)
            call SetEventAttackTargetUnit(udg_AttackSystem__TargetUnit)
            call SetEventAttackTargetItem(udg_AttackSystem__TargetItem)
            call SetEventAttackTargetDestructable(udg_AttackSystem__TargetDest)

            if udg_AttackSystem__TargetPoint != loc then
                call SetEventAttackTargetX(GetLocationX(udg_AttackSystem__TargetPoint))
                call SetEventAttackTargetY(GetLocationY(udg_AttackSystem__TargetPoint))
                call RemoveLocation(udg_AttackSystem__TargetPoint)
            endif
        endif

        set udg_AttackSystem__DamageAmount              = prevAmount
        set udg_AttackSystem__TriggerAttack             = prevAttack
        set udg_AttackSystem__TriggerUnit               = prevSource
        set udg_AttackSystem__TargetUnit                = prevTargetUnit
        set udg_AttackSystem__TargetItem                = prevTargetItem
        set udg_AttackSystem__TargetDest                = prevTargetDest
        set udg_AttackSystem__TargetPoint               = prevTargetPoint
        set udg_AttackSystem__IsAttackMelee             = prevMeleeFlag
        set udg_AttackSystem__IsAttackMain              = prevMainFlag
        set udg_AttackSystem_WantDestroyAttack          = wantDestroy

        set prevSource                                  = null
        set prevTargetUnit                              = null
        set prevTargetItem                              = null
        set prevTargetDest                              = null
        set prevTargetPoint                             = null
    endfunction

    private function OnAttackStart takes nothing returns nothing
        call FireEvent(1.00)
    endfunction
    private function OnAttackFinish takes nothing returns nothing
        call FireEvent(2.00)
    endfunction
    private function OnMissileLaunch takes nothing returns nothing
        call FireEvent(3.00)
    endfunction
    private function OnMissileImpact takes nothing returns nothing
        call FireEvent(4.00)
    endfunction
    private function OnMissileBounce takes nothing returns nothing
        call FireEvent(5.00)
    endfunction
    private function OnAttackDestroy takes nothing returns nothing
        call FireEvent(6.00)
    endfunction

    private function OnOperate takes nothing returns nothing
        local CustomAttack this
        if udg_AttackSystem_Operation == udg_AttackSystem_REGISTER_UNIT then
            if udg_AttackSystem_Unit != null and not IsUnitCustomAttackRegistered(udg_AttackSystem_Unit) then
                call CustomAttack.register(udg_AttackSystem_Unit)
                set this = CustomAttack[udg_AttackSystem_Unit]
                set this.melee                  =   udg_AttackSystem_Melee
                set this.showShadow             =   udg_AttackSystem_ShowShadow
                set this.maxTargets             =   udg_AttackSystem_MaxTargets
                set this.maxBounces             =   udg_AttackSystem_MaxBounces
                set this.acquisitionRange       =   udg_AttackSystem_AcquireRange
                set this.range                  =   udg_AttackSystem_Range
                set this.cooldown               =   udg_AttackSystem_Cooldown
                set this.damagePoint            =   udg_AttackSystem_DamagePoint
                set this.launchX                =   udg_AttackSystem_LaunchX
                set this.launchY                =   udg_AttackSystem_LaunchY
                set this.launchZ                =   udg_AttackSystem_LaunchZ
                set this.impactZ                =   udg_AttackSystem_ImpactZ
                set this.arc                    =   udg_AttackSystem_Arc*bj_DEGTORAD
                set this.speed                  =   udg_AttackSystem_Speed
                set this.minSpeed               =   udg_AttackSystem_MinSpeed
                set this.maxSpeed               =   udg_AttackSystem_MaxSpeed
                set this.acceleration           =   udg_AttackSystem_Acceleration
                set this.scale                  =   udg_AttackSystem_Scale
                set this.minDamage              =   udg_AttackSystem_MinDamage
                set this.maxDamage              =   udg_AttackSystem_MaxDamage
                set this.attacktype             =   udg_AttackSystem_AttackType
                set this.damagetype             =   udg_AttackSystem_DamageType
                set this.weapontype             =   udg_AttackSystem_CombatSound
                set this.model                  =   udg_AttackSystem_Model
                //! runtextmacro optional ATTACK_SYSTEM_GUI_VARS_RESET()
            endif
        elseif udg_AttackSystem_Operation == udg_AttackSystem_UNREGISTER_UNIT then
            if udg_AttackSystem_Unit != null and IsUnitCustomAttackRegistered(udg_AttackSystem_Unit) then
                call UnitCustomAttackUnregister(udg_AttackSystem_Unit)
            endif
        elseif udg_AttackSystem_Operation == udg_AttackSystem_ENABLE_UNIT then
            if udg_AttackSystem_Unit != null then
                call UnitCustomAttackEnable(udg_AttackSystem_Unit)
            endif
        elseif udg_AttackSystem_Operation == udg_AttackSystem_DISABLE_UNIT then
            if udg_AttackSystem_Unit != null then
                call UnitCustomAttackDisable(udg_AttackSystem_Unit)
            endif
        elseif udg_AttackSystem_Operation == udg_AttackSystem_DESTROY_ATTACK then
            if udg_AttackSystem_Attack > 0 and Attack(udg_AttackSystem_Attack).flag then
                call Attack(udg_AttackSystem_Attack).destroy()
            endif
        endif
    endfunction

    private function Init takes nothing returns nothing
        set udg_AttackSystem_OperationTrigger = CreateTrigger()
        call TriggerAddAction(udg_AttackSystem_OperationTrigger, function OnOperate)
        call RegisterAttackEventHandler(EVENT_ATTACK_START, function OnAttackStart)
        call RegisterAttackEventHandler(EVENT_ATTACK_FINISH, function OnAttackFinish)
        call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_LAUNCH, function OnMissileLaunch)
        call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_IMPACT, function OnMissileImpact)
        call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_BOUNCE, function OnMissileBounce)
        call RegisterAttackEventHandler(EVENT_ATTACK_DESTROY, function OnAttackDestroy)
    endfunction


endlibrary
AttackSystemGUI Documentation
  Events
    Map initialization
  Conditions
  Actions
    Custom script: /*
    -------- ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| --------
    -------- ||||||||||||||||||||||||||||||||||||||||||||||||| GUI ATTACK SYSTEM GUIDE |||||||||||||||||||||||||||||||||||||||||||||||| --------
    -------- ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| --------
    -------- First you need to choose what operation you want to do using the system. Like this: --------
    Set AttackSystem_Operation = AttackSystem_REGISTER_UNIT
    -------- It can be any from the list below: --------
    -------- --------
    -------- 1. REGISTER_UNIT -- registers a chosen unit to the system --------
    -------- 2. UNREGISTER_UNIT -- unregisters a chosen unit from the system --------
    -------- 3. ENABLE_UNIT -- enables the chosen unit's attacks --------
    -------- 4. DISABLE_UNIT -- disables the chosen unit's attacks --------
    -------- 5. DESTROY_ATTACK -- force destroys a chosen attack instance --------
    -------- --------
    -------- Next, you have to setup all the data necessary for the operation you chose. Below are the --------
    -------- list of data needed for each operation (Note that all these have an AttackSystem_ prefix): --------
    -------- --------
    -------- 1. - Unit -- the unit to be registered --------
    -------- - Melee -- determines if the unit is melee or not --------
    -------- - ShowShadow -- determines if the shadow of a ranged attack's missile is visible --------
    -------- - MaxTargets -- the max number of targets a unit can have when attacking --------
    -------- - MaxBounces -- max number of attack bounces --------
    -------- - AcquireRange -- attack acquisition range --------
    -------- - Range -- attack range --------
    -------- - BounceRange -- attack missile bounce range --------
    -------- - Cooldown -- attack cooldown --------
    -------- - DamagePoint -- attack damage point --------
    -------- - LaunchX -- the relative x of the attack launch location based on the unit's local axis --------
    -------- - LaunchY -- the relative y of the attack launch location based on the unit's local axis --------
    -------- - LaunchZ -- the relative z of the attack launch location based on the unit's local axis --------
    -------- - ImpactZ -- the relative z of the attack impact location based on the target's local axis --------
    -------- - Arc -- the attack launch angle from the ground (in degrees) --------
    -------- - Speed -- the attack missile speed --------
    -------- - MinSpeed -- minimum attack missile speed --------
    -------- - MaxSpeed -- maximum attack missile speed --------
    -------- - Acceleration -- attack missile acceleration --------
    -------- - Scale -- attack missile scale --------
    -------- - MinDamage -- minimum attack damage --------
    -------- - MaxDamage -- manimum attack damage --------
    -------- - AttackType -- attack attack type --------
    -------- - DamageType -- attack damage type --------
    -------- - CombatSound -- attack weapon sound --------
    -------- - Model -- attack missile model --------
    -------- --------
    -------- 2, 3, 4. - Unit -- the unit you want to perform an operation on --------
    -------- --------
    -------- 5. - Attack -- the attack instance to be destroyed --------
    -------- --------
    -------- The example we chose is the REGISTER_UNIT operation. As you can see in number 1, --------
    -------- we need to setup those variables so now we do: --------
    Set AttackSystem_Unit = (Last created unit)
    Set AttackSystem_Melee = False
    Set AttackSystem_ShowShadow = True
    Set AttackSystem_MaxTargets = 1
    Set AttackSystem_MaxBounces = 1
    Set AttackSystem_AcquireRange = 1000.00
    Set AttackSystem_Range = 550.00
    Set AttackSystem_BounceRange = 550.00
    Set AttackSystem_Cooldown = 1.35
    Set AttackSystem_DamagePoint = 0.85
    Set AttackSystem_LaunchX = 0.00
    Set AttackSystem_LaunchY = 25.00
    Set AttackSystem_LaunchZ = 60.00
    Set AttackSystem_ImpactZ = 60.00
    Set AttackSystem_Arc = 35.00
    Set AttackSystem_Speed = 450.00
    Set AttackSystem_MinSpeed = 0.00
    Set AttackSystem_MaxSpeed = 1000.00
    Set AttackSystem_Acceleration = 0.00
    Set AttackSystem_Scale = 1.00
    Set AttackSystem_MinDamage = 21.00
    Set AttackSystem_MaxDamage = 27.00
    Set AttackSystem_AttackType = Hero
    Set AttackSystem_DamageType = Normal
    Set AttackSystem_CombatSound = Rock Heavy Bash
    Set AttackSystem_Model = Abilities\Weapons\BloodElfMissile\BloodElfMissile.mdl
    -------- --------
    -------- And finally, what we have to do is execute the operation which in this case is very simple. --------
    -------- To execute the opertation we have to use this: --------
    Trigger - Run AttackSystem_OperationTrigger (ignoring conditions)
    -------- Easy right? ;) --------
    -------- --------
    -------- --------
    -------- EVENT HANDLERS: --------
    -------- --------
    -------- Use "Game - AttackSystem__Event becomes Equal to 'RealValue' " to catch an attack --------
    -------- event. --------
    -------- --------
    -------- 1.00 -- Attack start event --------
    -------- -- This event fires when a unit starts its attack animation --------
    -------- 2.00 -- Attack finish event --------
    -------- -- This event fires when a unit finishes its attack animation --------
    -------- 3.00 -- Attack missile launch event --------
    -------- -- This event fires for each successfully launched attack missile --------
    -------- 4.00 -- Attack missile impact event --------
    -------- -- This event fires when an attack missile lands to its target --------
    -------- 5.00 -- Attack missile bounce event --------
    -------- -- This event fires an attack missile bounces off from its target --------
    -------- 6.00 -- Attack is destroyed event --------
    -------- -- This event fires when an attack instance is destroyed (explicitly or implicitly) --------
    -------- --------
    -------- --------
    -------- EVENT RESPONSES: --------
    -------- --------
    -------- Readonly: --------
    -------- --------
    -------- AttackSystem__IsAttackMelee --------
    -------- -- Checks if the triggering attack is melee --------
    -------- AttackSystem__IsAttackMain --------
    -------- -- Checks if the triggering attack is an attack to the main target (in case the unit --------
    -------- source can have multiple targets when attacking) --------
    -------- AttackSystem__TriggerAttack --------
    -------- -- The triggering attack instance --------
    -------- AttackSystem__TriggerUnit --------
    -------- -- The triggering attack's source unit --------
    -------- --------
    -------- Editable: --------
    -------- --------
    -------- AttackSystem__DamageAmount --------
    -------- AttackSystem__TargetUnit --------
    -------- AttackSystem__TargetItem --------
    -------- AttackSystem__TargetDest --------
    -------- AttackSystem__TargetPoint --------
    -------- --------
    -------- --------
    -------- You can set the boolean variable "AttackSystem_WantDestroyAttack" to 'true' inside an --------
    -------- attack event handler trigger to destroy the current Attack after the current trigger execution. --------
    -------- --------
    -------- ======================================================================= --------
    -------- --------
    -------- --------
    -------- ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| --------
    -------- |||||||||||||||||||||||||||||||||||||||||||||||||||||||||| CONSTANTS SETTING ||||||||||||||||||||||||||||||||||||||||||||||||||||||||| --------
    -------- |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ --------
    Set AttackSystem_REGISTER_UNIT = 1
    Set AttackSystem_UNREGISTER_UNIT = 2
    Set AttackSystem_ENABLE_UNIT = 3
    Set AttackSystem_DISABLE_UNIT = 4
    Set AttackSystem_DESTROY_ATTACK = 5
    Custom script: //! runtextmacro optional ATTACK_SYSTEM_GUI_VARS_RESET()
    Custom script: /*
    -------- --------
    -------- --------
    -------- ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| --------
    -------- |||||||||||||||||||||||||||||||||||||||||||||||||||||||||| VARIABLE CREATOR ||||||||||||||||||||||||||||||||||||||||||||||||||||||||| --------
    -------- ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| --------
    Set AttackSystem_Acceleration = 0.00
    Set AttackSystem_AcquireRange = 0.00
    Set AttackSystem_Arc = 0.00
    Set AttackSystem_Attack = 0
    Set AttackSystem_AttackType = Spells
    Set AttackSystem_BounceRange = 0.00
    Set AttackSystem_CombatSound = Metal Light Chop
    Set AttackSystem_Cooldown = 0.00
    Set AttackSystem_DamagePoint = 0.00
    Set AttackSystem_DamageType = Unknown
    Set AttackSystem_ImpactZ = 0.00
    Set AttackSystem_LaunchX = 0.00
    Set AttackSystem_LaunchY = 0.00
    Set AttackSystem_LaunchZ = 0.00
    Set AttackSystem_MaxBounces = 0
    Set AttackSystem_MaxDamage = 0.00
    Set AttackSystem_MaxSpeed = 0.00
    Set AttackSystem_MaxTargets = 0
    Set AttackSystem_Melee = False
    Set AttackSystem_MinDamage = 0.00
    Set AttackSystem_MinSpeed = 0.00
    Set AttackSystem_Model =
    Set AttackSystem_Operation = 0
    Set AttackSystem_OperationTrigger = (This trigger)
    Set AttackSystem_Range = 0.00
    Set AttackSystem_Scale = 0.00
    Set AttackSystem_Speed = 0.00
    Set AttackSystem_ShowShadow = False
    Set AttackSystem_Unit = No unit
    Set AttackSystem_WantDestroyAttack = False
    Set AttackSystem__DamageAmount = 0.00
    Set AttackSystem__Event = 0.00
    Set AttackSystem__IsAttackMain = False
    Set AttackSystem__IsAttackMelee = False
    Set AttackSystem__TargetDest = No destructible
    Set AttackSystem__TargetItem = No item
    Set AttackSystem__TargetPoint = AttackSystem__TargetPoint
    Set AttackSystem__TargetUnit = No unit
    Set AttackSystem__TriggerAttack = 0
    Set AttackSystem__TriggerUnit = No unit
    Set AttackSystem__TriggerUnit = No unit
    -------- =======================================================================*/ --------
library EvasionSystem /*


    */
uses /*

    */
AttackSystem      /*    https://www.hiveworkshop.com/threads/307417/
    */
UnitDex           /*    https://www.hiveworkshop.com/threads/248209/
    */
Table             /*    https://www.hiveworkshop.com/threads/188084/
    */
ErrorMessage      /*    https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage/main.j/


    */
//! novjass

    |-------------|
    | Description |
    |-------------|
    /*
        This is a simple library used to simulate the default evasion behavior.
        This snippet supports evasion stacking. Evasions stack multiplicatively.

    */


    |-----|
    | API |
    |-----|
    /*
        Event Responses
      */
constant function   GetEventMissedAttack            takes nothing                               returns Attack/*
      */
constant function   GetEventMissedSource            takes nothing                               returns unit/*
      */
constant function   GetEventMissedTargetUnit        takes nothing                               returns unit/*

        A value of 1.00 is equal to 100%
      */
function            SetUnitEvasion                  takes unit whichUnit, real evasion          returns nothing/*
      */
function            SetUnitAccuracy                 takes unit whichUnit, real accuracy         returns nothing/*
      */
function            SetAttackAccuracy               takes Attack whichAttack, real accuracy returns nothing/*

      */
function            GetUnitEvasion                  takes unit whichUnit                        returns real/*
      */
function            GetUnitAccuracy                 takes unit whichUnit                        returns real/*
      */
function            GetAttackAccuracy               takes Attack whichAttack                    returns real/*
 
      */
function            RegisterAttackEvasionHandler    takes code handler                          returns nothing/*
      */
function            UnregisterAttackEvasionHandler  takes code handler                          returns nothing/*


    */
//! endnovjass

    globals
    /*=======================================================================*/
    /*                         SYSTEM CONFIGURATION                          */
    /*=======================================================================*/
        /*
        *   The maximum distance the projectile can deflect from the position
        *   of the target unit when an evasion occurs
        */

        private constant real MIN_DISTANCE_OFFSET_PERCENT   = 0.15
        /*
        *   The minimum distance the projectile can deflect from the position
        *   of the target unit when an evasion occurs
        */

        private constant real MAX_DISTANCE_OFFSET_PERCENT   = 0.25
        /*
        *   The maximum angle (in radians) of the projectile's deflection location
        *   from the angle of the line from the target to the attacker's location
        */

        private constant real MIN_ANGLE_OFFSET              = 0.00
        /*
        *   The minimum angle (in radians) of the projectile's deflection location
        *   from the angle of the line from the target to the attacker's location
        */

        private constant real MAX_ANGLE_OFFSET              = bj_PI
        /*
        *   Evasion texttag text
        */

        private constant string EVASION_TEXT_MESSAGE        = "Miss"
        /*
        *   Evasion texttag color
        */

        private constant integer EVASION_TEXT_ALPHA         = 0xCC
        private constant integer EVASION_TEXT_RED           = 0xFF
        private constant integer EVASION_TEXT_GREEN         = 0x00
        private constant integer EVASION_TEXT_BLUE          = 0x00
    /*=======================================================================*/
    /*                         END OF CONFIGURATION                          */
    /*=======================================================================*/
    endglobals

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

    globals
        private boolean array isDummy
        private real array evasion
        private real array accuracy
        private real array marksmanship
        private Table table
        private integer handlerCount = 0
        private trigger handlerTrigger = null
        private unit eventSource = null
        private unit eventTarget = null
        private Attack eventAttack = 0
    endglobals

    constant function GetEventMissedAttack takes nothing returns Attack
        return eventAttack
    endfunction
    constant function GetEventMissedSource takes nothing returns unit
        return eventSource
    endfunction
    constant function GetEventMissedTargetUnit takes nothing returns unit
        return eventTarget
    endfunction

    function SetUnitEvasion takes unit u, real value returns nothing
        set evasion[GetUnitId(u)] = value
    endfunction
    function AddUnitEvasion takes unit u, real value returns nothing
        local integer i = GetUnitId(u)
        set evasion[i] = evasion[i] + (1.00 - evasion[i])*value
    endfunction

    function SetUnitAccuracy takes unit u, real value returns nothing
        set marksmanship[GetUnitId(u)] = value
    endfunction
    function AddUnitAccuracy takes unit u, real value returns nothing
        local integer i = GetUnitId(u)
        set marksmanship[i] = marksmanship[i] + (1.00 - marksmanship[i])*value
    endfunction

    function SetAttackAccuracy takes Attack attack, real value returns nothing
        set accuracy[attack] = value
    endfunction
    function AddAttackAccuracy takes Attack attack, real value returns nothing
        set accuracy[attack] = accuracy[attack] + (1.00 - accuracy[attack])*value
    endfunction

    function GetUnitEvasion takes unit u returns real
        return evasion[GetUnitId(u)]
    endfunction
    function GetUnitAccuracy takes unit u returns real
        return marksmanship[GetUnitId(u)]
    endfunction
    function GetAttackAccuracy takes Attack attack returns real
        return accuracy[attack]
    endfunction

    function RegisterAttackEvasionHandler takes code handler returns nothing
        local boolexpr expr = Filter(handler)
        debug call ThrowError(table.handle.has(GetHandleId(expr)), "EvasionSystem", "RegisterAttackEvasionHandler()", "", 0, "Attempted to register an already registered handler")
        if handlerCount == 0 then
            set handlerTrigger = CreateTrigger()
        endif
        set handlerCount = handlerCount + 1
        set table.triggercondition[GetHandleId(expr)] = TriggerAddCondition(handlerTrigger, expr)
        set expr = null
    endfunction
    function UnregisterAttackEvasionHandler takes code handler returns nothing
        local integer exprId = GetHandleId(Filter(handler))
        debug call ThrowError(not table.handle.has(exprId), "EvasionSystem", "UnregisterAttackEvasionHandler()", "", 0, "Attempted to unregister an unregistered handler")
        call TriggerRemoveCondition(handlerTrigger, table.triggercondition[exprId])
        call table.handle.remove(exprId)
        set handlerCount = handlerCount - 1
        if handlerCount == 0 then
            call DestroyTrigger(handlerTrigger)
            set handlerTrigger = null
        endif
    endfunction

    private function FireHandlers takes Attack attack, unit source, unit missedTarget returns nothing
        local Attack prevAttack = eventAttack
        local unit prevSource = eventSource
        local unit prevTarget = eventTarget
        set eventAttack = attack
        set eventSource = source
        set eventTarget = missedTarget
        call TriggerEvaluate(handlerTrigger)
        set eventAttack = prevAttack
        set eventSource = prevSource
        set eventTarget = prevTarget
        set prevSource = null
        set prevTarget = null
    endfunction

    private function DisplayEvasionText takes real x, real y returns nothing
        local texttag text = CreateTextTag()
        call SetTextTagPermanent(text, false)
        call SetTextTagText(text, EVASION_TEXT_MESSAGE, 0.023)
        call SetTextTagColor(text, EVASION_TEXT_RED, EVASION_TEXT_GREEN, EVASION_TEXT_BLUE, EVASION_TEXT_ALPHA)
        call SetTextTagPos(text, x - 30.00, y, 70.00)
        call SetTextTagVelocity(text, 0.00, 0.03)
        call SetTextTagFadepoint(text, 1.20)
        call SetTextTagLifespan(text, 2.00)
        set text = null
    endfunction

    private function DivertAttackFromTarget takes Attack attack returns nothing
        local unit attacker = attack.source
        local unit target = attack.targetUnit
        local real sourceX = GetUnitX(attacker)
        local real sourceY = GetUnitY(attacker)
        local real targetX = GetUnitX(target)
        local real targetY = GetUnitY(target)
        local real dx = sourceX - targetX
        local real dy = sourceY - targetY
        local real angle
        local real offset
        call DisplayEvasionText(sourceX, sourceY)
        if GetRandomInt(0, 1) == 0 then
            set angle = Atan2(dy, dx) + GetRandomReal(MIN_ANGLE_OFFSET, MAX_ANGLE_OFFSET)
        else
            set angle = Atan2(dy, dx) - GetRandomReal(MIN_ANGLE_OFFSET, MAX_ANGLE_OFFSET)
        endif
        set angle = angle - I2R(R2I(angle/(2.00*bj_PI)))*(2.00*bj_PI)
        set offset = GetRandomReal(MIN_DISTANCE_OFFSET_PERCENT, MAX_DISTANCE_OFFSET_PERCENT)*SquareRoot(dx*dx + dy*dy)
        set attack.target = null
        set attack.targetX = targetX + offset*Cos(angle)
        set attack.targetY = targetY + offset*Sin(angle)
        set attacker = null
        set target = null
    endfunction

    private function OnExpire takes nothing returns nothing
        local timer expired = GetExpiredTimer()
        local integer id = GetHandleId(expired)
        local Attack attack = table[id]
        local real evasion = 0.00
        local unit missedTarget = attack.targetUnit
        if attack.targetUnit != null then
            set evasion = GetUnitEvasion(attack.targetUnit)
        endif
        if GetRandomReal(0.00, 1.00) <= evasion + (1.00 - evasion)*(1.00 - GetAttackAccuracy(attack)) then
            call DivertAttackFromTarget(attack)
            call FireHandlers(attack, attack.source, missedTarget)
        endif
        call table.remove(id)
        set missedTarget = null
        set expired = null
    endfunction

    private function OnAttackFinish takes nothing returns nothing
        local real evasion = 0.00
        local unit missedTarget
        if CustomAttack.triggerAttack.melee then
            set missedTarget = CustomAttack.triggerAttack.targetUnit
            if missedTarget != null then
                set evasion = GetUnitEvasion(missedTarget)
            endif
            if GetRandomReal(0.00, 1.00) <= evasion + (1.00 - evasion)*(1.00 - GetUnitAccuracy(CustomAttack.triggerAttack.source)) then
                set CustomAttack.triggerAttack.target = null
                call DisplayEvasionText(GetUnitX(CustomAttack.triggerAttack.source), GetUnitY(CustomAttack.triggerAttack.source))
                call FireHandlers(CustomAttack.triggerAttack, CustomAttack.triggerAttack.source, missedTarget)
            endif
            set missedTarget = null
        endif
    endfunction

    private function OnProjectileLaunch takes nothing returns nothing
        local timer t = CreateTimer()
        call SetAttackAccuracy(CustomAttack.triggerAttack, GetUnitAccuracy(CustomAttack.triggerAttack.source))
        set table[GetHandleId(t)] = CustomAttack.triggerAttack
        call TimerStart(t, 0.00, false, function OnExpire)
        set t = null
    endfunction

    private function OnIndex takes nothing returns nothing
        call SetUnitAccuracy(GetIndexedUnit(), 1.00)
    endfunction

    private module InitModule
        private static method onInit takes nothing returns nothing
            local code onIndex = function OnIndex
            set table = Table.create()
            call OnUnitIndex(onIndex)
            call RegisterAttackEventHandler(EVENT_ATTACK_FINISH, function OnAttackFinish)
            call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_LAUNCH, function OnProjectileLaunch)
        endmethod
    endmodule

    private struct Init extends array
        implement InitModule
    endstruct


endlibrary
library AttackModifier /*


    */
uses /*

    */
AttackSystem      /*    https://www.hiveworkshop.com/threads/307417/
    */
UnitDex           /*    https://www.hiveworkshop.com/threads/248209/
    */
Table             /*    https://www.hiveworkshop.com/threads/188084/
    */
NxList            /*    https://github.com/nestharus/JASS/blob/master/jass/Data%20Structures/NxList/script.j/
    */
ErrorMessage      /*    https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage/main.j/


    */
//! novjass

    |-------------|
    | Description |
    |-------------|
    /*
        The purpose of this library is to solve the problem that commonly arises from many coded attacker-modifier
        systems such as orb-effects, life-steal, damage return, cleave, bash, critical-strike, and so on.
        An attack modifier should IDEALLY take effect only when an attack that lands was launched by the time
        the attack modifier is present on the attacker - a condition not taken into account by any attack-
        modifying systems so far. For example, a ranged attack is launched by a unit who has a life-steal buff
        but before the attack lands on the target, the life-steal buff on the attacker expires. At which case,
        those system will not trigger the life-steal effect when that attack lands since they only check for the
        conditions at the time the damage is detected. Such bug can be solved using this system (And at the cost
        of using an alternative attack system =P).

    */


    |-----|
    | API |
    |-----|
    /*
      */
struct AttackModifier/*

          */
string missileArt   /*  The model of the special effect attached to the modified attacks

          */
static method   create              takes nothing                               returns AttackModifier/*
          */
method          destroy             takes nothing                               returns nothing/*
            - Constructor/Destructor

          */
method          registerUnit        takes unit whichUnit                        returns this/*
          */
method          unregisterUnit      takes unit whichUnit                        returns this/*
          */
method          clearUnits          takes unit whichUnit                        returns this/*
            - Adds/Removes an attack modifier to/from a unit

          */
method          registerHandler     takes code handler                          returns this/*
          */
method          unregisterHandler   takes code handler                          returns this/*
          */
method          clearHandlers       takes nothing                               returns this/*
            - Registers/Unregisters a handler code that will run when a modified Attack lands on the target
            - You can use the event responses of the AttackSystem library inside these registered handler codes

          */
method          updateAttackerArt   takes string model, string attachPoint      returns this/*
            - Updates the special effect attached to all units with this attack modifier


    */
//! endnovjass

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

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

    private struct List1 extends array
        AttackModifier data
        implement NxList
    endstruct

    private struct List2 extends array
        AttackModifier data
        implement NxList
    endstruct

    private struct UnitList extends array
        integer data
        effect art
        implement NxList
    endstruct

    struct AttackModifier

        string missileArt
        private trigger trigger
        private integer handlerCount
        private effect attackerEffect
        private string attackerEffectModel

        private static TableArray table
        private static TableArray handlerTable

        method updateAttackerArt takes string model, string attachPoint returns thistype
            local UnitList node
            if model != null and attachPoint != null then
                set node = UnitList(this).first
                loop
                    exitwhen node == 0
                    call DestroyEffect(node.art)
                    set node.art = AddSpecialEffectTarget(model, GetUnitById(node.data), attachPoint)
                    set node = node.next
                endloop
            endif
            return this
        endmethod

        method registerUnit takes unit u returns thistype
            local integer unitId = GetUnitId(u)
            local List1 node = List1(unitId).enqueue()
            debug call ThrowError(table[this].has(unitId), "AttackModifier", "registerUnit()", "thistype", this, "Attempted to register an already registered unit")
            set node.data = this
            set table[this][unitId] = node
            set node = UnitList(this).enqueue()
            set UnitList(node).data = unitId
            set table[this][-unitId] = node
            return this
        endmethod
        method unregisterUnit takes unit u returns thistype
            local integer unitId = GetUnitId(u)
            local UnitList node = table[this][-unitId]
            debug call ThrowError(not table[this].has(unitId), "AttackModifier", "unregisterUnit()", "thistype", this, "Attempted to unregister an unregistered unit")
            call DestroyEffect(node.art)
            set node.art = null
            call node.remove()
            call List1(table[this][unitId]).remove()
            call table[this].remove(-unitId)
            call table[this].remove(unitId)
            return this
        endmethod
        method clearUnits takes nothing returns thistype
            local UnitList node = UnitList(this).first
            local UnitList nextNode
            loop
                exitwhen node == 0
                set nextNode = node.next
                call this.unregisterUnit(GetUnitById(node.data))
                set node = nextNode
            endloop
            return this
        endmethod

        method registerHandler takes code handler returns thistype
            local boolexpr expr = Filter(handler)
            debug call ThrowError(handlerTable[this].has(GetHandleId(expr)), "AttackModifier", "registerHandler()", "thistype", this, "Attempted to register an already registered handler")
            if this.handlerCount == 0 then
                set this.trigger = CreateTrigger()
            endif
            set this.handlerCount = this.handlerCount + 1
            set handlerTable[this].triggercondition[GetHandleId(expr)] = TriggerAddCondition(this.trigger, expr)
            return this
        endmethod
        method unregisterHandler takes code handler returns thistype
            local integer exprId = GetHandleId(Filter(handler))
            debug call ThrowError(not handlerTable[this].has(exprId), "AttackModifier", "unregisterHandler()", "thistype", this, "Attempted to unregister an unregistered handler")
            call TriggerRemoveCondition(this.trigger, handlerTable[this].triggercondition[exprId])
            call handlerTable[this].handle.remove(exprId)
            set this.handlerCount = this.handlerCount - 1
            if this.handlerCount == 0 then
                call DestroyTrigger(this.trigger)
                set this.trigger = null
            endif
            return this
        endmethod
        method clearHandlers takes nothing returns thistype
            if this.handlerCount > 0 then
                call handlerTable[this].flush()
                call DestroyTrigger(this.trigger)
                set this.trigger = null
                set this.handlerCount = 0
            endif
            return this
        endmethod

        static method create takes nothing returns thistype
            local thistype this = allocate()
            call UnitList(this).clear()
            debug set table[0].boolean[this] = true
            return this
        endmethod
        method destroy takes nothing returns nothing
            debug call ThrowError(not table[0].boolean[this], "AttackModifier", "destroy()", "thistype", this, "Double-free Attempted")
            debug set table[0].boolean[this] = false
            call this.clearUnits()
            call this.clearHandlers()
            call UnitList(this).destroy()
            call this.deallocate()
        endmethod

        private static method onMeleeAttackLaunch takes nothing returns nothing
            local List1 node
            if IsEventAttackMelee() then
                set node = List1(GetUnitId(GetEventAttackSource())).first
                loop
                    exitwhen node == 0
                    call TriggerEvaluate(node.data.trigger)
                    set node = node.next
                endloop
            endif
        endmethod

        private static method onAttackMissileLaunch takes nothing returns nothing
            local List1 node = List1(GetUnitId(GetEventAttackSource())).first
            call List2(CustomAttack.triggerAttack).clear()
            loop
                exitwhen node == 0
                set List2(CustomAttack.triggerAttack).enqueue().data = node.data
                call CustomAttack.triggerAttack.addSfx(node.data.missileArt)
                set node = node.next
            endloop
        endmethod

        private static method onAttackMissileImpact takes nothing returns nothing
            local List2 node = List2(CustomAttack.triggerAttack).first
            loop
                exitwhen node == 0
                call TriggerEvaluate(node.data.trigger)
                set node = node.next
            endloop
        endmethod

        private static method onAttackMissileDestroy takes nothing returns nothing
            if not IsEventAttackMelee() then
                call List2(CustomAttack.triggerAttack).destroy()
            endif
        endmethod

        private static method onIndex takes nothing returns nothing
            call List1(GetIndexedUnitId()).clear()
        endmethod
        private static method onDeindex takes nothing returns nothing
            local List1 node = List1(GetIndexedUnitId()).first
            local List1 nextNode
            loop
                exitwhen node == 0
                set nextNode = node.next
                call node.data.unregisterUnit(GetIndexedUnit())
                set node = nextNode
            endloop
            call List1(GetIndexedUnitId()).destroy()
        endmethod

        private static method init takes nothing returns nothing
            local code onIndex = function thistype.onIndex
            local code onDeindex = function thistype.onDeindex
            call OnUnitIndex(onIndex)
            call OnUnitDeindex(onDeindex)
            call RegisterAttackEventHandler(EVENT_ATTACK_FINISH, function thistype.onMeleeAttackLaunch)
            call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_LAUNCH, function thistype.onAttackMissileLaunch)
            call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_IMPACT, function thistype.onAttackMissileImpact)
            call RegisterAttackEventHandler(EVENT_ATTACK_DESTROY, function thistype.onAttackMissileDestroy)
            set table = TableArray[JASS_MAX_ARRAY_SIZE - 1]
            set handlerTable = TableArray[JASS_MAX_ARRAY_SIZE - 1]
        endmethod
        implement Init

    endstruct


endlibrary
library AttackMissileUtils initializer Init /*


    */
uses /*

    */
AttackSystem      /*    https://www.hiveworkshop.com/threads/307417/
    */
UnitDex           /*    https://www.hiveworkshop.com/threads/248209/


    */
//! novjass

    |-------------|
    | Description |
    |-------------|
    /*
        This library provides utility functions for Attack missile enumerations.

    */


    |-----|
    | API |
    |-----|
    /*

      */
Attack array ENUMED_ATTACK_MISSILES/*
        - This array variable stores all the enumerated Attack missiles after calling one of the functions below
        - The stored values are automatically cleared before the enumeration when calling the enumerator functions
        - Acts as a circular singly-linked list
        - How to traverse the list:
          local Attack node
          call EnumAttackMissilesInRange(0.00, 0.00, 100.00)
          set node = ENUMED_ATTACK_MISSILES[0] //refers to the first element in the list
          loop
              exitwhen node == 0
              ...
              set node = ENUMED_ATTACK_MISSILES[node] //returns 0 if 'node' is the last element
          endloop


      */
function    EnumAttackMissilesAll                   takes nothing                                                                           returns nothing/*
      */
function    EnumAttackMissilesInRange               takes real x, real y, real radius                                                       returns nothing/*
      */
function    EnumAttackMissilesInSphere              takes real x, real y, real z, real radius                                               returns nothing/*
      */
function    EnumAttackMissilesInRectangle           takes real minX, real minY, real maxX, real maxY                                        returns nothing/*
      */
function    EnumAttackMissilesInCube                takes real minX, real minY, real minZ, real maxX, real maxY, real maxZ                  returns nothing/*
      */
function    EnumAttackMissilesInCylinder            takes real x, real y, real radius, real minZ, real maxZ                                 returns nothing/*

      */
function    EnumAttackMissilesOfUnitAll             takes unit source                                                                       returns nothing/*
      */
function    EnumAttackMissilesOfUnitInRange         takes unit source, real x, real y, real radius                                          returns nothing/*
      */
function    EnumAttackMissilesOfUnitInSphere        takes unit source, real x, real y, real z, real radius                                  returns nothing/*
      */
function    EnumAttackMissilesOfUnitInRectangle     takes unit source, real minX, real minY, real maxX, real maxY                           returns nothing/*
      */
function    EnumAttackMissilesOfUnitInCube          takes unit source, real minX, real minY, real minZ, real maxX, real maxY, real maxZ     returns nothing/*
      */
function    EnumAttackMissilesOfUnitInCylinder      takes unit source, real x, real y, real radius, real minZ, real maxZ                    returns nothing/*


    */
//! endnovjass

    globals
        Attack array ENUMED_ATTACK_MISSILES
        private integer array prev
        private integer array next
        private integer array unitPrev
        private integer array unitNext
        private integer array unitFirst
        private integer array unitLast
    endglobals

    private function ClearPrevEnumed takes nothing returns nothing
        local integer node = ENUMED_ATTACK_MISSILES[0]
        local integer temp
        loop
            exitwhen node == 0
            set temp = ENUMED_ATTACK_MISSILES[node]
            set ENUMED_ATTACK_MISSILES[node] = 0
            set node = temp
        endloop
        set ENUMED_ATTACK_MISSILES[0] = 0
    endfunction

    private function EnumAttackMissiles takes real x, real y, real z, real radius, boolean spherical returns nothing
        local Attack node = next[0]
        local integer temp = 0
        local real dx
        local real dy
        local real dz
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            set dx = node.x - x
            set dy = node.y - y
            if spherical then
                set dz = node.z - z
            else
                set dz = 0.00
            endif
            if dx*dx + dy*dy + dz*dz <= radius*radius then
                set ENUMED_ATTACK_MISSILES[temp] = node
                set temp = node
            endif
            set node = next[node]
        endloop
    endfunction

    function EnumAttackMissilesInRange takes real x, real y, real radius returns nothing
        call EnumAttackMissiles(x, y, 0.00, radius, false)
    endfunction

    function EnumAttackMissilesInSphere takes real x, real y, real z, real radius returns nothing
        call EnumAttackMissiles(x, y, z, radius, true)
    endfunction

    function EnumAttackMissilesInRectangle takes real minX, real minY, real maxX, real maxY returns nothing
        local Attack node = next[0]
        local integer temp = 0
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            if node.x >= minX and node.x <= maxX and node.y >= minY and node.y <= maxY then
                set ENUMED_ATTACK_MISSILES[temp] = node
                set temp = node
            endif
            set node = next[node]
        endloop
    endfunction

    function EnumAttackMissilesInCube takes real minX, real minY, real minZ, real maxX, real maxY, real maxZ returns nothing
        local Attack node = next[0]
        local integer temp = 0
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            if node.x >= minX and node.x <= maxX and node.y >= minY and node.y <= maxY and node.z >= minZ and node.z <= maxZ then
                set ENUMED_ATTACK_MISSILES[temp] = node
                set temp = node
            endif
            set node = next[node]
        endloop
    endfunction

    function EnumAttackMissilesInCylinder takes real x, real y, real radius, real minZ, real maxZ returns nothing
        local Attack node = next[0]
        local integer temp = 0
        local real dx
        local real dy
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            if node.z >= minZ and node.z <= maxZ then
                set dx = node.x - x
                set dy = node.y - y
                if dx*dx + dy*dy <= radius*radius then
                    set ENUMED_ATTACK_MISSILES[temp] = node
                    set temp = node
                endif
            endif
            set node = next[node]
        endloop
    endfunction

    function EnumAttackMissilesAll takes nothing returns nothing
        local Attack node = next[0]
        local integer temp = 0
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            set ENUMED_ATTACK_MISSILES[temp] = node
            set temp = node
            set node = next[node]
        endloop
    endfunction

    private function EnumAttackMissilesOfUnit takes unit source, real x, real y, real z, real radius, boolean spherical returns nothing
        local Attack node = unitFirst[GetUnitId(source)]
        local integer temp = 0
        local real dx
        local real dy
        local real dz
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            set dx = node.x - x
            set dy = node.y - y
            if spherical then
                set dz = node.z - z
            else
                set dz = 0.00
            endif
            if dx*dx + dy*dy + dz*dz <= radius*radius then
                set ENUMED_ATTACK_MISSILES[temp] = node
                set temp = node
            endif
            set node = unitNext[node]
        endloop
    endfunction

    function EnumAttackMissilesOfUnitInRange takes unit source, real x, real y, real radius returns nothing
        call EnumAttackMissilesOfUnit(source, x, y, 0.00, radius, false)
    endfunction

    function EnumAttackMissilesOfUnitInSphere takes unit source, real x, real y, real z, real radius returns nothing
        call EnumAttackMissilesOfUnit(source, x, y, z, radius, true)
    endfunction

    function EnumAttackMissilesOfUnitInRectangle takes unit source, real minX, real minY, real maxX, real maxY returns nothing
        local Attack node = unitFirst[GetUnitId(source)]
        local integer temp = 0
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            if node.x >= minX and node.x <= maxX and node.y >= minY and node.y <= maxY then
                set ENUMED_ATTACK_MISSILES[temp] = node
                set temp = node
            endif
            set node = unitNext[node]
        endloop
    endfunction

    function EnumAttackMissilesOfUnitInCube takes unit source, real minX, real minY, real minZ, real maxX, real maxY, real maxZ returns nothing
        local Attack node = unitFirst[GetUnitId(source)]
        local integer temp = 0
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            if node.x >= minX and node.x <= maxX and node.y >= minY and node.y <= maxY and node.z >= minZ and node.z <= maxZ then
                set ENUMED_ATTACK_MISSILES[temp] = node
                set temp = node
            endif
            set node = unitNext[node]
        endloop
    endfunction

    function EnumAttackMissilesOfUnitInCylinder takes unit source, real x, real y, real radius, real minZ, real maxZ returns nothing
        local Attack node = unitFirst[GetUnitId(source)]
        local integer temp = 0
        local real dx
        local real dy
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            if node.z >= minZ and node.z <= maxZ then
                set dx = node.x - x
                set dy = node.y - y
                if dx*dx + dy*dy <= radius*radius then
                    set ENUMED_ATTACK_MISSILES[temp] = node
                    set temp = node
                endif
            endif
            set node = unitNext[node]
        endloop
    endfunction

    function EnumAttackMissilesOfUnitAll takes unit source returns nothing
        local Attack node = unitFirst[GetUnitId(source)]
        local integer temp = 0
        call ClearPrevEnumed()
        loop
            exitwhen node == 0
            set ENUMED_ATTACK_MISSILES[temp] = node
            set temp = node
            set node = unitNext[node]
        endloop
    endfunction

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

    private function UnitRemoveNode takes integer unitId, integer node returns nothing
        if unitPrev[node] == 0 then
            set unitFirst[unitId] = unitNext[node]
        else
            set unitNext[unitPrev[unitId]] = unitNext[node]
        endif
        if unitNext[node] == 0 then
            set unitLast[unitId] = unitPrev[node]
        else
            set unitPrev[unitNext[unitId]] = unitPrev[node]
        endif
    endfunction

    private function OnMissileLaunch takes nothing returns nothing
        local integer node = CustomAttack.triggerAttack
        local integer unitId = GetUnitId(GetEventAttackSource())

        // Enqueue into the global list
        set next[node] = 0
        set prev[node] = prev[0]
        set next[prev[0]] = node
        set prev[0] = node

        // Enqueue into the unit-specific list
        if unitFirst[unitId] == 0 then
            set unitFirst[unitId] = node
        else
            set unitNext[unitLast[unitId]] = node
        endif
        set unitNext[node] = 0
        set unitPrev[node] = unitLast[unitId]
        set unitLast[unitId] = node
    endfunction

    private function OnMissileDestroy takes nothing returns nothing
        local integer node = CustomAttack.triggerAttack
        local integer unitId

        if not IsEventAttackMelee() then
            set unitId = GetUnitId(GetEventAttackSource())

            // Remove from the global list
            set next[prev[node]] = next[node]
            set prev[next[node]] = prev[node]

            // Remove from the unit-specific list
            call UnitRemoveNode(GetUnitId(GetEventAttackSource()), node)
        endif
    endfunction

    private function OnDeindex takes nothing returns nothing
        local integer node = unitFirst[GetIndexedUnitId()]
        loop
            exitwhen node == 0
            // Remove from the unit-specific list
            call UnitRemoveNode(GetIndexedUnitId(), node)
            set node = unitNext[node]
        endloop
    endfunction

    private function Init takes nothing returns nothing
        local code onDeindex = function OnDeindex
        call OnUnitDeindex(onDeindex)
        call RegisterAttackEventHandler(EVENT_ATTACK_MISSILE_LAUNCH, function OnMissileLaunch)
        call RegisterAttackEventHandler(EVENT_ATTACK_DESTROY, function OnMissileDestroy)
    endfunction


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
            &nbs