• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[GUI-Friendly] Custom Missile System 1.4

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: ILH
Description:
This is my Custom Missile System.

The reason why I made it is because you have on-collide events for units, destructables and items.
You also can transform existing unit to a missile and transform them back afterwards.
Another nice feature is that a missile can hit the same target multiple times but not multiple times between the given amount of time.
Another reason why I made this system is to have missiles that can collide with other missiles and can be targetable.

How to install:
JASS:
//  How to install:
//      1. Copy or create all variables that are used in this map into your map.
//          1a. If you use vJASS then you can enable the CMS_vJASS_variables trigger and use that one.
//          1b. If you use GUI then you can use the CMS_GUI_variables trigger to automatically import them.
//      2. Copy the CMS_System trigger, CMS_Configuration trigger and CMS_Collision trigger and paste it into your map.
//      4. Import the same models that are imported by this map into yours.
//          (You can export the files in the import manager.)
//          (It is not necessary to import the icon too.)
//      5. Create or copy the objects "MISSILE", "DUMMY", "CMS ABILITY GHOST" and "CMS ABILITY MORPH" into your map.
//          Make sure that "CMS ABILITY MORPH" has the proper values.
//      6. Set the variables in the CMS_Configuration trigger to the right values.
//      7. Make sure that you have all required libraries in your map.
//          If one of the libraries from this map is of a higher version, then use the one from this map.

JASS:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Wietlol's Custom Missile System 1.4 15/12/2015
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Description:
//      This system is made to create custom missiles.
//      It is designed to have 3D movement, events on collision, de-locusted missiles, different collision detections,
//          targetable missiles, units as missiles, full missile control, collisions with items and destructables,
//          missiles that can hit targets multiple times and with GUI support.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  How to install:
//      1. Copy or create all variables that are used in this map into your map.
//          1a. If you use vJASS then you can enable the CMS_vJASS_variables trigger and use that one.
//          1b. If you use GUI then you can use the CMS_GUI_variables trigger to automatically import them.
//      2. Copy the CMS_System trigger, CMS_Configuration trigger and CMS_Collision trigger and paste it into your map.
//      4. Import the same models that are imported by this map into yours.
//          (You can export the files in the import manager.)
//          (It is not necessary to import the icon too.)
//      5. Create or copy the objects "MISSILE", "DUMMY", "CMS ABILITY GHOST" and "CMS ABILITY MORPH" into your map.
//          Make sure that "CMS ABILITY MORPH" has the proper values.
//      6. Set the variables in the CMS_Configuration trigger to the right values.
//      7. Make sure that you have all required libraries in your map.
//          If one of the libraries from this map is of a higher version, then use the one from this map.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Feature list:
//      This system:
//      - allows you to create missiles with
//          - 3d movement visualizations.
//          - 3d movement calculations.
//          - 3d acceleration.
//          - collision detection with all widgets with different algorithms.
//          - homing behavior.
//          - a unique index for arrays with your custom data.
//      - allows the creation of missiles from existing units (for usages like a dash or knockback).
//      - has on-collide events for all widgets.
//      - can run a large amount of missiles.
//      - allows missile to hit the same target multiple times with a configurable delay as "Collision_Cooldown".
//        
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Function List:
//      function CMS_CreateMissile takes unit source, integer typeId, unit whichMissile, boolean importDefaultValues returns integer
//          Creates a new missile using the starting variables and the given parameters.
//         
//      function CMS_CreateMissileEx takes nothing returns integer
//          Creates a new missile only using the starting variables.
//          This function is recommended for GUI users.
//         
//      function CMS_DestroyMissile takes integer index returns boolean
//          Immediately destroys the missile of the given index.
//         
//      function CMS_GetIndex takes unit whichMissile returns integer
//          Returns the index of a missile.
//          This can be used in combination with "CMS_DestroyMissile()".
//         
//      function CMS_SetDefaultVariables takes integer index returns nothing
//          Sets all the param variables to their default value.
//         
//      function CMS_GetSpeedPerSecond takes integer index returns real
//          Returns the speed per second.
//      function CMS_SetSpeedPerSecond takes integer index, real speed returns nothing
//          Sets the speed per second.
//         
//      function CMS_GetAngleDeg takes integer index returns real
//          Returns the angle in degrees.
//      function CMS_SetAngleDeg takes integer index, real angle returns nothing
//          Sets the angle in degrees.
//         
//      function CMS_GetPitchDeg takes integer index returns real
//          Returns the pitch in degrees.
//      function CMS_SetPitchDeg takes integer index, real pitch returns nothing
//          Sets the pitch in degrees.
//         
//      function CMS_GetAccelerationPerSecond takes integer index returns real
//          Returns the acceleration per second.
//      function CMS_SetAccelerationPerSecond takes integer index, real acc returns nothing
//          Sets the acceleration per second.
//         
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Credits:
//      * To Vexorian and Tickles
//          - For Vexorian's dummy model that is used as the missile model which is editted by Tickles.
//      * To Xonok
//          - For showing me his missile system on which this structure is designed.
//         
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Changelog:
//      1.4 - 15/12/2015 - Additional features (detection, optimized events, acceleration).
//          - Replaced locations with coordinates.
//          - Removed "_Param" prefixes from variables that are not array variables of the missiles.
//          - Added Acceleration.
//          - Added event calls with udg_CMS_Param_Type[] as value.
//          - Trigger calls for each X missiles to prevent op limit.
//          - Optmized collision cooldown.
//          - Added Rectangular collision detection.
//          - Added Polygon collision detection.
//          - Added booleans to check for Unit, Destructable and Item separately.
//         
//      1.3.3 - 6/12/2015 - Fixed bugs
//          - Replaced widget handles in hashtable with their handle index to prevent timer/widget leak
//              when the target or missile is removed during the timer.
//          - Fixed a bug where the missiles didn't start at their StartingHeight when a new missile was created.
//          - Replaced some GetWidgetX/Y() with GetUnitX/Y().
//          - Updated BasicFunctions usage.
//          - Removed "_Param" from starting variables because of GUI variable length limit.
//          - Added StartingDefaultValues to import default values for GUI usage. (Default true.)
//          - Added CMS_CreateMissileEx(source, missile, typeId, importDefaultValues).
//         
//      1.3.2 - 14/11/2015 - Reviewed system.
//          - Added final starting parameters.
//          - Replaced GetWidgetX()/GetWidgetY() with GetUnitX()/GetUnitY().
//          - Replaced KillUnit() with UnitApplyTimedLife().
//         
//      1.3 - 20/05/2015 - Redesigned system structure.
//          - Placed calculations inside functions with a cache.
//          - Removed calculations from the interval function.
//          - Finished system's concept.
//          - Removed the missile acceleration option.
//         
//      1.2 - 15/05/2015 - Rewritten System
//          - Rewrote the data structure into arrays only.
//          - Ripped stuff that is not necessary.
//          - Uses locust bugged units so the missiles are targetable but unselectable.
//          - Uses better getter and setter.
//          - Uses library and got rid of triggers.
//         
//      1.1 - 26/03/2015 - Collision Fixes
//          - Fixed map bounds check.
//          - Added Ghost ability.
//          - Added CMS_Param_Remove_Collision.
//         
//      1.0 - 01/03/2015 - First official release.
//         
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Known bugs:
//      - None
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////





/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  CMS System
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library CustomMissileSystem uses PositionFunctions, TimeFunctions
   
    //These globals are not needed in GUI so they are implemented inside the system itself.
    globals
       
        integer         udg_CMS_COLLISIONTYPE_POLYGON
        Polygon array   udg_CMS_Param_Polygon
       
        hashtable       udg_CMS_HASHTABLE   = InitHashtable()
        group           udg_CMS_GROUP       = CreateGroup()
        timer           udg_CMS_TIMER       = CreateTimer()
        real            udg_CMS_INTERVAL    = 0.03
        trigger         udg_CMS_TRIGGER     = CreateTrigger()
        integer         udg_CMS_LOOP_MAX    = 100
        //Missile positions in interval function.
        real            udg_CMS_X           = 0
        real            udg_CMS_Y           = 0
        real            udg_CMS_X_OLD       = 0
        real            udg_CMS_Y_OLD       = 0
        //Rectangular collision detection.
        real            udg_CMS_RX          = 0
        real            udg_CMS_RY          = 0
        real            udg_CMS_RW          = 0
        real            udg_CMS_RH          = 0
        real            udg_CMS_RA          = 0
       
        real            udg_CMS_GameTime    = 0
       
        integer         udg_CMS_NextIndex   = 0
        boolean array   udg_CMS_Index_Occupied
       
        integer array   udg_CMS_MissileIndex
        real array      udg_CMS_Param_OriginalHeight
        real array      udg_CMS_Param_V_X
        real array      udg_CMS_Param_V_Y
        real array      udg_CMS_Param_V_Z
        real array      udg_CMS_Param_A_X
        real array      udg_CMS_Param_A_Y
        real array      udg_CMS_Param_A_Z
       
        integer         udg_CMS_IntervalCount
        real array      udg_CMS_Cache_V_H
        integer array   udg_CMS_Cache_V_H_Index
        real array      udg_CMS_Cache_A_H
        integer array   udg_CMS_Cache_A_H_Index
        real array      udg_CMS_Cache_Angle
        integer array   udg_CMS_Cache_Angle_Index
        real array      udg_CMS_Cache_Pitch
        integer array   udg_CMS_Cache_Pitch_Index
        real array      udg_CMS_Cache_Speed
        integer array   udg_CMS_Cache_Speed_Index
        real array      udg_CMS_Cache_Acceleration
        integer array   udg_CMS_Cache_Acceleration_Index
       
    endglobals
   
    //Set the default variables of the params with the given index.
    function CMS_SetDefaultVariables takes integer index returns nothing
        set udg_CMS_Param_Collision_Cooldown[index]     = 1.
        set udg_CMS_Param_Collision_Dest[index]         = false
        set udg_CMS_Param_Collision_Height[index]       = 0.
        set udg_CMS_Param_Collision_Height_Inc[index]   = 0.
        set udg_CMS_Param_Collision_Item[index]         = false
        set udg_CMS_Param_Collision_Type[index]         = 0
        set udg_CMS_Param_Collision_Unit[index]         = true
        set udg_CMS_Param_Collision_Width[index]        = 50.
        set udg_CMS_Param_Collision_Width_Inc[index]    = 0.
        set udg_CMS_Param_Distance[index]               = 0.
        set udg_CMS_Param_Duration[index]               = 0.
        set udg_CMS_Param_Effect[index]                 = null
        set udg_CMS_Param_Gravity[index]                = 0.
        set udg_CMS_Param_IsAUnit[index]                = false
        set udg_CMS_Param_IsDestroyed[index]            = false
        set udg_CMS_Param_IsFlying[index]               = false
        set udg_CMS_Param_IsHoming[index]               = false
        set udg_CMS_Param_MaxDistance[index]            = 0.
        set udg_CMS_Param_MaxDuration[index]            = 0.
        set udg_CMS_Param_Missile[index]                = null
        set udg_CMS_Param_OriginalHeight[index]         = 0.
        set udg_CMS_Param_Source[index]                 = null
        set udg_CMS_Param_Subtype[index]                = 0
        set udg_CMS_Param_Target_Location[index]        = null
        set udg_CMS_Param_Target_Type[index]            = 0
        set udg_CMS_Param_Target_Unit[index]            = null
        set udg_CMS_Param_TurnRate[index]               = 0.
        set udg_CMS_Param_Type[index]                   = -1
        set udg_CMS_Param_WalkingHeight[index]          = 0
        set udg_CMS_Param_A_X[index]                    = 0.
        set udg_CMS_Param_A_Y[index]                    = 0.
        set udg_CMS_Param_A_Z[index]                    = 0.
        set udg_CMS_Param_V_X[index]                    = 0.
        set udg_CMS_Param_V_Y[index]                    = 0.
        set udg_CMS_Param_V_Z[index]                    = 0.
        set udg_CMS_Cache_A_H[index]                    = 0.
        set udg_CMS_Cache_V_H[index]                    = 0.
        set udg_CMS_Cache_Acceleration[index]           = 0.
        set udg_CMS_Cache_Angle[index]                  = 0.
        set udg_CMS_Cache_Pitch[index]                  = 0.
        set udg_CMS_Cache_Speed[index]                  = 0.
        set udg_CMS_Cache_V_H_Index[index]              = 0
        set udg_CMS_Cache_Acceleration_Index[index]     = 0
        set udg_CMS_Cache_Angle_Index[index]            = 0
        set udg_CMS_Cache_Pitch_Index[index]            = 0
        set udg_CMS_Cache_Speed_Index[index]            = 0
    endfunction
   
    //Create a new unique id for the missile.
    function CMS_CreateUniqueId takes nothing returns integer
        loop
            set udg_CMS_NextIndex = udg_CMS_NextIndex + 1
            if udg_CMS_NextIndex > 8190 then
                set udg_CMS_NextIndex = 1
            endif
           
            exitwhen not udg_CMS_Index_Occupied[udg_CMS_NextIndex]
        endloop
       
        set udg_CMS_Index_Occupied[udg_CMS_NextIndex] = true
        return udg_CMS_NextIndex
    endfunction
   
    //Set the Z angle animation.
    function CMS_SetUnitZAngle takes unit whichUnit, real angle returns nothing
        //Can only be used on Vexorian's Dummy model or other models which use the same animations.
        local integer i = GetUnitTypeId(whichUnit)
       
        if i != udg_CMS_MISSILE_UNITTYPE and i != udg_CMS_DUMMY_UNITTYPE then
            return
        endif
       
        set i = R2I(angle*RADTODEG + 90.5)
       
        if i >= 180 then
            set i = 179
        elseif i < 0 then
            set i = 0
        endif
       
        call SetUnitAnimationByIndex(whichUnit, i)
    endfunction
   
    //Get the missile index of the given unit.
    function CMS_GetIndex takes unit whichMissile returns integer
        return udg_CMS_MissileIndex[GetUnitUserData(whichMissile)]
    endfunction
   
    //Set/get horizontal velocity.
    //Only used internal.
    function CMS_GetVH takes integer index returns real
        local real V_H
       
        if udg_CMS_Cache_V_H_Index[index] == udg_CMS_IntervalCount then
            return udg_CMS_Cache_V_H[index]
        endif
       
        set V_H = SquareRoot(udg_CMS_Param_V_X[index]*udg_CMS_Param_V_X[index] + udg_CMS_Param_V_Y[index]*udg_CMS_Param_V_Y[index])
       
        set udg_CMS_Cache_V_H[index] = V_H
        set udg_CMS_Cache_V_H_Index[index] = udg_CMS_IntervalCount
       
        return V_H
    endfunction
    function CMS_SetVH takes integer index, real V_H returns nothing
        local real V_H_Factor
       
        if CMS_GetVH(index) == 0. then
            set udg_CMS_Param_V_X[index] = 0.
            set udg_CMS_Param_V_Y[index] = 0.
        else
            set V_H_Factor = V_H / udg_CMS_Cache_V_H[index]
            set udg_CMS_Param_V_X[index] = udg_CMS_Param_V_X[index] * V_H_Factor
            set udg_CMS_Param_V_Y[index] = udg_CMS_Param_V_Y[index] * V_H_Factor
        endif
       
        set udg_CMS_Cache_V_H[index] = V_H
        set udg_CMS_Cache_V_H_Index[index] = udg_CMS_IntervalCount
       
    endfunction
   
    //Set/get 3D speed.
    //BJ functions for speed per second and normal functions for speed per interval.
    function CMS_GetSpeed takes integer index returns real
        local real speed
       
        if udg_CMS_Cache_Speed_Index[index] == udg_CMS_IntervalCount then
            return udg_CMS_Cache_Speed[index]
        endif
       
        set speed = SquareRoot(udg_CMS_Param_V_X[index]*udg_CMS_Param_V_X[index] + udg_CMS_Param_V_Y[index]*udg_CMS_Param_V_Y[index] + udg_CMS_Param_V_Z[index]*udg_CMS_Param_V_Z[index])
       
        set udg_CMS_Cache_Speed[index] = speed
        set udg_CMS_Cache_Speed_Index[index] = udg_CMS_IntervalCount
       
        return speed
    endfunction
    function CMS_SetSpeed takes integer index, real speed returns nothing
        local real factor
        local real oldSpeed = CMS_GetSpeed(index)
       
        if oldSpeed == 0. then
            set udg_CMS_Param_V_X[index] = speed
        else
            set factor = speed/oldSpeed
           
            set udg_CMS_Param_V_X[index] = udg_CMS_Param_V_X[index] * factor
            set udg_CMS_Param_V_Y[index] = udg_CMS_Param_V_Y[index] * factor
            set udg_CMS_Param_V_Z[index] = udg_CMS_Param_V_Z[index] * factor
        endif
       
        set udg_CMS_Cache_Speed[index] = speed
        set udg_CMS_Cache_Speed_Index[index] = udg_CMS_IntervalCount
       
    endfunction
    function CMS_GetSpeedPerSecond takes integer index returns real
        return CMS_GetSpeed(index) / udg_CMS_INTERVAL
    endfunction
    function CMS_SetSpeedPerSecond takes integer index, real speed returns nothing
        call CMS_SetSpeed(index, speed * udg_CMS_INTERVAL)
    endfunction
   
    function CMS_GetAH takes integer index returns real
        local real A_H
       
        if udg_CMS_Cache_A_H_Index[index] == udg_CMS_IntervalCount then
            return udg_CMS_Cache_A_H[index]
        endif
       
        set A_H = SquareRoot(udg_CMS_Param_A_X[index]*udg_CMS_Param_A_X[index] + udg_CMS_Param_A_Y[index]*udg_CMS_Param_A_Y[index])
       
        set udg_CMS_Cache_A_H[index] = A_H
        set udg_CMS_Cache_A_H_Index[index] = udg_CMS_IntervalCount
       
        return A_H
    endfunction
    function CMS_SetAH takes integer index, real A_H returns nothing
        local real A_H_Factor
        local real speed
       
        if CMS_GetAH(index) == 0. then
            set speed = CMS_GetSpeed(index)
            if speed == 0. then
                set udg_CMS_Param_A_X[index] = A_H
            else
                set udg_CMS_Param_A_X[index] = A_H * (udg_CMS_Param_V_X[index] / speed)
                set udg_CMS_Param_A_Y[index] = A_H * (udg_CMS_Param_V_Y[index] / speed)
                set udg_CMS_Param_A_Z[index] = A_H * (udg_CMS_Param_V_Z[index] / speed)
            endif
        else
            set A_H_Factor = A_H / udg_CMS_Cache_A_H[index]
            set udg_CMS_Param_A_X[index] = udg_CMS_Param_A_X[index] * A_H_Factor
            set udg_CMS_Param_A_Y[index] = udg_CMS_Param_A_Y[index] * A_H_Factor
        endif
       
        set udg_CMS_Cache_A_H[index] = A_H
        set udg_CMS_Cache_A_H_Index[index] = udg_CMS_IntervalCount
       
    endfunction
   
    //Set/get angle (facing angle).
    function CMS_GetAngle takes integer index returns real
        local real angle
       
        if udg_CMS_Cache_Angle_Index[index] == udg_CMS_IntervalCount then
            return udg_CMS_Cache_Angle[index]
        endif
       
        if udg_CMS_Param_V_X[index] == 0. then
            if udg_CMS_Param_V_Y[index] < 0. then
                set angle = PI * 1.5
            elseif udg_CMS_Param_V_Y[index] > 0. then
                set angle = PI * 0.5
            else
                set angle = 0.
            endif
        else
            set angle = Atan(udg_CMS_Param_V_Y[index] / udg_CMS_Param_V_X[index])
            if udg_CMS_Param_V_X[index] < 0. then
                set angle = angle -PI
            endif
        endif
       
        set udg_CMS_Cache_Angle[index] = angle
        set udg_CMS_Cache_Angle_Index[index] = udg_CMS_IntervalCount
       
        return angle
    endfunction
    function CMS_SetAngle takes integer index, real angle returns nothing
        local real V_H = CMS_GetVH(index)
        local real A_H = CMS_GetAH(index)
        local real cos = Cos(angle)
        local real sin = Sin(angle)
        set udg_CMS_Param_V_X[index] = cos * V_H
        set udg_CMS_Param_V_Y[index] = sin * V_H
        set udg_CMS_Param_A_X[index] = cos * A_H
        set udg_CMS_Param_A_Y[index] = sin * A_H
        call SetUnitFacing(udg_CMS_Param_Missile[udg_CMS_LoopIndex], angle*RADTODEG)
       
        set udg_CMS_Cache_Angle[index] = angle
        set udg_CMS_Cache_Angle_Index[index] = udg_CMS_IntervalCount
       
    endfunction
    function CMS_GetAngleDeg takes integer index returns real
        return CMS_GetAngle(index) * RADTODEG
    endfunction
    function CMS_SetAngleDeg takes integer index, real angle returns nothing
        call CMS_SetAngle(index, angle * DEGTORAD)
    endfunction
   
    //Set/get pitch (vertical angle).
    //BJ functions for degree and normal functions for radians.
    function CMS_GetPitch takes integer index returns real
        local real pitch
        local real V_H
       
        if udg_CMS_Cache_Pitch_Index[index] == udg_CMS_IntervalCount then
            return udg_CMS_Cache_Pitch[index]
        endif
       
        set V_H = CMS_GetVH(index)
       
        if V_H == 0. then
            if udg_CMS_Param_V_Z[index] < 0. then
                set pitch = PI * 1.5
            elseif udg_CMS_Param_V_Z[index] > 0. then
                set pitch = PI * 0.5
            else
                set pitch = 0.
            endif
        else
            set pitch = Atan(udg_CMS_Param_V_Z[index] / V_H)
        endif
       
        set udg_CMS_Cache_Pitch[index] = pitch
        set udg_CMS_Cache_Pitch_Index[index] = udg_CMS_IntervalCount
       
        return pitch
    endfunction
    function CMS_SetPitch takes integer index, real pitch returns nothing
        local real V_H = CMS_GetVH(index)
        local real V = SquareRoot(V_H*V_H + udg_CMS_Param_V_Z[index]*udg_CMS_Param_V_Z[index])
        local real A_H = CMS_GetAH(index)
        local real A = SquareRoot(A_H*A_H + udg_CMS_Param_A_Z[index]*udg_CMS_Param_A_Z[index])
        local real sin = Sin(pitch)
        local real cos = Cos(pitch)
       
        call CMS_SetVH(index, cos * V)
        set udg_CMS_Param_V_Z[index] = sin * V
        call CMS_SetAH(index, cos * A)
        set udg_CMS_Param_A_Z[index] = sin * A
       
        call CMS_SetUnitZAngle(udg_CMS_Param_Missile[index], pitch)
        set udg_CMS_Cache_Pitch[index] = pitch
        set udg_CMS_Cache_Pitch_Index[index] = udg_CMS_IntervalCount
       
    endfunction
    function CMS_GetPitchDeg takes integer index returns real
        return CMS_GetPitch(index) * RADTODEG
    endfunction
    function CMS_SetPitchDeg takes integer index, real pitch returns nothing
        call CMS_SetPitch(index, pitch * DEGTORAD)
    endfunction
   
    function CMS_GetAcceleration takes integer index returns real
        local real acc
       
        if udg_CMS_Cache_Acceleration_Index[index] == udg_CMS_IntervalCount then
            return udg_CMS_Cache_Acceleration[index]
        endif
       
        set acc = SquareRoot(udg_CMS_Param_A_X[index]*udg_CMS_Param_A_X[index] + udg_CMS_Param_A_Y[index]*udg_CMS_Param_A_Y[index] + udg_CMS_Param_A_Z[index]*udg_CMS_Param_A_Z[index])
       
        set udg_CMS_Cache_Acceleration[index] = acc
        set udg_CMS_Cache_Acceleration_Index[index] = udg_CMS_IntervalCount
       
        return acc
    endfunction
    function CMS_SetAcceleration takes integer index, real acc returns nothing
        local real factor
        local real oldAcc = CMS_GetAcceleration(index)
        local real speed
       
        if oldAcc == 0. then
            set speed = CMS_GetSpeed(index)
            if speed == 0. then
                set udg_CMS_Param_A_X[index] = acc
            else
                set udg_CMS_Param_A_X[index] = acc * (udg_CMS_Param_V_X[index] / speed)
                set udg_CMS_Param_A_Y[index] = acc * (udg_CMS_Param_V_Y[index] / speed)
                set udg_CMS_Param_A_Z[index] = acc * (udg_CMS_Param_V_Z[index] / speed)
            endif
        else
            set factor = acc/oldAcc
           
            set udg_CMS_Param_A_X[index] = udg_CMS_Param_A_X[index] * factor
            set udg_CMS_Param_A_Y[index] = udg_CMS_Param_A_Y[index] * factor
            set udg_CMS_Param_A_Z[index] = udg_CMS_Param_A_Z[index] * factor
        endif
       
        set udg_CMS_Cache_Acceleration[index] = acc
        set udg_CMS_Cache_Acceleration_Index[index] = udg_CMS_IntervalCount
       
    endfunction
    function CMS_GetAccelerationPerSecond takes integer index returns real
        return CMS_GetAcceleration(index) / (udg_CMS_INTERVAL*udg_CMS_INTERVAL)
    endfunction
    function CMS_SetAccelerationPerSecond takes integer index, real acc returns nothing
        call CMS_SetAcceleration(index, acc * (udg_CMS_INTERVAL*udg_CMS_INTERVAL))
    endfunction
   
    //Destroy the missile of index "index".
    function CMS_DestroyMissile takes integer index returns boolean
        local unit missile
        local real originalHeight
        local boolean isAUnit
       
        set udg_CMS_Index = index
        set udg_CMS_Event_Missile_Destroyed = -1
        set udg_CMS_Event_Missile_Destroyed = 0
        set udg_CMS_Event_Missile_Destroyed = -1
        set udg_CMS_Event_Missile_Destroyed = udg_CMS_Param_Type[udg_CMS_Index]
       
        if not udg_CMS_Param_IsDestroyed[index] then
            return false
        endif
       
        set missile = udg_CMS_Param_Missile[udg_CMS_Index]
        set originalHeight = udg_CMS_Param_OriginalHeight[udg_CMS_Index]
        set isAUnit = udg_CMS_Param_IsAUnit[udg_CMS_Index]
       
        //Remove the special effect and target location.
        call DestroyEffect(udg_CMS_Param_Effect[index])
        call RemoveLocation(udg_CMS_Param_Target_Location[index])
       
        set udg_CMS_MissileIndex[GetUnitUserData(udg_CMS_Param_Missile[udg_CMS_Amount])] = index
        set udg_CMS_MissileIndex[GetUnitUserData(udg_CMS_Param_Missile[index])] = 0
       
        call FlushChildHashtable(udg_CMS_HASHTABLE, GetHandleId(udg_CMS_Param_Missile[index]))
        set udg_CMS_Param_Collision_Cooldown[index]     = udg_CMS_Param_Collision_Cooldown[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Dest[index]         = udg_CMS_Param_Collision_Dest[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Height[index]       = udg_CMS_Param_Collision_Height[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Height_Inc[index]   = udg_CMS_Param_Collision_Height_Inc[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Item[index]         = udg_CMS_Param_Collision_Item[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Type[index]         = udg_CMS_Param_Collision_Type[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Unit[index]         = udg_CMS_Param_Collision_Unit[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Width[index]        = udg_CMS_Param_Collision_Width[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Width_Inc[index]    = udg_CMS_Param_Collision_Width_Inc[udg_CMS_Amount]
        set udg_CMS_Param_Distance[index]               = udg_CMS_Param_Distance[udg_CMS_Amount]
        set udg_CMS_Param_Duration[index]               = udg_CMS_Param_Duration[udg_CMS_Amount]
        set udg_CMS_Param_Effect[index]                 = udg_CMS_Param_Effect[udg_CMS_Amount]
        set udg_CMS_Param_Gravity[index]                = udg_CMS_Param_Gravity[udg_CMS_Amount]
        set udg_CMS_Param_Index[index]                  = udg_CMS_Param_Index[udg_CMS_Amount]
        set udg_CMS_Param_IsAUnit[index]                = udg_CMS_Param_IsAUnit[udg_CMS_Amount]
        set udg_CMS_Param_IsDestroyed[index]            = udg_CMS_Param_IsDestroyed[udg_CMS_Amount]
        set udg_CMS_Param_IsFlying[index]               = udg_CMS_Param_IsFlying[udg_CMS_Amount]
        set udg_CMS_Param_IsHoming[index]               = udg_CMS_Param_IsHoming[udg_CMS_Amount]
        set udg_CMS_Param_MaxDistance[index]            = udg_CMS_Param_MaxDistance[udg_CMS_Amount]
        set udg_CMS_Param_MaxDuration[index]            = udg_CMS_Param_MaxDuration[udg_CMS_Amount]
        set udg_CMS_Param_Missile[index]                = udg_CMS_Param_Missile[udg_CMS_Amount]
        set udg_CMS_Param_OriginalHeight[index]         = udg_CMS_Param_OriginalHeight[udg_CMS_Amount]
        set udg_CMS_Param_Source[index]                 = udg_CMS_Param_Source[udg_CMS_Amount]
        set udg_CMS_Param_Subtype[index]                = udg_CMS_Param_Subtype[udg_CMS_Amount]
        set udg_CMS_Param_Target_Location[index]        = udg_CMS_Param_Target_Location[udg_CMS_Amount]
        set udg_CMS_Param_Target_Type[index]            = udg_CMS_Param_Target_Type[udg_CMS_Amount]
        set udg_CMS_Param_Target_Unit[index]            = udg_CMS_Param_Target_Unit[udg_CMS_Amount]
        set udg_CMS_Param_TurnRate[index]               = udg_CMS_Param_TurnRate[udg_CMS_Amount]
        set udg_CMS_Param_Type[index]                   = udg_CMS_Param_Type[udg_CMS_Amount]
        set udg_CMS_Param_WalkingHeight[index]          = udg_CMS_Param_WalkingHeight[udg_CMS_Amount]
        set udg_CMS_Param_A_X[index]                    = udg_CMS_Param_A_X[udg_CMS_Amount]
        set udg_CMS_Param_A_Y[index]                    = udg_CMS_Param_A_Y[udg_CMS_Amount]
        set udg_CMS_Param_A_Z[index]                    = udg_CMS_Param_A_Z[udg_CMS_Amount]
        set udg_CMS_Param_V_X[index]                    = udg_CMS_Param_V_X[udg_CMS_Amount]
        set udg_CMS_Param_V_Y[index]                    = udg_CMS_Param_V_Y[udg_CMS_Amount]
        set udg_CMS_Param_V_Z[index]                    = udg_CMS_Param_V_Z[udg_CMS_Amount]
        set udg_CMS_Cache_A_H[index]                    = udg_CMS_Cache_A_H[udg_CMS_Amount]
        set udg_CMS_Cache_V_H[index]                    = udg_CMS_Cache_V_H[udg_CMS_Amount]
        set udg_CMS_Cache_Acceleration[index]           = udg_CMS_Cache_Acceleration[udg_CMS_Amount]
        set udg_CMS_Cache_Angle[index]                  = udg_CMS_Cache_Angle[udg_CMS_Amount]
        set udg_CMS_Cache_Pitch[index]                  = udg_CMS_Cache_Pitch[udg_CMS_Amount]
        set udg_CMS_Cache_Speed[index]                  = udg_CMS_Cache_Speed[udg_CMS_Amount]
        set udg_CMS_Cache_V_H_Index[index]              = udg_CMS_Cache_V_H_Index[udg_CMS_Amount]
        set udg_CMS_Cache_Acceleration_Index[index]     = udg_CMS_Cache_Acceleration_Index[udg_CMS_Amount]
        set udg_CMS_Cache_Angle_Index[index]            = udg_CMS_Cache_Angle_Index[udg_CMS_Amount]
        set udg_CMS_Cache_Pitch_Index[index]            = udg_CMS_Cache_Pitch_Index[udg_CMS_Amount]
        set udg_CMS_Cache_Speed_Index[index]            = udg_CMS_Cache_Speed_Index[udg_CMS_Amount]
        set udg_CMS_Param_Collision_Cooldown[udg_CMS_Amount]    = 0.
        set udg_CMS_Param_Collision_Dest[udg_CMS_Amount]        = false
        set udg_CMS_Param_Collision_Height[udg_CMS_Amount]      = 0.
        set udg_CMS_Param_Collision_Height_Inc[udg_CMS_Amount]  = 0.
        set udg_CMS_Param_Collision_Item[udg_CMS_Amount]        = false
        set udg_CMS_Param_Collision_Type[udg_CMS_Amount]        = 0
        set udg_CMS_Param_Collision_Unit[udg_CMS_Amount]        = false
        set udg_CMS_Param_Collision_Width[udg_CMS_Amount]       = 0.
        set udg_CMS_Param_Collision_Width_Inc[udg_CMS_Amount]   = 0.
        set udg_CMS_Param_Distance[udg_CMS_Amount]              = 0.
        set udg_CMS_Param_Duration[udg_CMS_Amount]              = 0.
        set udg_CMS_Param_Effect[udg_CMS_Amount]                = null
        set udg_CMS_Param_Gravity[udg_CMS_Amount]               = 0.
        set udg_CMS_Param_Index[udg_CMS_Amount]                 = 0
        set udg_CMS_Param_IsAUnit[udg_CMS_Amount]               = false
        set udg_CMS_Param_IsDestroyed[udg_CMS_Amount]           = false
        set udg_CMS_Param_IsFlying[udg_CMS_Amount]              = false
        set udg_CMS_Param_IsHoming[udg_CMS_Amount]              = false
        set udg_CMS_Param_MaxDistance[udg_CMS_Amount]           = 0.
        set udg_CMS_Param_MaxDuration[udg_CMS_Amount]           = 0.
        set udg_CMS_Param_Missile[udg_CMS_Amount]               = null
        set udg_CMS_Param_OriginalHeight[udg_CMS_Amount]        = 0.
        set udg_CMS_Param_Source[udg_CMS_Amount]                = null
        set udg_CMS_Param_Subtype[udg_CMS_Amount]               = 0
        set udg_CMS_Param_Target_Location[udg_CMS_Amount]       = null
        set udg_CMS_Param_Target_Type[udg_CMS_Amount]           = 0
        set udg_CMS_Param_Target_Unit[udg_CMS_Amount]           = null
        set udg_CMS_Param_TurnRate[udg_CMS_Amount]              = 0.
        set udg_CMS_Param_Type[udg_CMS_Amount]                  = 0
        set udg_CMS_Param_WalkingHeight[udg_CMS_Amount]         = 0.
        set udg_CMS_Param_A_X[udg_CMS_Amount]                   = 0.
        set udg_CMS_Param_A_Y[udg_CMS_Amount]                   = 0.
        set udg_CMS_Param_A_Z[udg_CMS_Amount]                   = 0.
        set udg_CMS_Param_V_X[udg_CMS_Amount]                   = 0.
        set udg_CMS_Param_V_Y[udg_CMS_Amount]                   = 0.
        set udg_CMS_Param_V_Z[udg_CMS_Amount]                   = 0.
        set udg_CMS_Cache_A_H[udg_CMS_Amount]                   = 0.
        set udg_CMS_Cache_V_H[udg_CMS_Amount]                   = 0.
        set udg_CMS_Cache_Acceleration[udg_CMS_Amount]          = 0.
        set udg_CMS_Cache_Angle[udg_CMS_Amount]                 = 0.
        set udg_CMS_Cache_Pitch[udg_CMS_Amount]                 = 0.
        set udg_CMS_Cache_Speed[udg_CMS_Amount]                 = 0.
        set udg_CMS_Cache_V_H_Index[udg_CMS_Amount]             = 0
        set udg_CMS_Cache_Acceleration_Index[udg_CMS_Amount]    = 0
        set udg_CMS_Cache_Angle_Index[udg_CMS_Amount]           = 0
        set udg_CMS_Cache_Pitch_Index[udg_CMS_Amount]           = 0
        set udg_CMS_Cache_Speed_Index[udg_CMS_Amount]           = 0
        set udg_CMS_Amount = udg_CMS_Amount -1
       
        if not isAUnit then
            call UnitApplyTimedLife(missile, 0, 0.01)
        else
            call UnitRemoveAbility(missile, udg_CMS_ABILITY_GHOST)
            call SetUnitPathing(missile, true)
            call SetUnitPosition(missile, GetUnitX(missile), GetUnitY(missile))
            call SetUnitFlyHeight(missile, originalHeight, 999)
        endif
       
        if udg_CMS_LoopIndex > -1 then
            set udg_CMS_LoopIndex = udg_CMS_LoopIndex -1
        endif
        if udg_CMS_Amount == 0 then
            call PauseTimer(udg_CMS_TIMER)
        endif
       
        set missile = null
        return true
    endfunction
   
    //! runtextmacro CMS_COLLISION_FUNCTIONS()
   
    function CMS_Interval_X takes nothing returns boolean
        local rect r
        local unit FoG
        local real z
        local real angle
        local real x
        local real y
        local integer iteration = 0
       
        loop
            if udg_CMS_LoopIndex > udg_CMS_Amount then
                return true
            endif
            exitwhen iteration > udg_CMS_LOOP_MAX
            set iteration = iteration +1
           
            //Move missile.
            set udg_CMS_X_OLD = GetUnitX(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
            set udg_CMS_Y_OLD = GetUnitY(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
            set udg_CMS_X = udg_CMS_X_OLD + udg_CMS_Param_V_X[udg_CMS_LoopIndex]
            set udg_CMS_Y = udg_CMS_Y_OLD + udg_CMS_Param_V_Y[udg_CMS_LoopIndex]
            call SetUnitX(udg_CMS_Param_Missile[udg_CMS_LoopIndex], udg_CMS_X)
            call SetUnitY(udg_CMS_Param_Missile[udg_CMS_LoopIndex], udg_CMS_Y)
           
            set z = GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex]) + udg_CMS_Param_V_Z[udg_CMS_LoopIndex] - (GetCoordinateZ(udg_CMS_X, udg_CMS_Y) - GetCoordinateZ(udg_CMS_X_OLD, udg_CMS_Y_OLD))
            if z <= udg_CMS_Param_WalkingHeight[udg_CMS_LoopIndex] then
                if udg_CMS_Param_IsFlying[udg_CMS_LoopIndex] or z < -20. then
                    set udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] = true
                    call SetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex], z, 0)
                else
                    set z = udg_CMS_Param_WalkingHeight[udg_CMS_LoopIndex]
                    set udg_CMS_Param_V_Z[udg_CMS_LoopIndex] = 0.
                    call SetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex], z, 0)
                endif
            else
                call SetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex], z, 0)
                //Add gravity effect.
                if udg_CMS_Param_Gravity[udg_CMS_LoopIndex] != 0. then
                    set udg_CMS_Param_V_Z[udg_CMS_LoopIndex] = udg_CMS_Param_V_Z[udg_CMS_LoopIndex] + udg_CMS_Param_Gravity[udg_CMS_LoopIndex]
                    call CMS_SetPitch(udg_CMS_LoopIndex, CMS_GetPitch(udg_CMS_LoopIndex))
                endif
            endif
           
            //Check for the missile's limits.
            set udg_CMS_Param_Distance[udg_CMS_LoopIndex] = udg_CMS_Param_Distance[udg_CMS_LoopIndex] + CMS_GetSpeed(udg_CMS_LoopIndex)
            if udg_CMS_Param_MaxDistance[udg_CMS_LoopIndex] > 0. and udg_CMS_Param_Distance[udg_CMS_LoopIndex] >= udg_CMS_Param_MaxDistance[udg_CMS_LoopIndex] then
                set udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] = true
            endif
            set udg_CMS_Param_Duration[udg_CMS_LoopIndex] = udg_CMS_Param_Duration[udg_CMS_LoopIndex] + udg_CMS_INTERVAL
            if udg_CMS_Param_MaxDuration[udg_CMS_LoopIndex] > 0. and udg_CMS_Param_Duration[udg_CMS_LoopIndex] >= udg_CMS_Param_MaxDuration[udg_CMS_LoopIndex] then
                set udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] = true
            endif
           
            //Check collision.
            if udg_CMS_Param_Collision_Type[udg_CMS_LoopIndex] == udg_CMS_COLLISIONTYPE_SIMPLE then
               
                call SetRect(RECT, udg_CMS_X - udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex], udg_CMS_Y - udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex], udg_CMS_X + udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex], udg_CMS_Y + udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex])
                //Collision with units.
                if udg_CMS_Param_Collision_Unit[udg_CMS_LoopIndex] then
                    call GroupEnumUnitsInRect(udg_CMS_GROUP, RECT, null)
                    loop
                        set FoG = FirstOfGroup(udg_CMS_GROUP)
                        exitwhen FoG == null
                        call GroupRemoveUnit(udg_CMS_GROUP, FoG)
                       
                        call CMS_Collide_Unit(FoG)
                    endloop
                endif
                //Collision with destructables.
                if udg_CMS_Param_Collision_Dest[udg_CMS_LoopIndex] then
                    call EnumDestructablesInRect(RECT, null, function CMS_Collide_Dest_Simple)
                endif
                //Collision with items.
                if udg_CMS_Param_Collision_Item[udg_CMS_LoopIndex] then
                    call EnumItemsInRect(RECT, null, function CMS_Collide_Item_Simple)
                endif
               
                //Increase width and height.
                set udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex] = udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex] + udg_CMS_Param_Collision_Width_Inc[udg_CMS_LoopIndex]
                set udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] = udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] + udg_CMS_Param_Collision_Height_Inc[udg_CMS_LoopIndex]
               
            elseif udg_CMS_Param_Collision_Type[udg_CMS_LoopIndex] == udg_CMS_COLLISIONTYPE_CIRCLE then
               
                call SetRect(RECT, udg_CMS_X - udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex], udg_CMS_Y - udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex], udg_CMS_X + udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex], udg_CMS_Y + udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex])
                //Collision with units.
                if udg_CMS_Param_Collision_Unit[udg_CMS_LoopIndex] then
                    call GroupEnumUnitsInRange(udg_CMS_GROUP, udg_CMS_X, udg_CMS_Y, udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex], null)
                    loop
                        set FoG = FirstOfGroup(udg_CMS_GROUP)
                        exitwhen FoG == null
                        call GroupRemoveUnit(udg_CMS_GROUP, FoG)
                       
                        call CMS_Collide_Unit(FoG)
                    endloop
                endif
                //Collision with destructables.
                if udg_CMS_Param_Collision_Dest[udg_CMS_LoopIndex] then
                    call EnumDestructablesInRect(RECT, null, function CMS_Collide_Dest_Circle)
                endif
                //Collision with items.
                if udg_CMS_Param_Collision_Item[udg_CMS_LoopIndex] then
                    call EnumItemsInRect(RECT, null, function CMS_Collide_Item_Circle)
                endif
               
                //Increase width and height.
                set udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex] = udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex] + udg_CMS_Param_Collision_Width_Inc[udg_CMS_LoopIndex]
                set udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] = udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] + udg_CMS_Param_Collision_Height_Inc[udg_CMS_LoopIndex]
               
            elseif udg_CMS_Param_Collision_Type[udg_CMS_LoopIndex] == udg_CMS_COLLISIONTYPE_RECTANGLE then
               
                //Collision with units.
                set udg_CMS_RA = GetUnitFacing(udg_CMS_Param_Missile[udg_CMS_LoopIndex])*DEGTORAD
                set udg_CMS_RX = (udg_CMS_X+udg_CMS_X_OLD) /2
                set udg_CMS_RY = (udg_CMS_Y+udg_CMS_Y_OLD) /2
                set udg_CMS_RW = udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex]*2 + DistanceBetweenCoordinates(udg_CMS_X, udg_CMS_Y, udg_CMS_X_OLD, udg_CMS_Y_OLD)
                set udg_CMS_RH = udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex]*2
                set z = udg_CMS_RW/2
                call SetRect(RECT, udg_CMS_RX-z, udg_CMS_RY-z, udg_CMS_RX+z, udg_CMS_RY+z)
                if udg_CMS_Param_Collision_Unit[udg_CMS_LoopIndex] then
                    call GroupEnumUnitsInRect(udg_CMS_GROUP, RECT, null)
                    loop
                        set FoG = FirstOfGroup(udg_CMS_GROUP)
                        exitwhen FoG == null
                        call GroupRemoveUnit(udg_CMS_GROUP, FoG)
                       
                        if IsPointInAngledRectangleRad(udg_CMS_RX, udg_CMS_RY, udg_CMS_RW, udg_CMS_RH, udg_CMS_RA, GetUnitX(FoG), GetUnitY(FoG)) then
                            call CMS_Collide_Unit(FoG)
                        endif
                    endloop
                endif
                //Collision with destructables.
                if udg_CMS_Param_Collision_Dest[udg_CMS_LoopIndex] then
                    call EnumDestructablesInRect(RECT, null, function CMS_Collide_Dest_Rectangle)
                endif
                //Collision with items.
                if udg_CMS_Param_Collision_Item[udg_CMS_LoopIndex] then
                    call EnumItemsInRect(RECT, null, function CMS_Collide_Item_Rectangle)
                endif
           
            elseif udg_CMS_Param_Collision_Type[udg_CMS_LoopIndex] == udg_CMS_COLLISIONTYPE_POLYGON then
               
                call udg_CMS_Param_Polygon[udg_CMS_LoopIndex].setPosition(udg_CMS_X, udg_CMS_Y)
                call udg_CMS_Param_Polygon[udg_CMS_LoopIndex].setRotationDeg(GetUnitFacing(udg_CMS_Param_Missile[udg_CMS_LoopIndex]))
                call udg_CMS_Param_Polygon[udg_CMS_LoopIndex].getBounds() //Stores the bounds in RECT (BasicFunctions)
                //Collision with units.
                if udg_CMS_Param_Collision_Unit[udg_CMS_LoopIndex] then
                    call GroupEnumUnitsInRect(udg_CMS_GROUP, RECT, null)
                    loop
                        set FoG = FirstOfGroup(udg_CMS_GROUP)
                        exitwhen FoG == null
                        call GroupRemoveUnit(udg_CMS_GROUP,FoG)
                       
                        if udg_CMS_Param_Polygon[udg_CMS_LoopIndex].containsPoint(GetUnitX(FoG), GetUnitY(FoG)) then
                           call CMS_Collide_Unit(FoG)
                        endif
                    endloop
                endif
                //Collision with destructables.
                if udg_CMS_Param_Collision_Dest[udg_CMS_LoopIndex] then
                    call EnumDestructablesInRect(RECT, null, function CMS_Collide_Dest_Polygon)
                endif
                //Collision with items.
                if udg_CMS_Param_Collision_Item[udg_CMS_LoopIndex] then
                    call EnumItemsInRect(RECT, null, function CMS_Collide_Item_Polygon)
                endif
               
            endif
           
            //Update direction if the missile is homing.
            if udg_CMS_Param_IsHoming[udg_CMS_LoopIndex] then
                if udg_CMS_Param_Target_Type[udg_CMS_LoopIndex] == udg_CMS_TARGETTYPE_UNIT then
                   
                    //Update angle.
                    set x = GetUnitX(udg_CMS_Param_Target_Unit[udg_CMS_LoopIndex])
                    set y = GetUnitY(udg_CMS_Param_Target_Unit[udg_CMS_LoopIndex])
                    set angle = DenormalizeAngleDeg(AngleBetweenCoordinatesDeg(udg_CMS_X, udg_CMS_Y, x, y) - CMS_GetAngleDeg(udg_CMS_LoopIndex))
                   
                    if angle > udg_CMS_Param_TurnRate[udg_CMS_LoopIndex] then
                        set angle = udg_CMS_Param_TurnRate[udg_CMS_LoopIndex]
                    elseif angle < -udg_CMS_Param_TurnRate[udg_CMS_LoopIndex] then
                        set angle = -udg_CMS_Param_TurnRate[udg_CMS_LoopIndex]
                    endif
                   
                    call CMS_SetAngleDeg(udg_CMS_LoopIndex, CMS_GetAngleDeg(udg_CMS_LoopIndex) + angle)
                   
                    //Update pitch.
                    if udg_CMS_Param_IsFlying[udg_CMS_LoopIndex] then
                       
                        set angle = AngleBetweenCoordinatesDeg(0, GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y), DistanceBetweenCoordinates(x, y, udg_CMS_X, udg_CMS_Y), GetUnitFlyHeight(udg_CMS_Param_Target_Unit[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y)) - CMS_GetPitchDeg(udg_CMS_LoopIndex)
                       
                        if angle > udg_CMS_Param_TurnRate[udg_CMS_LoopIndex] then
                            set angle = udg_CMS_Param_TurnRate[udg_CMS_LoopIndex]
                        elseif angle < -udg_CMS_Param_TurnRate[udg_CMS_LoopIndex] then
                            set angle = -udg_CMS_Param_TurnRate[udg_CMS_LoopIndex]
                        endif
                       
                        call CMS_SetPitchDeg(udg_CMS_LoopIndex, CMS_GetPitchDeg(udg_CMS_LoopIndex) + angle)
                    endif
                   
                elseif udg_CMS_Param_Target_Type[udg_CMS_LoopIndex] == udg_CMS_TARGETTYPE_LOCATION then
                   
                    //Update angle.
                    set x = GetLocationX(udg_CMS_Param_Target_Location[udg_CMS_LoopIndex])
                    set y = GetLocationY(udg_CMS_Param_Target_Location[udg_CMS_LoopIndex])
                    set angle = DenormalizeAngleDeg(AngleBetweenCoordinatesDeg(udg_CMS_X, udg_CMS_Y, x, y) - CMS_GetAngleDeg(udg_CMS_LoopIndex))
                   
                    if angle > udg_CMS_Param_TurnRate[udg_CMS_LoopIndex] then
                        set angle = udg_CMS_Param_TurnRate[udg_CMS_LoopIndex]
                    elseif angle < -udg_CMS_Param_TurnRate[udg_CMS_LoopIndex] then
                        set angle = -udg_CMS_Param_TurnRate[udg_CMS_LoopIndex]
                    endif
                    call CMS_SetAngleDeg(udg_CMS_LoopIndex, CMS_GetAngleDeg(udg_CMS_LoopIndex) + angle)
                   
                    //Update pitch.
                    if udg_CMS_Param_IsFlying[udg_CMS_LoopIndex] then
                       
                        set angle = AngleBetweenCoordinatesDeg(0, GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y), DistanceBetweenCoordinates(x, y, udg_CMS_X, udg_CMS_Y), GetCoordinateZ(x, y)) - CMS_GetPitchDeg(udg_CMS_LoopIndex)
                       
                        if angle > udg_CMS_Param_TurnRate[udg_CMS_LoopIndex] then
                            set angle = udg_CMS_Param_TurnRate[udg_CMS_LoopIndex]
                        elseif angle < -udg_CMS_Param_TurnRate[udg_CMS_LoopIndex] then
                            set angle = -udg_CMS_Param_TurnRate[udg_CMS_LoopIndex]
                        endif
                       
                        call CMS_SetPitchDeg(udg_CMS_LoopIndex, CMS_GetPitchDeg(udg_CMS_LoopIndex) + angle)
                       
                    endif
                   
                endif
               
            endif
           
            set udg_CMS_Param_V_X[udg_CMS_LoopIndex] = udg_CMS_Param_V_X[udg_CMS_LoopIndex] + udg_CMS_Param_A_X[udg_CMS_LoopIndex]
            set udg_CMS_Param_V_Y[udg_CMS_LoopIndex] = udg_CMS_Param_V_Y[udg_CMS_LoopIndex] + udg_CMS_Param_A_Y[udg_CMS_LoopIndex]
            set udg_CMS_Param_V_Z[udg_CMS_LoopIndex] = udg_CMS_Param_V_Z[udg_CMS_LoopIndex] + udg_CMS_Param_A_Z[udg_CMS_LoopIndex]
           
            if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
                call CMS_DestroyMissile(udg_CMS_LoopIndex)
            endif
           
            set udg_CMS_LoopIndex = udg_CMS_LoopIndex +1
        endloop
        return false
    endfunction
    //This is the interval of the timer.
    function CMS_Interval takes nothing returns nothing
        set udg_CMS_LoopIndex = 1
        set udg_CMS_IntervalCount = udg_CMS_IntervalCount +1
        set udg_CMS_GameTime = GetElapsedGameTime()
       
        loop
            exitwhen TriggerEvaluate(udg_CMS_TRIGGER)
        endloop
       
        set udg_CMS_LoopIndex = -1
    endfunction
   
    function CMS_CreateMissile takes unit source, integer typeId, unit whichMissile, boolean importDefaultValues returns integer
        local integer id
        local timer t
        local location startingLocation = udg_CMS_StartingLocation
        local integer otherIndex = 0
        local integer sourceId
        local integer missileId
       
        set udg_CMS_Amount = udg_CMS_Amount +1
        if importDefaultValues then
            call CMS_SetDefaultVariables(udg_CMS_Amount)
        endif
        set udg_CMS_Param_Missile[udg_CMS_Amount] = whichMissile
        set udg_CMS_Param_Source[udg_CMS_Amount] = source
        set udg_CMS_Param_Type[udg_CMS_Amount] = typeId
       
        if udg_CMS_Param_Missile[udg_CMS_Amount] == null then
            if udg_CMS_StartingLocust then
                //Create a new missile dummy and give it locust.
                set udg_CMS_Param_Missile[udg_CMS_Amount] = CreateUnit(GetOwningPlayer(udg_CMS_Param_Source[udg_CMS_Amount]), udg_CMS_MISSILE_UNITTYPE, GetLocationX(startingLocation), GetLocationY(startingLocation), udg_CMS_StartingAngle)
                call UnitAddAbility(udg_CMS_Param_Missile[udg_CMS_Amount], 'Aloc')
                call UnitMakeAbilityPermanent(udg_CMS_Param_Missile[udg_CMS_Amount], true, 'Aloc')
               
            else
                //Create a new dummy unit and transform it into the missile dummy to de-locust the unit.
                set udg_CMS_Param_Missile[udg_CMS_Amount] = CreateUnit(GetOwningPlayer(udg_CMS_Param_Source[udg_CMS_Amount]), udg_CMS_DUMMY_UNITTYPE, GetLocationX(startingLocation), GetLocationY(startingLocation), udg_CMS_StartingAngle)
                call UnitAddAbility(udg_CMS_Param_Missile[udg_CMS_Amount], udg_CMS_ABILITY_MORPH)
                call UnitRemoveAbility(udg_CMS_Param_Missile[udg_CMS_Amount], udg_CMS_ABILITY_MORPH)
               
                //Remove collision.
                call UnitAddAbility(udg_CMS_Param_Missile[udg_CMS_Amount], udg_CMS_ABILITY_GHOST)
                call UnitMakeAbilityPermanent(udg_CMS_Param_Missile[udg_CMS_Amount], true, udg_CMS_ABILITY_GHOST)
                call SetUnitPathing(udg_CMS_Param_Missile[udg_CMS_Amount], false)
            endif
        else
            set otherIndex = CMS_GetIndex(udg_CMS_Param_Missile[udg_CMS_Amount])
           
            //Remove collision.
            call UnitAddAbility(udg_CMS_Param_Missile[udg_CMS_Amount], udg_CMS_ABILITY_GHOST)
            call UnitMakeAbilityPermanent(udg_CMS_Param_Missile[udg_CMS_Amount], true, udg_CMS_ABILITY_GHOST)
            call SetUnitPathing(udg_CMS_Param_Missile[udg_CMS_Amount], false)
        endif
        //Add and remove crow form.
        if UnitAddAbility(udg_CMS_Param_Missile[udg_CMS_Amount], 'Arav') then
            call UnitRemoveAbility(udg_CMS_Param_Missile[udg_CMS_Amount], 'Arav')
        endif
       
        //Make sure that the source is not immediately hit.
        if udg_CMS_StartingSourceFilter then
            call SaveReal(udg_CMS_HASHTABLE, GetHandleId(udg_CMS_Param_Missile[udg_CMS_Amount]), GetHandleId(udg_CMS_Param_Source[udg_CMS_Amount]), GetElapsedGameTime())
        endif
       
        //Save the original height.
        if otherIndex > 0 then
            set udg_CMS_Param_OriginalHeight[udg_CMS_Amount] = GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_Amount])
            call SetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_Amount], udg_CMS_StartingHeight, 999)
        else
            set udg_CMS_Param_OriginalHeight[udg_CMS_Amount] = udg_CMS_Param_OriginalHeight[otherIndex]
            call SetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_Amount], udg_CMS_StartingHeight, 0)
        endif
       
        //Generate speed and direction.
        call CMS_SetSpeedPerSecond(udg_CMS_Amount, udg_CMS_StartingSpeed)
        call CMS_SetAngleDeg(udg_CMS_Amount, udg_CMS_StartingAngle)
        call CMS_SetPitchDeg(udg_CMS_Amount, udg_CMS_StartingPitch)
        call CMS_SetAccelerationPerSecond(udg_CMS_Amount, udg_CMS_StartingAcceleration)
       
        //Start the timer if this is the first missile.
        if udg_CMS_Amount == 1 then
            call TimerStart(udg_CMS_TIMER, udg_CMS_INTERVAL, true, function CMS_Interval)
        endif
       
        //Create the unique index.
        set udg_CMS_Param_Index[udg_CMS_Amount] = CMS_CreateUniqueId()
        set udg_CMS_MissileIndex[GetUnitUserData(udg_CMS_Param_Missile[udg_CMS_Amount])] = udg_CMS_Amount
       
        //Call the event.
        set udg_CMS_Event_Missile_Created = -1
        set udg_CMS_Event_Missile_Created = 0
        set udg_CMS_Event_Missile_Created = -1
        set udg_CMS_Event_Missile_Created = udg_CMS_Param_Type[udg_CMS_Amount]
       
        //Clean leaks.
        call RemoveLocation(startingLocation)
        set startingLocation = null
       
        //Reset variables to standard settings.
        set udg_CMS_StartingAcceleration = 0.
        set udg_CMS_StartingAngle = 0.
        set udg_CMS_StartingHeight = 0.
        set udg_CMS_StartingLocation = null
        set udg_CMS_StartingLocust = true
        set udg_CMS_StartingPitch = 0.
        set udg_CMS_StartingSourceFilter = true
        set udg_CMS_StartingSpeed = 0.
       
        return udg_CMS_Amount
    endfunction
   
    //This function creates a new missile.
    //It uses the missile source and all starting variables.
    function CMS_CreateMissileEx takes nothing returns integer
        local integer id = CMS_CreateMissile(udg_CMS_StartingSource, udg_CMS_StartingType, udg_CMS_StartingMissile, udg_CMS_StartingDefaultValues)
       
        set udg_CMS_StartingSource = null
        set udg_CMS_StartingType = -1
        set udg_CMS_StartingMissile = null
        set udg_CMS_StartingDefaultValues = true
        return id
    endfunction
   
endlibrary


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  This code initializes the standard settings.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//This is the initial function.
function InitTrig_CMS_System takes nothing returns nothing
   
    call TriggerAddCondition(udg_CMS_TRIGGER, Filter(function CMS_Interval_X))
   
    //Initialize the constants.
    set udg_CMS_COLLISIONTYPE_NONE = 0
    set udg_CMS_COLLISIONTYPE_SIMPLE = 1
    set udg_CMS_COLLISIONTYPE_CIRCLE = 2
    set udg_CMS_COLLISIONTYPE_RECTANGLE = 3
    set udg_CMS_COLLISIONTYPE_POLYGON = 4
    set udg_CMS_TARGETTYPE_NONE = 0
    set udg_CMS_TARGETTYPE_UNIT = 1
    set udg_CMS_TARGETTYPE_LOCATION = 2
   
    //Initialize the indexes.
    set udg_CMS_IntervalCount = 1
    set udg_CMS_LoopIndex = -1
    set udg_CMS_Amount = 0
   
    //Initialize standard starting settings.
    set udg_CMS_StartingAcceleration = 0.
    set udg_CMS_StartingAngle = 0.
    set udg_CMS_StartingDefaultValues = true
    set udg_CMS_StartingHeight = 0.
    set udg_CMS_StartingLocation = null
    set udg_CMS_StartingLocust = true
    set udg_CMS_StartingMissile = null
    set udg_CMS_StartingPitch = 0.
    set udg_CMS_StartingSource = null
    set udg_CMS_StartingSourceFilter = true
    set udg_CMS_StartingSpeed = 0.
    set udg_CMS_StartingType = -1
   
    //! runtextmacro CMS_INIT()
   
endfunction

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  CMS System end
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JASS:
//! textmacro CMS_COLLISION_FUNCTIONS
   
    //Units
    function CMS_Collide_Unit takes unit enumUnit returns nothing
        local real difference
        local timer t
        local integer id
        local integer collidedId
        local integer missileId
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return
        endif
       
        set udg_CMS_Collided_Unit = enumUnit
        set collidedId = GetHandleId(udg_CMS_Collided_Unit)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetUnitFlyHeight(udg_CMS_Collided_Unit) + GetCoordinateZ(GetUnitX(udg_CMS_Collided_Unit), GetUnitY(udg_CMS_Collided_Unit))
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_U = -1
        set udg_CMS_Event_Missile_Collided_U = 0
        set udg_CMS_Event_Missile_Collided_U = -1
        set udg_CMS_Event_Missile_Collided_U = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
    endfunction
   
    //SIMPLE
    function CMS_Collide_Dest_Simple takes nothing returns boolean
        local real difference
        local timer t
        local integer id
        local integer collidedId
        local integer missileId
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return false
        endif
       
        set udg_CMS_Collided_Dest = GetEnumDestructable()
        set collidedId = GetHandleId(udg_CMS_Collided_Dest)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return false
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetCoordinateZ(GetDestructableX(udg_CMS_Collided_Dest), GetDestructableY(udg_CMS_Collided_Dest))
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return false
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_D = -1
        set udg_CMS_Event_Missile_Collided_D = 0
        set udg_CMS_Event_Missile_Collided_D = -1
        set udg_CMS_Event_Missile_Collided_D = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
        return false
    endfunction
    function CMS_Collide_Item_Simple takes nothing returns boolean
        local real difference
        local integer collidedId
        local integer missileId
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return false
        endif
       
        set udg_CMS_Collided_Item = GetEnumItem()
        set collidedId = GetHandleId(udg_CMS_Collided_Item)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return false
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetCoordinateZ(GetItemX(udg_CMS_Collided_Item), GetItemY(udg_CMS_Collided_Item))
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return false
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_I = -1
        set udg_CMS_Event_Missile_Collided_I = 0
        set udg_CMS_Event_Missile_Collided_I = -1
        set udg_CMS_Event_Missile_Collided_I = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
        return false
    endfunction
   
    //CIRCLE
    function CMS_Collide_Dest_Circle takes nothing returns boolean
        local real difference
        local timer t
        local integer id
        local integer collidedId
        local integer missileId
        local real x
        local real y
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return false
        endif
       
        set udg_CMS_Collided_Dest = GetEnumDestructable()
        set x = GetDestructableX(udg_CMS_Collided_Dest)
        set y = GetDestructableY(udg_CMS_Collided_Dest)
       
        if DistanceBetweenCoordinates(udg_CMS_X, udg_CMS_Y, x, y) > udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex] then
            return false
        endif
       
        set collidedId = GetHandleId(udg_CMS_Collided_Dest)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return false
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetCoordinateZ(x, y)
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return false
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_D = -1
        set udg_CMS_Event_Missile_Collided_D = 0
        set udg_CMS_Event_Missile_Collided_D = -1
        set udg_CMS_Event_Missile_Collided_D = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
        return false
    endfunction
    function CMS_Collide_Item_Circle takes nothing returns boolean
        local real difference
        local integer collidedId
        local integer missileId
        local real x
        local real y
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return false
        endif
       
        set udg_CMS_Collided_Item = GetEnumItem()
        set x = GetItemX(udg_CMS_Collided_Item)
        set y = GetItemY(udg_CMS_Collided_Item)
       
        if DistanceBetweenCoordinates(udg_CMS_X, udg_CMS_Y, x, y) > udg_CMS_Param_Collision_Width[udg_CMS_LoopIndex] then
            return false
        endif
       
        set collidedId = GetHandleId(udg_CMS_Collided_Item)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return false
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetCoordinateZ(x, y)
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return false
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_I = -1
        set udg_CMS_Event_Missile_Collided_I = 0
        set udg_CMS_Event_Missile_Collided_I = -1
        set udg_CMS_Event_Missile_Collided_I = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
        return false
    endfunction
   
    //RECTANGLE
    function CMS_Collide_Dest_Rectangle takes nothing returns boolean
        local real difference
        local timer t
        local integer id
        local integer collidedId
        local integer missileId
        local real x
        local real y
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return false
        endif
       
        set udg_CMS_Collided_Dest = GetEnumDestructable()
        set x = GetDestructableX(udg_CMS_Collided_Dest)
        set y = GetDestructableY(udg_CMS_Collided_Dest)
       
        if not IsPointInAngledRectangleRad(udg_CMS_RX, udg_CMS_RY, udg_CMS_RW, udg_CMS_RH, udg_CMS_RA, x, y) then
            return false
        endif
       
        set collidedId = GetHandleId(udg_CMS_Collided_Dest)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return false
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetCoordinateZ(x, y)
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return false
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_D = -1
        set udg_CMS_Event_Missile_Collided_D = 0
        set udg_CMS_Event_Missile_Collided_D = -1
        set udg_CMS_Event_Missile_Collided_D = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
        return false
    endfunction
    function CMS_Collide_Item_Rectangle takes nothing returns boolean
        local real difference
        local integer collidedId
        local integer missileId
        local real x
        local real y
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return false
        endif
       
        set udg_CMS_Collided_Item = GetEnumItem()
        set x = GetItemX(udg_CMS_Collided_Item)
        set y = GetItemY(udg_CMS_Collided_Item)
       
        if not IsPointInAngledRectangleRad(udg_CMS_RX, udg_CMS_RY, udg_CMS_RW, udg_CMS_RH, udg_CMS_RA, x, y) then
            return false
        endif
       
        set collidedId = GetHandleId(udg_CMS_Collided_Item)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return false
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetCoordinateZ(x, y)
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return false
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_I = -1
        set udg_CMS_Event_Missile_Collided_I = 0
        set udg_CMS_Event_Missile_Collided_I = -1
        set udg_CMS_Event_Missile_Collided_I = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
        return false
    endfunction
   
    //POLYGON
    function CMS_Collide_Dest_Polygon takes nothing returns boolean
        local real difference
        local timer t
        local integer id
        local integer collidedId
        local integer missileId
        local real x
        local real y
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return false
        endif
       
        set udg_CMS_Collided_Dest = GetEnumDestructable()
        set x = GetDestructableX(udg_CMS_Collided_Dest)
        set y = GetDestructableY(udg_CMS_Collided_Dest)
       
        if not udg_CMS_Param_Polygon[udg_CMS_LoopIndex].containsPoint(x, y) then
            return false
        endif
       
        set collidedId = GetHandleId(udg_CMS_Collided_Dest)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return false
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetCoordinateZ(x, y)
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return false
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_D = -1
        set udg_CMS_Event_Missile_Collided_D = 0
        set udg_CMS_Event_Missile_Collided_D = -1
        set udg_CMS_Event_Missile_Collided_D = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
        return false
    endfunction
    function CMS_Collide_Item_Polygon takes nothing returns boolean
        local real difference
        local integer collidedId
        local integer missileId
        local real x
        local real y
       
        //If the missile is destroyed then don't do anything.
        if udg_CMS_Param_IsDestroyed[udg_CMS_LoopIndex] then
            return false
        endif
       
        set udg_CMS_Collided_Item = GetEnumItem()
        set x = GetItemX(udg_CMS_Collided_Item)
        set y = GetItemY(udg_CMS_Collided_Item)
       
        if not udg_CMS_Param_Polygon[udg_CMS_LoopIndex].containsPoint(x, y) then
            return false
        endif
       
        set collidedId = GetHandleId(udg_CMS_Collided_Item)
        set missileId = GetHandleId(udg_CMS_Param_Missile[udg_CMS_LoopIndex])
        //If the target is not targetable by this missile then don't do anything.
        if LoadReal(udg_CMS_HASHTABLE, missileId, collidedId) > udg_CMS_GameTime - udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] then
            return false
        endif
       
        //Check if the target collides in height.
        if udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] > 0 then
            set difference = GetCoordinateZ(x, y)
            set difference = difference - (GetUnitFlyHeight(udg_CMS_Param_Missile[udg_CMS_LoopIndex])+GetCoordinateZ(udg_CMS_X, udg_CMS_Y))
            if difference > udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] or difference < -udg_CMS_Param_Collision_Height[udg_CMS_LoopIndex] then
                return false
            endif
        endif
       
        //Call the event.
        set udg_CMS_Event_Missile_Collided_I = -1
        set udg_CMS_Event_Missile_Collided_I = 0
        set udg_CMS_Event_Missile_Collided_I = -1
        set udg_CMS_Event_Missile_Collided_I = udg_CMS_Param_Type[udg_CMS_LoopIndex]
       
        //The unit is a valid target.
        if udg_CMS_ValidTarget then
            set udg_CMS_ValidTarget = false
           
            //Make the unit untargetable for a set amount of time.
            if udg_CMS_Param_Collision_Cooldown[udg_CMS_LoopIndex] > udg_CMS_INTERVAL then
                call SaveReal(udg_CMS_HASHTABLE, missileId, collidedId, udg_CMS_GameTime)
            endif
        endif
        return false
    endfunction
   
//! endtextmacro
  • CMS Configuration
    • Events
    • Conditions
    • Actions
      • Custom script: //! textmacro CMS_INIT
      • Set CMS_ABILITY_GHOST = CMS ABILITY GHOST
      • Set CMS_ABILITY_MORPH = CMS ABILITY MORPH
      • Set CMS_DUMMY_UNITTYPE = DUMMY
      • Set CMS_MISSILE_UNITTYPE = MISSILE
      • Custom script: //! endtextmacro
  • Firebolt Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Firebolt (2)
    • Actions
      • Set TempLocation = (Position of (Target unit of ability being cast))
      • Set TempUnit = (Triggering unit)
      • -------- - --------
      • Set CMS_StartingLocation = (Position of TempUnit)
      • Set CMS_StartingAngle = (Angle from CMS_StartingLocation to TempLocation)
      • Set CMS_StartingHeight = 50.00
      • Set CMS_StartingSource = TempUnit
      • Set CMS_StartingSpeed = 700.00
      • Set CMS_StartingAcceleration = 200.00
      • Set CMS_StartingType = 1
      • Custom script: set udg_TempInteger = CMS_CreateMissileEx()
      • -------- - --------
      • Set CMS_Param_WalkingHeight[TempInteger] = 55.00
      • Set CMS_Param_Gravity[TempInteger] = (-45.00 x 0.03)
      • Set CMS_Param_MaxDistance[TempInteger] = 1700.00
      • Set CMS_Param_Collision_Type[TempInteger] = CMS_COLLISIONTYPE_CIRCLE
      • Set CMS_Param_Collision_Width[TempInteger] = 100.00
      • Special Effect - Create a special effect attached to the origin of CMS_Param_Missile[TempInteger] using Abilities\Weapons\FireBallMissile\FireBallMissile.mdl
      • Set CMS_Param_Effect[TempInteger] = (Last created special effect)
      • -------- - --------
      • Custom script: call RemoveLocation(udg_TempLocation)
  • Firebolt Collide
    • Events
      • Game - CMS_Event_Missile_Collided_U becomes Equal to 0.00
    • Conditions
      • CMS_Param_Type[CMS_LoopIndex] Equal to 1
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (CMS_Collided_Unit is A structure) Equal to False
          • (CMS_Collided_Unit belongs to an enemy of (Owner of CMS_Param_Source[CMS_LoopIndex])) Equal to True
          • (CMS_Collided_Unit is dead) Equal to False
        • Then - Actions
          • Set CMS_Param_IsDestroyed[CMS_LoopIndex] = True
          • Set CMS_ValidTarget = True
          • Unit - Cause CMS_Param_Source[CMS_LoopIndex] to damage CMS_Collided_Unit, dealing 300.00 damage of attack type Spells and damage type Magic
          • -------- - --------
          • -------- apply knockback --------
          • -------- - --------
          • Set CMS_StartingSource = CMS_Param_Source[CMS_LoopIndex]
          • Set CMS_StartingMissile = CMS_Collided_Unit
          • Set CMS_StartingLocation = (Position of CMS_Collided_Unit)
          • Set TempLocation = (Position of CMS_Param_Missile[CMS_LoopIndex])
          • Set CMS_StartingAngle = (Angle from TempLocation to CMS_StartingLocation)
          • Custom script: call RemoveLocation(udg_TempLocation)
          • Set CMS_StartingHeight = 0.00
          • Set CMS_StartingSource = TempUnit
          • Set CMS_StartingSpeed = 800.00
          • Set CMS_StartingAcceleration = -1600.00
          • Set CMS_StartingType = 2
          • -------- - --------
          • Custom script: set udg_TempInteger = CMS_CreateMissileEx()
          • -------- - --------
          • Set CMS_Param_IsAUnit[TempInteger] = True
          • Set CMS_Param_MaxDuration[TempInteger] = 0.40
        • Else - Actions
Changelog:
- 1.4 - Added additional features (detection, optimized events, acceleration).
- 1.3 - Redesigned data and calculation structure.
- 1.2 - Rewritten the system.
- 1.1 - Fixed Collision bugs.
- 1.0 - Official releas on te Hive

Feel free to post any feedback or suggestions.

Keywords:
Wietlol, CMS, Missile, Projectile, Knockback, Knockup, Dash, Jump, Leap, System, Firebolt, Advanced, OP, GUI.
Contents

Just another Warcraft III map (Map)

Reviews
29.02.2016 BPower: Very well written. Very read-able as expected. The homing missile behaviour doesn't work for me in the uploaded test map. I think it has to to with radians to degree conversion. Can you check that? 21:56, 16th Jun 2015...
Level 7
Joined
Aug 11, 2010
Messages
269
Because the API is incomplete, the code is far from optimal and it is a pain to read/edit.

I actually find it easier to modify than the 3D bash system, which is is the system everyone seems to recommend when recommending a missile system. If you wouldn't recommend this system, what would you recommend? Because honestly; the feature set of this one has forever won my heart, but I'm willing to try out other systems.
 
Level 8
Joined
Aug 5, 2014
Messages
192
It is possible to change size of missile? And its possible to add more than one effect on the missile?

I've edited the code, to allow me to do it, but were there any built-in parameters to to this?
 
Last edited:
Top