1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Fill your cup and take your pick among the maps best suited for this year's Hive Cup. The 6th Melee Mapping Contest Poll is up!
    Dismiss Notice
  3. Shoot to thrill, play to kill. Sate your hunger with the 33rd Modeling Contest!
    Dismiss Notice
  4. Do you hear boss music? It's the 17th Mini Mapping Contest!
    Dismiss Notice
  5. 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.

[GUI-Friendly] Custom Missile System 1.4

Submitted by Wietlol
This bundle is marked as substandard. It may contain bugs, not perform optimally or otherwise be in violation of the submission rules.
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:
Code (vJASS):
//  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.


System
Code (vJASS):
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  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
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Code (vJASS):

//! 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


Example
  • 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
Moderator
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...
  1. 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
    BPower: Missile systems are very complex and hard to review.
    Here is a start for a TO-DO-LIST with mandatory
    issues to be fixed or discussed.
    I will continue my review upon your reaction to my review.

    PS: I'm a fan of missile systems

    14:27, 27th Apr 2015
    IcemanBo: https://www.hiveworkshop.com/posts/2679209/
     
  2. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,638
    Resources:
    3
    Spells:
    3
    Resources:
    3
    Last edited: Dec 15, 2015
  3. JC Helas

    JC Helas

    Joined:
    Oct 19, 2014
    Messages:
    126
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    Wiet where is the global you said, never find, I confuse where, i need it because unit group array 2,3,4,5,6.... still not work. please
     
  4. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,638
    Resources:
    3
    Spells:
    3
    Resources:
    3
    ?

    can you explain that in english?
    and what global?
     
  5. GIMLI_2

    GIMLI_2

    Joined:
    Mar 21, 2011
    Messages:
    1,430
    Resources:
    2
    Maps:
    2
    Resources:
    2
    this looks really promising, i will try it out. what about adding a string variable where you can insert animation tags that are played while flying?
     
  6. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,638
    Resources:
    3
    Spells:
    3
    Resources:
    3
    Well... I tried to do it but it seems that special effects are not using animations other than stand and death.

    Not really much I can do.
     
  7. GIMLI_2

    GIMLI_2

    Joined:
    Mar 21, 2011
    Messages:
    1,430
    Resources:
    2
    Maps:
    2
    Resources:
    2
    well you mentioned that this can be used as a leap/dash system too, so i think its useful
     
  8. GhostHunter123

    GhostHunter123

    Joined:
    Oct 17, 2012
    Messages:
    479
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Wietlol, can you provide an example for dragging a unit?
     
  9. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,638
    Resources:
    3
    Spells:
    3
    Resources:
    3
    dragging a unit?
    like in a missile is taking a unit with it when it collides with it?
     
  10. GhostHunter123

    GhostHunter123

    Joined:
    Oct 17, 2012
    Messages:
    479
    Resources:
    1
    Spells:
    1
    Resources:
    1
    That is correct.
     
  11. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,638
    Resources:
    3
    Spells:
    3
    Resources:
    3
    Yea I can do that but it will be next monday/thuesday if not later.
     
  12. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,638
    Resources:
    3
    Spells:
    3
    Resources:
    3
    Update 1.1

    I just released version 1.1

    Here are the changes:
    • Fixed map bounds check.
    • Added Ghost ability.
    • Added CMS_Param_Remove_Collision parameter.
     
  13. EyeCandy

    EyeCandy

    Joined:
    May 13, 2012
    Messages:
    64
    Resources:
    0
    Resources:
    0
    Does this provide an option for a missile to home in on target? Also does it give option to specify either location target or unit and if unit, home onto unit. For Dota players, you will know Spectre's Spectral Dagger is configured like what I just said.
     
  14. Dat-C3

    Dat-C3

    Joined:
    Mar 15, 2012
    Messages:
    2,530
    Resources:
    10
    Models:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    1
    Resources:
    10
  15. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,638
    Resources:
    3
    Spells:
    3
    Resources:
    3
    Yes it does:
    By default the missile doesn't chase a target.
    You can set CMS_Param_Target_Type to either CMS_TT_Location or CMS_TT_Unit and specify the CMS_Param_Target_Location or CMS_Param_Target_Unit to make the target.
    (Homing towards a location can help in making horizontal arcing or using it to change the location during the flight... or make a missile circle around a location or whatever you want.

    You also have to set the turn rate to a value.
    The turn rate is the turn speed of the missile.
    Some games use the turn as instant (the missile would turn instantly to the target) which is a turn rate of 180 in this system.
    A turn rate of 0 doesn't turn the missile at all.

    Same here, as soon as I have time and will... Even though it doesn't take that much time :)
     
  16. jonbon29

    jonbon29

    Joined:
    Aug 19, 2014
    Messages:
    1,098
    Resources:
    0
    Resources:
    0
    Now that you mention what this system does, I have some spells that I wanted create. Looks good to use, I'll use this system as soon as its approved.
     
  17. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,525
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    The code seems a bit too laborious for a missle system. I will give some time before I read it in detail.
     
  18. btdonald

    btdonald

    Joined:
    Dec 3, 2011
    Messages:
    346
    Resources:
    8
    Maps:
    1
    Spells:
    7
    Resources:
    8
    Your code seems ineffecient :eek:
     
  19. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,525
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    • Please add some descriptive comments in config.
      As user I want to know why I need things like "Stun_Buff" in a missle system.
    • Sometimes it's worthy to write more letters to gain description for names.
      Just for example in this code I would not use something like
      CMS_TT_Unit
      . "TT" does not say anything to the reader.
      But most other names seems good.
      Code (vJASS):
      /*
      - CMS_Param_Force_Destroy           - boolean
      This boolean tells if the destruction of a missile must be done at all cost.
      It can also be used as an additional Destroy variable.

      ^What's the difference to using the destroy trigger? It has influence on it?
      Code (vJASS):

      //      - CMS_Player                        - player
      //          This variable is used as a tempvariable when deseleting missiles.
      //          
      //      - CMS_OldLocation                   - point (location in JASS)
      //          This variable is used as a tempvariable when checking for collision.
      //          
      //      - CMS_NewLocation                   - point (location in JASS)
      //          This variable is used as a tempvariable when checking for collision.

      ^In case user doesn't need to use them, temp variables should not be explained.
    • Code (vJASS):

      //      - CMS_Param_Lose_Control            - boolean
      //          This tells if the missile can still move and use abilities etc while it is flying.

      ^I'm not sure a real missle should be able walk around and cast abilities.
    • The maybe only function you really need a location is the GetLocationZ function.
      You can use one global location and work with the
      MoveLocation
      native instead of creating/destroying local locations.
    • Should
      udg_CMS_Param_Missile
      really be changeable? What happens if user changes the
      udg_CMS_Param_Missile
      and then Get/Save trigger gets fired? In my mind it would bug.
    • function CMS_Deselect_Missiles
      should not run when there is no missle.
      Also it could use global temporary unit group instead of creating/destroying local groups.
    • For de-select missles. Why you check for UnitTypeId, but not for unit itself?
      Can't it happen, that it has the same id, but is no missle?
    • "If cooridnates are in map bounds" could be an extra function for some readability.
      And the Min/Max coordinate values could be stored into variables once at game start.
    • In JASS you could keep RAD actually and only work with DEG when you need it for unit's facing angle.

    In the variable explainations, the triggers are explained like "this is the function...".
    No, it's not. That's the trigger, not the function.