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

[GUI-Friendly] Custom Missile System 1.4

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

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

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

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





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


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

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

Feel free to post any feedback or suggestions.

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

Just another Warcraft III map (Map)

Reviews
29.02.2016 BPower: Very well written. Very read-able as expected. The homing missile behaviour doesn't work for me in the uploaded test map. I think it has to to with radians to degree conversion. Can you check that? 21:56, 16th Jun 2015...

Moderator

M

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
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: http://www.hiveworkshop.com/forums/...e-system-1-1-a-263257/index2.html#post2679209
 
Level 5
Joined
May 13, 2012
Messages
66
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.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
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.

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 :)
 
Level 13
Joined
Aug 19, 2014
Messages
1,111
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.
 
  • 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.
    JASS:
    /*
    - 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?
    JASS:
    //      - 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.
  • JASS:
    //      - 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.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Update 1.2

I just released version 1.2

I think I now can actually say that it looks like a missile system :D

I am not quite sure if everything still works after the translation from a lot of variables into temp arrays but I think that everything works as intentioned.

Degrees are for users.
They rather use degrees when changing pitch or whatever... and I also find it easier to use.

--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------

Aaaaand update 1.3:

This is the final concept so no more functionalities unless they are really a must.
I changed the data structure and moved the calculations into functions that store the last result in a variable.
The power of this system is therefor (and because of locust) increased to 298% (from 420 missiles to 1250 missiles).
Another change is that missile's acceleration is removed.
All variables are now only in array form.
Added explaination of all variables.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
First and foremost: GUI

You tagged the systems as GUI, but it is not GUI!

A GUI resource shouldn't use any vJass only features. I know your core argument
is that it is GUI friendly ( somehow ), but putting everything into structs, while having wrapper
functions is also "GUI friendly".

I expect from GUI submissions, that they are coded in GUI or custom script.
JASS coded in plain JASS

For me this is a normal vJass submission and has to be moderated like such.


Do not label your functions with a BJ suffix. BJ refers to the blizzard.j

Let's begin:
  • Library basicFunctions --> library BasicFunctions, better would be a name related to Missile like MissileFunctions.
    Why not include them into the core library in the first place. Another option would be an external requirement ( remember you are using vJass )
    .
  • Encapsulation: use the private keyword for functions and variables not ment to be accessed ( you are using vJass )
    This ensures users do not accidently break your code.
    .
  • GetIndex does not appear to be so effective, as it is O(n), is there a distinct reason when this function should be used?
    .
  • Your variables do not follow the JPAG at all ( you are using vJass )
    .
  • I have to check it again, but iirc KillUnit is not a good way of removing a dummy.
    The locust effect vanishes and the unit gets caught by GroupEnumUnitsInRange ( I will check this )
    .
  • You do not maintain the fly height when flying over canions.
    This is required, once you want to take z collision into account.
    Otherwise it is just eyecandy.
    .
  • call SaveTimerHandle(udg_CMS_HASHTABLE, collidedId, missileId, t) this handle is never removed properly.
    .
  • Use just one global rect handle, instead of creating one per missile per iteration.
    .
  • CrowForm is added in any case on missile creation. Exclude it from the if then conditions and just do it once.
    .
  • call BJDebugMsg("pitch = " + R2S(CMS_GetPitchBJ(udg_CMS_Amount)))
    call BJDebugMsg("V_Z = " + R2S(udg_CMS_Param_V_Z[udg_CMS_Amount]))
    should not be there in missile create.

There is more to discuss, but I stop here for now.

Set to Need Fix
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,657
First and foremost: GUI

You tagged the systems as GUI, but it is not GUI!

A GUI resource shouldn't use any vJass only features. I know your core argument
is that it is GUI friendly ( somehow ), but putting everything into structs, while having wrapper
functions is also "GUI friendly".

I expect from GUI submissions, that they are coded in GUI or custom script.
JASS coded in plain JASS

For me this is a normal vJass submission and has to be moderated like such.
I'll call it vJASS then.

Do not label your functions with a BJ suffix. BJ refers to the blizzard.j
That answers a billion of my questions :D

Library basicFunctions --> library BasicFunctions, better would be a name related to Missile like MissileFunctions.
Why not include them into the core library in the first place. Another option would be an external requirement ( remember you are using vJass )
Those functions are usefull for anything and not only for this specific system.
They could even be part of Blizzard.j if that would be able to do.
However... I just place all functions that are like that inside a very basic library.

Encapsulation: use the private keyword for functions and variables not ment to be accessed ( you are using vJass )
This ensures users do not accidently break your code.
Still discovering vJASS :D

GetIndex does not appear to be so effective, as it is O(n), is there a distinct reason when this function should be used?
I think about replacing it with a linked list using the unit id by a unit indexer.
About the reason why to use it... you can get the index of the unit from the list... basically to use the destroy function or get the data of the arrays.

Your variables do not follow the JPAG at all ( you are using vJass )
JPAG?

I have to check it again, but iirc KillUnit is not a good way of removing a dummy.
The locust effect vanishes and the unit gets caught by GroupEnumUnitsInRange ( I will check this )
Well... the problem was that the special effect for example didnt play its death animation when the unit is removed.
Therefor, I thought to use KillUnit() instead of RemoveUnit() and that solved at least that problem.
Didn't think about other things that might occur.

You do not maintain the fly height when flying over canions.
This is required, once you want to take z collision into account.
Otherwise it is just eyecandy.
- (GetCoordinateZ(x2, y2) - GetCoordinateZ(x1, y1)
Really?

call SaveTimerHandle(udg_CMS_HASHTABLE, collidedId, missileId, t) this handle is never removed properly.
How do I not?
I null t, I remove the saved handle in that place, I never overwrite the handle...
What goes wrong?

Use just one global rect handle, instead of creating one per missile per iteration.
How can I change the size of an existing rect?

CrowForm is added in any case on missile creation. Exclude it from the if then conditions and just do it once.
Dunno if it gives problems with the order in which the missile setup is then but as far as I see, it should work.

call BJDebugMsg("pitch = " + R2S(CMS_GetPitchBJ(udg_CMS_Amount)))
call BJDebugMsg("V_Z = " + R2S(udg_CMS_Param_V_Z[udg_CMS_Amount]))
should not be there in missile create.
True.
 
Level 6
Joined
Feb 16, 2014
Messages
193
Just a question, can this missile system allow for multiple shooting for a single unit?
like, a unit can shoot 5 arrows at the same time.
 
Level 6
Joined
Feb 16, 2014
Messages
193
Wait, I have another question, if I have about 10 units firing about 20 projectiles every second would the game lag ? or crash ?
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I just released version 1.4

I fixed the issues based on BPower's post and added a few requested features such as acceleration, rectangular collision detection (for hyper speed missiles) and optimized the collision cooldown.

Wait, I have another question, if I have about 10 units firing about 20 projectiles every second would the game lag ? or crash ?
Sorry for not answering this question earlier :/
It will definately lagg... because you didn't specify the duration of how long the missiles will remain.
This means that after 5 minutes, there will be 60,000 missiles which is such a huge amount that nothing in your game works correctly any more.

But the game will not necessarily lagg when you have 10 units firing 20 projectiles every second if the missiles last for 2 seconds (which is pretty long for a regular missile).
The creation of missiles is so efficient that you can create more than 100 missiles and not even see any spike.
There can be at max 600-1000 missiles in the map (could be lower if you have some other stuff as well) but you can increase that by setting "udg_CMS_INTERVAL" to a higher value like "0.04" or "0.05" in the CMS_System trigger in the global block.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Why is there so much "garbage" in basic functions?
By garbage I mean stuff which the user doesn't need, maybe doesn't want.

Polygon is not listed as requirement in the core system.

Overall very readable and good coding.

I saw you allow rectangle collision, which I didn't cover in Missile yet.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I went through the entire code.

Great coding and overall a very useful system.

1.) I don't like the way you allocate instances. ( index + occupied. )
Warcraft initializes variables to a default value ( null, "", 0, 0. and false ).
For me it would be logical if only arrays which are actually used get initialized.
Example: array_string[100] = "hi" would initialize 0 - 99 with "" and 100 with "hi".
It may be different for wc3, I can't tell you.

Arguments which support my assumption are this and this post.

However which was tested, is that if you raised the array size for variables generated in the GUI editor, the amount of memory space in kb also raises.

I would go with stacking, like the default vJass struct creator does.


2.) I really don't like "BasicFunctions". There are very useful functions, particularly for algebra and geometry, but there is sooooo much crap inside.
Nobody needs backwards handle to integer conversion. It just eats up memory space.
I don't want to say it's not useful for you, how could I say that, but it's surely useless
for the rest of the wc3 modding community.

It's probably the best missile system submitted in the spell section ( I don't know all of them ), but I'll not approve it, simply because of the basic function library.
Maybe one of the other moderators thinks different.

3.)
There can be at max 600-1000 missiles in the map
like the fuel consumption information of the new Merdeces S500. Pure overestimation. I say it's 350 - 450 in a realistic test.
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,657
1, I still have to actually make it...

2, Yea, it is pretty much an overkill.
But every time when I use only a part of it, people ask me to upload the entire thing.

3, Ofcourse, there is a difference between some missiles.
If you have an arrow rain of 1000 arrows, you dont need all of them with collision detection... only one with the area of all.
It also depends on how much other things you have running, so for that, I lowered my "estimation" already.

Id say that you wont reach 350-450 without trying, but it is doable.
 
Level 7
Joined
Aug 11, 2010
Messages
269
I can't get homing missiles to work.

Here is the trigger that I've been fiddling with for over an hour now trying to get it to work; it just doesn't want to work...

  • HELR Aimed Shot Effect
    • Events
    • Unit - A unit Starts the effect of an ability
    • Conditions
    • (Ability being cast) Equal to [Hero - Elven Ranger] - Aimed Shot
    • Actions
    • Set TempPoint = (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 TempPoint)
    • Set CMS_StartingHeight = 32.00
    • Set CMS_StartingSource = TempUnit
    • Set CMS_StartingSpeed = 12.00
    • Set CMS_StartingAcceleration = 200.00
    • Set CMS_StartingType = 1
    • Custom script: set udg_TempInteger = CMS_CreateMissileEx()
    • -------- - --------
    • Set CMS_Param_Gravity[TempInteger] = (-2.00 x 0.03)
    • Set CMS_Param_IsFlying[TempInteger] = True
    • Set CMS_Param_IsHoming[TempInteger] = True
    • Set CMS_Param_TurnRate[TempInteger] = 180.00
    • Set CMS_Param_Target_Type[TempInteger] = CMS_TARGETTYPE_UNIT
    • Set CMS_Param_Target_Unit[TempInteger] = (Target unit of ability being cast)
    • 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\MoonPriestessMissile\MoonPriestessMissile.mdl
    • Set CMS_Param_Effect[TempInteger] = (Last created special effect)
    • -------- - --------
    • Custom script: call RemoveLocation(udg_TempPoint)
All it does is rotate 180 degrees and flies backwards. It seems to follow the unit but reversed of how it's supposed to. It's really irritating! It seems like no matter what value I change it won't do anything.
 
Level 7
Joined
Aug 11, 2010
Messages
269
Why on earth is this in the simple/substandard section of the hive? I still use the missile system. It's the best and easiest to customize missile system out there, with a maker that regularly replies to questions and concerns? I'm actually kind of disgusted that this isn't listed with the other spells...
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
Hey Wietlol,

I tested your missile system, because the feature list seemed to be amazing.

Then I tested the map, but the firebolt cannot fly up to the gryphon rider. Also, it bounces on the ground, and I could not find a boolean to check, whether I want to to explode on ground or not.
How could I make that missile really follow a 3d movement, e.g. hit the gryphon?

Can you give more examples in the map for usage of your system?
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
The bounce shouldnt happen at all.
It would be an interesting idea, but I didnt implement it in the system.

The missile should fly up to flying units as long as the "isFlying" boolean is set to true.
To make it follow the gryphons, the "isHoming" boolean should be true as well.
If these are set, then it will attempt to fly up to and follow flying units.
 
Level 14
Joined
Jul 1, 2008
Messages
1,314
thanks for your answer, but the bouncing happens in the test map, you provided. just shoot the firebolt from that platform to see what I meant.

If I set IsHoming and IsFlying both to true in the map, you uploaded, then the firebolt seems to explode right on the caster. It is strange, because you have set
  • (CMS_Collided_Unit belongs to an enemy of (Owner of CMS_Param_Source[CMS_LoopIndex])) Equal to (==) True
inn your test map.
I did not modify anything else, please see for yourself.
 
Top