• 🏆 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!

Missile [GUI] version 1.6.1

This bundle is marked as awaiting update. A staff member has requested changes to it before it can be approved.
Missile [GUI]

A system made to handle, optimize and ease projectile creation in your map.




The three golden rules for Missile
top
  1. Neither Missile__Origin nor Missile__Impact leak.
    Missile removes both location handles internally.

    ---
  2. All variables storing missile properties such as Missile__Owner or Missile__Damage
    are only valid within a trigger action function fired from the Missile trigger interface.
    Otherwise they will be null, 0 or false. To put it simple invalid.

    ---
  3. Every Missile related variable is prefixed with Missile
    You may only use variables with two underscores __, such as Missile__Source
    or variables with one underscore _, such as Missile_Source[index].
    All other variables are private and strictly reserved for the internal structure of Missile.


Create & destroy missiles
top

Create a missiles via Run Missile Configuration <gen> - Set missile properties - Run Missile <gen> syntax.
For example like here in my Fireball trigger:
  • Fireball Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Fireball
    • Actions
      • Set Missile__TempLoc = (Target point of ability being cast)
      • -------- --------
      • Trigger - Run Missile Configurate <gen> (ignoring conditions)
      • Set Missile__Source = (Triggering unit)
      • Set Missile__Owner = (Triggering player)
      • Set Missile__Origin = (Position of Missile__Source)
      • Set Missile__Impact = (Missile__Origin offset by 550.00 towards (Angle from Missile__Origin to Missile__TempLoc) degrees)
      • Set Missile__ImpactZ = 50.00
      • Set Missile__OriginZ = 50.00
      • Set Missile__Speed = 500.00
      • Set Missile__Model = Abilities\Weapons\FireBallMissile\FireBallMissile.mdl
      • Trigger - Run Missile <gen> (ignoring conditions)
      • -------- --------
      • Custom script: call RemoveLocation(udg_Missile__TempLoc)
A missile can be manually destroyed during each trigger event via
  • Set Missile__WantDestroy = True
or automatically when reaching the impact point.



Missile trigger interface
top

Missile optionally runs the five below listed trigger events.
Each event will upon occurrence run the trigger set to the trigger variable.
In the example below Fireball Actions <gen> would run for all events.

  • Set Missile__OnPeriodic = Fireball Actions <gen>
  • Set Missile__OnCollideUnit = Fireball Actions <gen>
  • Set Missile__OnCollideDestructable = Fireball Actions <gen>
  • Set Missile__OnFinish = Fireball Actions <gen>
  • Set Missile__OnRemove = Fireball Actions <gen>
You can identify the dispatched trigger by evaluating Missile__EventId in an integer comparison with
these constant Missile event variables for example Missile__EventId equals EVENT_MISSILE_FINISH

  • EVENT_MISSILE_PERIODIC runs every timer interval.
  • EVENT_MISSILE_COLLIDE_UNIT runs on unit collision.
  • EVENT_MISSILE_COLLIDE_DEST runs on destructable collision.
  • EVENT_MISSILE_FINISH runs if the impact point is reached. If on finish is defined you must destroy the missile manually.
  • EVENT_MISSILE_REMOVE runs when a missile is irrevocably destroyed.

Missile variables - Documentated in JASS format
top

JASS:
//=============================================================
//  Missile API. 
//=============================================================  
// Syntax for missile creation in GUI:
//    1. Run trigger Missile Configurate <gen> 
//    2. Set properties via scalar variables.
//    3. Run trigger Missile <gen>
// JASS users may call the create and launch function directly.
//
// Missile operates with custom events.
// This means that you have access to 
// a missile's properties within action functions
// of the Missile trigger interface. 
//
// "udg_Missile__EventId" indicates
// which event trigger has been dispatched.
//
// "udg_Missile__EventIndex" is the array index
// of the triggering missile.
//
// Avaliable events are:
//
//   1.) udg_EVENT_MISSILE_COLLIDE_UNIT
//        • Runs when a missile collides with a unit.
//
//   2.) udg_EVENT_MISSILE_COLLIDE_DEST
//        • Runs when a missile collides with a destructable.
//
//   3.) udg_EVENT_MISSILE_PERIODIC
//        • Runs every missile timer interval.
//
//   4.) udg_EVENT_MISSILE_FINISH
//        • Runs when a missile reaches its impact position.
//
//   5.) udg_EVENT_MISSILE_REMOVE
//        • Runs when a missile is destroyed.
//
//   6.) udg_EVENT_MISSILE_NOTHING
//        • Value of udg_Missile__EventId when accessed
//          from outside a trigger action function.
//
// During every missile event you may use the 
// following GUI generated variables.
// Every scalar variable has an equivalent array variable,
// which you can read and use at any time. 
// For exmple: udg_Missile__Source vs. udg_Missile_Source[index]
//
// Trigger variables which fire Missile events:
//
//    trigger udg_Missile__OnCollideUnit            -    trigger array udg_Missile_OnUnit 
//    trigger udg_Missile__OnCollideDestructable    -    trigger array udg_Missile_OnDest
//    trigger udg_Missile__OnPeridoic               -    trigger array udg_Missile_OnPeriodic
//    trigger udg_Missile__OnRemove                 -    trigger array udg_Missile_OnRemove
//    trigger udg_Missile__OnFinish                 -    trigger array udg_Missile_OnFinish
//
// Variables which mimic a function call:
//
//    location udg_Missile__Origin    
//    location udg_Missile__Impact      
//    boolean  udg_Missile__WantDestroy    -    boolean array udg_Missile_WantDestroy
//    boolean  udg_Missile__Recycle        -    boolean array udg_Missile_Recycle
//    real     udg_Missile__Scale          -    real    array udg_Missile_Scale
//    real     udg_Missile__FlyTime        -    real    array udg_Missile_FlyTime ( Converts time in seconds to a vector lenght )
//    real     udg_Missile__Model          -    string  array udg_Missile_Model   ( Converts a string path to a special effect )
//    real     udg_Missile__Arc            -    real    array udg_Missile_Arc     ( Converts an arc in degree to height value )
//    real     udg_Missile__Curve          -    real    array udg_Missile_Curve   ( Converts a curve in degree to an open value )
//
// Variables for read-only:
//
//    integer udg_Missile__EventId     
//    integer udg_Missile__EventIndex  
//    unit    udg_Missile__Dummy           -    unit array udg_Missile_Dummy
//    real    udg_Missile__Angle           -    real array udg_Missile_Angle    ( In radians )
//    real    udg_Missile__Distance        -    real array udg_Missile_Distance ( Total distance traveled )
//
// Variables for read and set.
//
//    unit    udg_Missile__Source          -    unit    array udg_Missile_Source
//    unit    udg_Missile__Target          -    unit    array udg_Missile_Target    ( Enables homing behaviour towards a target unit )
//    player  udg_Missile__Owner           -    unit    array udg_Missile_Owner     ( Pseudo-owner for better onCollide evaluation ) 
//    real    udg_Missile__ImpactZ         -    real    array udg_Missile_ImpactZ
//    real    udg_Missile__OriginZ         -    real    array udg_Missile_OriginZ
//    real    udg_Missile__Damage          -    real    array udg_Missile_Damage
//    real    udg_Missile__Collision       -    real    array udg_Missile_Collision
//    real    udg_Missile__Speed           -    real    array udg_Missile_Speed
//    real    udg_Missile__Acceleration    -    real    array udg_Missile_Acceleration
//    real    udg_Missile__Height          -    real    array udg_Missile_Height    ( Highest point of the parabola )
//    real    udg_Missile__Open            -    real    array udg_Missile_Open      
//    real    udg_Missile__Turn            -    real    array udg_Missile_Turn      ( Turn ratio per second )
//    real    udg_Missile__Data            -    integer array udg_Missile_Data


Example trigger action function
top

Let's see how the event triggers run code in an action function.
We stick to the fireball scenario. This spell is also included in the demo map.

  • Fireball Actions
    • Events
    • Conditions
    • Actions
      • -------- As you can see Missile__EventId allows you --------
      • -------- to identify which event trigger dispatched. --------
      • -------- Therefore you can run all actions from one trigger function. --------
      • -------- Of course you can also seperate triggers into different triggers. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Missile__EventId Equal to EVENT_MISSILE_REMOVE
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Missile__Data Equal to 1
            • Then - Actions
              • -------- Create more fireballs --------
              • -------- Reference to Missile__Dummy will be removed --------
              • -------- Once you call Run Missile Configurate <gen> --------
              • Set Missile__TempLoc = (Position of Missile__Dummy)
              • For each (Integer A) from 1 to 6, do (Actions)
                • Loop - Actions
                  • Trigger - Run Missile Configurate <gen> (ignoring conditions)
                  • Set Missile__Origin = (Point((X of Missile__TempLoc), (Y of Missile__TempLoc)))
                  • Set Missile__Impact = (Missile__Origin offset by 450.00 towards (60.00 x (Real((Integer A)))) degrees)
                  • Set Missile__ImpactZ = 50.00
                  • Set Missile__Height = 350.00
                  • Set Missile__OriginZ = 50.00
                  • Set Missile__Speed = 500.00
                  • Set Missile__Damage = 150.00
                  • Set Missile__Source = Missile_Source[Missile__EventIndex]
                  • Set Missile__Owner = Missile_Owner[Missile__EventIndex]
                  • Set Missile__Model = Abilities\Weapons\FireBallMissile\FireBallMissile.mdl
                  • Set Missile__OnCollideUnit = Fireball Actions <gen>
                  • Set Missile__OnRemove = Fireball Actions <gen>
                  • Set Missile__Data = 2
                  • Trigger - Run Missile <gen> (ignoring conditions)
              • Custom script: call RemoveLocation(udg_Missile__TempLoc)
              • Custom script: set udg_Missile__TempLoc = null
            • Else - Actions
              • Set Missile__TempLoc = (Position of Missile__Dummy)
              • Set Temp_Group = (Units within 128.00 of Missile__TempLoc matching ((((Matching unit) belongs to an enemy of Missile__Owner) Equal to True) and (((Matching unit) is alive) Equal to True)))
              • Unit Group - Pick every unit in Temp_Group and do (Actions)
                • Loop - Actions
                  • Unit - Cause Missile__Source to damage (Picked unit), dealing Missile__Damage damage of attack type Spells and damage type Normal
              • Special Effect - Create a special effect at Missile__TempLoc using Abilities\Spells\Other\Doom\DoomDeath.mdl
              • Special Effect - Destroy (Last created special effect)
              • Custom script: call DestroyGroup(udg_Temp_Group)
              • Custom script: call RemoveLocation(udg_Missile__TempLoc)
              • Custom script: set udg_Missile__TempLoc = null
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Missile__EventId Equal to EVENT_MISSILE_COLLIDE_UNIT
              • (Missile__UnitHit belongs to an enemy of Missile__Owner) Equal to True
            • Then - Actions
              • -------- See how easy damaging got. --------
              • Unit - Cause Missile__Source to damage Missile__UnitHit, dealing Missile__Damage damage of attack type Normal and damage type Normal
              • Set Missile__WantDestroy = True
            • Else - Actions


Keywords:
Missile, Projectile, System
Contents

Just another Warcraft III map (Map)

Reviews
IcemanBo
Submission: Missile [GUI] v1.61 Date: 1 January 2017 Status: Awaiting Update Rating: noneNote: It works actually, but there are some things that needs your attention first. Alone such little things that event ids are different from description is...

Moderator

M

Moderator

08:18, 31st May 2016
Bribe: This doesn't compile in vanilla WE due to the error messages you've set up spanning multiple lines.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
System Code:
JASS:
///=============================================================
// Name: Missile
// Version: 1.6.1
// Description: A system for your projectile needs.
// Author: BPower
//
// Written in JASS, API designed for usage with GUI.
// Published on hiveworkshop.com
//     - hiveworkshop.com/forums/spells-569/missile-gui-version-1-1-a-275718/
//=============================================================
// Before getting started make sure that you check 
// the settings in trigger Missile Configurate.
//
//     udg_Missile__DummyTypeId 
//        • Make sure you have Vexorians dummy.mdx imported.
//        • Only units of this type id will be recycled. 
//
//=============================================================
//  Variable naming. 
//============================================================= 
// Variables which users shouldn't access have no underscore.
// Array variables which are available at any time have one underscore _.
// Scalar variables which are only available during events have two underscores __.
// 
//=============================================================
//  Missile API. 
//=============================================================  
// Syntax for missile creation in GUI:
//    1. Run trigger Missile Configurate <gen> 
//    2. Set properties via scalar variables.
//    3. Run trigger Missile <gen>
// JASS users may call the create and launch function directly.
//
// Missile operates with custom events.
// This means that you have access to 
// a missile's properties within action functions
// of the Missile trigger interface. 
//
// "udg_Missile__EventId" indicates
// which event trigger has been dispatched.
//
// "udg_Missile__EventIndex" is the array index
// of the triggering missile.
//
// Avaliable events are:
//
//   1.) udg_EVENT_MISSILE_COLLIDE_UNIT
//        • Runs when a missile collides with a unit.
//
//   2.) udg_EVENT_MISSILE_COLLIDE_DEST
//        • Runs when a missile collides with a destructable.
//
//   3.) udg_EVENT_MISSILE_PERIODIC
//        • Runs every missile timer interval.
//
//   4.) udg_EVENT_MISSILE_FINISH
//        • Runs when a missile reaches its impact position.
//
//   5.) udg_EVENT_MISSILE_REMOVE
//        • Runs when a missile is destroyed.
//
//   6.) udg_EVENT_MISSILE_NOTHING
//        • Value of udg_Missile__EventId when accessed
//          from outside a trigger action function.
//
// During every missile event you may use the 
// following GUI generated variables.
// Every scalar variable has an equivalent array variable,
// which you can read and use at any time. 
// For exmple: udg_Missile__Source vs. udg_Missile_Source[index]
//
// Trigger variables which fire Missile events:
//
//    trigger udg_Missile__OnCollideUnit            -    trigger array udg_Missile_OnUnit 
//    trigger udg_Missile__OnCollideDestructable    -    trigger array udg_Missile_OnDest
//    trigger udg_Missile__OnPeridoic               -    trigger array udg_Missile_OnPeriodic
//    trigger udg_Missile__OnRemove                 -    trigger array udg_Missile_OnRemove
//    trigger udg_Missile__OnFinish                 -    trigger array udg_Missile_OnFinish
//
// Variables which mimic a function call:
//
//    location udg_Missile__Origin    
//    location udg_Missile__Impact      
//    boolean  udg_Missile__WantDestroy    -    boolean array udg_Missile_WantDestroy
//    boolean  udg_Missile__Recycle        -    boolean array udg_Missile_Recycle
//    real     udg_Missile__Scale          -    real    array udg_Missile_Scale
//    real     udg_Missile__FlyTime        -    real    array udg_Missile_FlyTime ( Converts time in seconds to a vector lenght )
//    real     udg_Missile__Model          -    string  array udg_Missile_Model   ( Converts a string path to a special effect )
//    real     udg_Missile__Arc            -    real    array udg_Missile_Arc     ( Converts an arc in degree to height value )
//    real     udg_Missile__Curve          -    real    array udg_Missile_Curve   ( Converts a curve in degree to an open value )
//
// Variables for read-only:
//
//    integer udg_Missile__EventId     
//    integer udg_Missile__EventIndex  
//    unit    udg_Missile__Dummy           -    unit array udg_Missile_Dummy
//    real    udg_Missile__Angle           -    real array udg_Missile_Angle    ( In radians )
//    real    udg_Missile__Distance        -    real array udg_Missile_Distance ( Total distance traveled )
//
// Variables for read and set.
//
//    unit    udg_Missile__Source          -    unit    array udg_Missile_Source
//    unit    udg_Missile__Target          -    unit    array udg_Missile_Target    ( Enables homing behaviour towards a target unit )
//    player  udg_Missile__Owner           -    unit    array udg_Missile_Owner     ( Pseudo-owner for better onCollide evaluation ) 
//    real    udg_Missile__ImpactZ         -    real    array udg_Missile_ImpactZ
//    real    udg_Missile__OriginZ         -    real    array udg_Missile_OriginZ
//    real    udg_Missile__Damage          -    real    array udg_Missile_Damage
//    real    udg_Missile__Collision       -    real    array udg_Missile_Collision
//    real    udg_Missile__Speed           -    real    array udg_Missile_Speed
//    real    udg_Missile__Acceleration    -    real    array udg_Missile_Acceleration
//    real    udg_Missile__Height          -    real    array udg_Missile_Height    ( Highest point of the parabola )
//    real    udg_Missile__Open            -    real    array udg_Missile_Open      
//    real    udg_Missile__Turn            -    real    array udg_Missile_Turn      ( Turn ratio per second )
//    real    udg_Missile__Data            -    integer array udg_Missile_Data
//
//=============================================================
// JASS user settings.
//=============================================================

// Set the timer timeout for projectile motion and trigger interface evaluation.
constant function Missile_GetTimerTimeout takes nothing returns real
    return 0.031250000
endfunction

// Set the maximum widget collision radius in this map.
// By default the largest pathing is the human townhall with 196.
constant function Missile_GetMaxCollision takes nothing returns real
    return 196.
endfunction

// Set the owning player of all dummy units.
// It should be a neutral player in your map.
constant function Missile_GetDummyOwner takes nothing returns player
    return Player(PLAYER_NEUTRAL_PASSIVE)
endfunction

// Fictional z height for units in your map.
function Missile_GetUnitBodySize takes unit whichUnit returns real
    return 100.
endfunction

// Fictional z height for destructables in your map.
function Missile_GetDestructableHeight takes destructable whichDest returns real
    return GetDestructableOccluderHeight(whichDest)
endfunction

// Fictional value for the maximum effect death time in your map.
constant function Missile_GetEffectDeathTime takes nothing returns integer
    return 3
endfunction

//=============================================================
// Required utility functions.
//=============================================================

// Debugging Missile.
// Change the condition from "true" to "false" or delete 
// the content of this function to disable error messages.
function Missile_ErrorMessage takes boolean expr, string func, string object, integer index, string text returns nothing
    local string orange = "|cffffa500" 
    local string blue   = "|cff99b4d1"
    local string next   = "\n"
    local string space  = "    "
    local string str    = space + next
    if expr and true then
        set str = str +                       orange + "Error in Trigger Missile|r" 
        set str = str + next + next + space + orange + "Function: |r"    + blue + func       + "|r" 
        set str = str        + next + space + orange + "Object: |r"      + blue + object     + "|r"
        set str = str        + next + space + orange + "Index: |r"       + blue + I2S(index) + "|r"
        set str = str        + next + space + orange + "Description: |r" + blue + text       + "|r"
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 3600.00, str)
    endif
endfunction

// Returns the proper Z height of point p(x/y).
function Missile_GetLocZ takes real x, real y returns real
    call MoveLocation(udg_MissileLocZ, x, y)
    return GetLocationZ(udg_MissileLocZ)
endfunction

function Missile_GetArc takes integer index returns real
    return Tan(udg_Missile__Arc*bj_DEGTORAD)*udg_Missile_AB_Distance[index]/4
endfunction

function Missile_GetCurve takes integer index returns real
    return Tan(udg_Missile__Curve*bj_DEGTORAD)*udg_Missile_AB_Distance[index]
endfunction

// Converts fly time to missile speed.
// Doesn't take acceleration into account.
function Missile_GetFlyTime takes integer index returns real                
    return (udg_Missile_AB_Distance[index] - udg_Missile_AB_Traveled[index])/RMaxBJ(0.0000001, udg_Missile__FlyTime)
endfunction

function Missile_Math takes integer index returns nothing
    local real dx
    local real dy
    local real iz// Allows to set an impact offset in z.
    
    if udg_Missile_Target[index] != null and GetUnitTypeId(udg_Missile_Target[index]) != 0 then
        set udg_Missile_OriginX[index] = GetUnitX(udg_Missile_Dummy[index])
        set udg_Missile_OriginY[index] = GetUnitY(udg_Missile_Dummy[index])
        set udg_Missile_OriginZ[index] = GetUnitFlyHeight(udg_Missile_Dummy[index])
        set udg_Missile_ImpactX[index] = GetUnitX(udg_Missile_Target[index])
        set udg_Missile_ImpactY[index] = GetUnitY(udg_Missile_Target[index])
        set udg_Missile_AB_Traveled[index] = 0.00
        // Once you set a homing target you'll lose arc & curve settings.
        // That may get patched in the next version update.
        set udg_Missile_Open[index] = 0.00
        set udg_Missile_Height[index] = 0.00
        set udg_Missile_Curve[index] = 0.00
        set udg_Missile_Arc[index] = 0.00
        set iz = GetUnitFlyHeight(udg_Missile_Target[index]) + udg_Missile_ImpactZ[index]
    else
        set iz = udg_Missile_ImpactZ[index]
    endif
    
    loop
        set dx = udg_Missile_OriginX[index] - udg_Missile_ImpactX[index]
        set dy = udg_Missile_OriginY[index] - udg_Missile_ImpactY[index]
        set dx = dx*dx + dy*dy
        set dy = SquareRoot(dx)
        exitwhen dx != 0.00 and dy != 0.00// Check for parabola limits.
        set udg_Missile_OriginX[index] = udg_Missile_OriginX[index] + 0.01
    endloop    
    set udg_Missile_A_Z[index] = Missile_GetLocZ(udg_Missile_OriginX[index], udg_Missile_OriginY[index])
    set udg_Missile_B_Z[index] = Missile_GetLocZ(udg_Missile_ImpactX[index], udg_Missile_ImpactY[index]) 
    set udg_Missile_AB_Square[index] = dx
    set udg_Missile_AB_Distance[index] = dy
    set udg_Missile_AB_Angle[index] = Atan2(udg_Missile_ImpactY[index] - udg_Missile_OriginY[index], udg_Missile_ImpactX[index] - udg_Missile_OriginX[index])
    set udg_Missile_AB_Pitch[index] = ((iz + udg_Missile_B_Z[index]) - (udg_Missile_OriginZ[index] + udg_Missile_A_Z[index]))/dy
endfunction

// Doesn't include all array types. Only those which require a reset.
function Missile_ResetArrays takes integer index returns nothing
    set udg_Missile_AB_Traveled[index]  = 0.00
    set udg_Missile_Acceleration[index] = 0.00
    set udg_Missile_Arc[index]          = 0.00
    set udg_Missile_Collision[index]    = 0.00
    set udg_Missile_Curve[index]        = 0.00
    set udg_Missile_CurveX[index]       = 0.00
    set udg_Missile_CurveY[index]       = 0.00
    set udg_Missile_Damage[index]       = 0.00
    set udg_Missile_Data[index]         = 0
    set udg_Missile_Distance[index]     = 0.00
    set udg_Missile_Dummy[index]        = null
    set udg_Missile_Effect[index]       = null
    set udg_Missile_Model[index]        = null
    set udg_Missile_FlyTime[index]      = 0.00
    set udg_Missile_Height[index]       = 0.00
    set udg_Missile_OriginZ[index]      = 0.00
    set udg_Missile_ImpactZ[index]      = 0.00
    set udg_Missile_OnUnit[index]       = null
    set udg_Missile_OnDest[index]       = null
    set udg_Missile_OnPeriodic[index]   = null
    set udg_Missile_OnFinish[index]     = null
    set udg_Missile_OnRemove[index]     = null
    set udg_Missile_Open[index]         = 0.00
    set udg_Missile_Owner[index]        = null
    set udg_Missile_Recycle[index]      = false
    set udg_Missile_Scale[index]        = 1.00
    set udg_Missile_Source[index]       = null
    set udg_Missile_Speed[index]        = 0.00
    set udg_Missile_Target[index]       = null
    set udg_Missile_Turn[index]         = 0.00
    set udg_Missile_WantDestroy[index]  = false
endfunction

function Missile_SaveUserData takes integer index returns nothing
    local boolean math = false
    
    set udg_Missile_OnUnit[index]       = udg_Missile__OnCollideUnit
    set udg_Missile_OnDest[index]       = udg_Missile__OnCollideDestructable
    set udg_Missile_OnPeriodic[index]   = udg_Missile__OnPeriodic
    set udg_Missile_OnFinish[index]     = udg_Missile__OnFinish
    set udg_Missile_OnRemove[index]     = udg_Missile__OnRemove
    set udg_Missile_Source[index]       = udg_Missile__Source 
    set udg_Missile_Target[index]       = udg_Missile__Target
    set udg_Missile_Owner[index]        = udg_Missile__Owner
    set udg_Missile_Data[index]         = udg_Missile__Data
    set udg_Missile_Collision[index]    = udg_Missile__Collision
    set udg_Missile_WantDestroy[index]  = udg_Missile__WantDestroy
    set udg_Missile_Damage[index]       = udg_Missile__Damage
    set udg_Missile_Acceleration[index] = udg_Missile__Acceleration
    set udg_Missile_Turn[index]         = udg_Missile__Turn
    
    // The user set a new origin point.
    if udg_Missile__Origin != null then
        set udg_Missile_OriginX[index] = GetLocationX(udg_Missile__Origin) 
        set udg_Missile_OriginY[index] = GetLocationY(udg_Missile__Origin) 
        call RemoveLocation(udg_Missile__Origin)
        set udg_Missile__Origin = null
        set math = true
    endif
    if udg_Missile_OriginZ[index] != udg_Missile__OriginZ then
        set udg_Missile_OriginZ[index] = udg_Missile__OriginZ
        set math = true
    endif

    if udg_Missile__Impact != null then
        set udg_Missile_ImpactX[index] = GetLocationX(udg_Missile__Impact) 
        set udg_Missile_ImpactY[index] = GetLocationY(udg_Missile__Impact)
        call RemoveLocation(udg_Missile__Impact)
        set udg_Missile__Impact = null
        set math = true
    endif
    if udg_Missile_ImpactZ[index] != udg_Missile__ImpactZ then
        set udg_Missile_ImpactZ[index] = udg_Missile__ImpactZ
        set math = true
    endif
    if math then
        call Missile_Math(index)
    endif

    // The user defined a new model.
    if udg_Missile_Model[index] != udg_Missile__Model then
        set udg_Missile_Model[index] = udg_Missile__Model
        if udg_Missile_Effect[index] != null then
            call DestroyEffect(udg_Missile_Effect[index])
        endif
        if StringLength(udg_Missile__Model) > 0 then
            set udg_Missile_Effect[index] = AddSpecialEffectTarget(udg_Missile__Model, udg_Missile_Dummy[index], "origin")
        else
            set udg_Missile_Effect[index] = null
        endif
    endif
    
    // Default scaling on Missile_Create is 1.00.
    if udg_Missile_Scale[index] != udg_Missile__Scale then
        set udg_Missile_Scale[index] = udg_Missile__Scale
        call SetUnitScale(udg_Missile_Dummy[index], udg_Missile__Scale, 0.00, 0.00)
    endif
    
    if udg_Missile_Curve[index] != udg_Missile__Curve then
        set udg_Missile_Curve[index] = udg_Missile__Curve
        set udg_Missile_Open[index] = Missile_GetCurve(index)
    else
        set udg_Missile_Open[index] = udg_Missile__Open
    endif

    if udg_Missile_Arc[index] != udg_Missile__Arc then
        set udg_Missile_Arc[index] = udg_Missile__Arc
        set udg_Missile_Height[index] = Missile_GetArc(index)
    else
        set udg_Missile_Height[index] = udg_Missile__Height
    endif

    if udg_Missile_FlyTime[index] != udg_Missile__FlyTime then
        set udg_Missile_FlyTime[index] = udg_Missile__FlyTime
        set udg_Missile_Speed[index] = Missile_GetFlyTime(index)
    else
        set udg_Missile_Speed[index] = udg_Missile__Speed
    endif
endfunction

function Missile_LoadData takes integer index returns nothing
    set udg_Missile__Index                 = index

    // Load event triggers.
    set udg_Missile__OnCollideUnit         = udg_Missile_OnUnit[index]
    set udg_Missile__OnCollideDestructable = udg_Missile_OnDest[index]
    set udg_Missile__OnPeriodic            = udg_Missile_OnPeriodic[index]
    set udg_Missile__OnFinish              = udg_Missile_OnFinish[index]
    set udg_Missile__OnRemove              = udg_Missile_OnRemove[index]
    
    // Load other handle types.
    set udg_Missile__Dummy                 = udg_Missile_Dummy[index]
    set udg_Missile__Source                = udg_Missile_Source[index] 
    set udg_Missile__Target                = udg_Missile_Target[index]
    set udg_Missile__Owner                 = udg_Missile_Owner[index]
    
    set udg_Missile__Damage                = udg_Missile_Damage[index]
    set udg_Missile__Collision             = udg_Missile_Collision[index] 
    set udg_Missile__Model                 = udg_Missile_Model[index] 
    set udg_Missile__Data                  = udg_Missile_Data[index]
    set udg_Missile__Scale                 = udg_Missile_Scale[index]
    set udg_Missile__WantDestroy           = udg_Missile_WantDestroy[index]
    
    set udg_Missile__Speed                 = udg_Missile_Speed[index]
    set udg_Missile__Acceleration          = udg_Missile_Acceleration[index]
    set udg_Missile__Open                  = udg_Missile_Open[index]
    set udg_Missile__Turn                  = udg_Missile_Turn[index]
    set udg_Missile__Height                = udg_Missile_Height[index]
    set udg_Missile__Angle                 = udg_Missile_Angle[index]
    set udg_Missile__Distance              = udg_Missile_Distance[index]
    set udg_Missile__Arc                   = udg_Missile_Arc[index]
    set udg_Missile__Curve                 = udg_Missile_Curve[index]
    set udg_Missile__FlyTime               = udg_Missile_FlyTime[index]
    
    set udg_Missile__OriginZ               = udg_Missile_OriginZ[index]
    set udg_Missile__ImpactZ               = udg_Missile_ImpactZ[index]
endfunction

function Missile_ResetScalars takes nothing returns nothing
    if udg_Missile__Origin != null then
        call RemoveLocation(udg_Missile__Origin)
        set udg_Missile__Origin = null
    endif
    if udg_Missile__Impact != null then
        call RemoveLocation(udg_Missile__Origin)
        set udg_Missile__Impact = null
    endif
    set udg_Missile__WantDestroy           = false
    set udg_Missile__Recycle               = false
    set udg_Missile__OnCollideUnit         = null
    set udg_Missile__OnPeriodic            = null
    set udg_Missile__OnFinish              = null
    set udg_Missile__OnCollideDestructable = null
    set udg_Missile__OnRemove              = null
    set udg_Missile__Dummy                 = null
    set udg_Missile__Source                = null
    set udg_Missile__Target                = null
    set udg_Missile__Owner                 = null
    set udg_Missile__Model                 = null
    set udg_Missile__Angle                 = 0.00
    set udg_Missile__Collision             = 0.00
    set udg_Missile__Damage                = 0.00
    set udg_Missile__Speed                 = 0.00
    set udg_Missile__Acceleration          = 0.00
    set udg_Missile__Open                  = 0.00
    set udg_Missile__Height                = 0.00
    set udg_Missile__Turn                  = 0.00
    set udg_Missile__Distance              = 0.00
    set udg_Missile__ImpactZ               = 0.00
    set udg_Missile__OriginZ               = 0.00
    set udg_Missile__Arc                   = 0.00
    set udg_Missile__Curve                 = 0.00
    set udg_Missile__FlyTime               = 0.00
    set udg_Missile__Scale                 = 1.00
    set udg_Missile__EventId               = udg_EVENT_MISSILE_NOTHING
    set udg_Missile__Data                  = 0
    set udg_Missile__Index                 = 0
endfunction

//=============================================================
// Wrapper functions.
//=============================================================

function Missile_RunTrigger takes integer index, trigger trig, integer eventId returns boolean
    set udg_Missile__EventId = eventId
    set udg_Missile__EventIndex = index
    call Missile_LoadData(index)
    if TriggerEvaluate(trig) then
        call TriggerExecute(trig)
        call Missile_SaveUserData(index)
    endif
    // Just in case the user accidently changes udg_Missile__Index.
    set udg_Missile__EventIndex = 0
    set udg_Missile__EventId = udg_EVENT_MISSILE_NOTHING
    return udg_Missile__WantDestroy
endfunction  

//=============================================================
// Delayed dummy recycler. Data structure stack.
//=============================================================
// According to my tests:
//    - Using KillUnit(unit) will remove the 'Aloc' effect for group enumerations.
//    - Using UnitApplyTimedLife(unit, 'BTLF', 0.01) keeps on dropping the fps until you hit 0.
//
// The cleanest approach appears to be RemoveUnit(unit). Using a stack with timed dummy
// recycling helps us to properly display the attached special effects.

function Missile_UpdateStack takes nothing returns nothing
    local integer index = 0
    
    loop
        exitwhen index == udg_MissileDummyCount

        if udg_MissileDummyTime[index] <= 0 then
            call RemoveUnit(udg_MissileDummyStack[index])
            
            set udg_MissileDummyCount = udg_MissileDummyCount - 1
            if  udg_MissileDummyCount == 0 then
                set udg_MissileDummyStack[index] = null
                call PauseTimer(udg_MissileDummyTimer)
            else
                set udg_MissileDummyStack[index] = udg_MissileDummyStack[udg_MissileDummyCount]
                set udg_MissileDummyTime[index] = udg_MissileDummyTime[udg_MissileDummyCount]
                set udg_MissileDummyStack[udg_MissileDummyCount] = null
            endif
        else
            set udg_MissileDummyTime[index] = udg_MissileDummyTime[index] - 1
            set index = index + 1
        endif
    endloop
endfunction

function Missile_RecycleDummy takes unit dummy returns nothing
    if GetUnitTypeId(dummy) == udg_Missile__DummyTypeId then
        
        set udg_MissileDummyTime[udg_MissileDummyCount] = Missile_GetEffectDeathTime()// Approximate death time for special effects.
        set udg_MissileDummyStack[udg_MissileDummyCount] = dummy
        
        if udg_MissileDummyCount == 0 then
            call TimerStart(udg_MissileDummyTimer, 1., true, function Missile_UpdateStack)
        endif
        set udg_MissileDummyCount = udg_MissileDummyCount + 1
    endif
endfunction

//=============================================================
// Missile structure. Static unique doubly linked list.
//=============================================================

function Missile_SetListNextNode takes integer node, integer nextNode returns nothing
    set udg_MissileNextNode[node] = nextNode
endfunction
constant function Missile_GetListNextNode takes integer node returns integer
    return udg_MissileNextNode[node]
endfunction

function Missile_SetListPrevNode takes integer node, integer prevNode returns nothing
    set udg_MissilePrevNode[node] = prevNode
endfunction
constant function Missile_GetListPrevNode takes integer node returns integer
    return udg_MissilePrevNode[node]
endfunction

function Missile_SetListFirstNode takes integer node returns nothing
    set udg_MissileNextNode[0] = node
endfunction
constant function Missile_GetListFirstNode takes nothing returns integer
    return udg_MissileNextNode[0]
endfunction

function Missile_SetListLastNode takes integer node returns nothing
    set udg_MissilePrevNode[0] = node
endfunction
constant function Missile_GetListLastNode takes nothing returns integer
    return udg_MissilePrevNode[0]
endfunction

function Missile_ListRemoveNode takes integer node returns nothing
    call Missile_SetListNextNode(Missile_GetListPrevNode(node), Missile_GetListNextNode(node))
    call Missile_SetListPrevNode(Missile_GetListNextNode(node), Missile_GetListPrevNode(node))
endfunction

function Missile_ListPushNode takes integer node returns nothing
    call Missile_SetListPrevNode(Missile_GetListFirstNode(), node)
    call Missile_SetListNextNode(node, Missile_GetListFirstNode())
    call Missile_SetListFirstNode(node)
    call Missile_SetListPrevNode(node, 0)
endfunction

function Missile_AllocateNode takes nothing returns integer
    local integer node = udg_MissileRecycler[0]
    
    if node == 0 then
        set node = udg_MissileNodes + 1
           // Overflow
        if node == JASS_MAX_ARRAY_SIZE then
            call Missile_ErrorMessage(true, "Missile_AllocateNode", "udg_MissileNodes", JASS_MAX_ARRAY_SIZE, "Stack overflow!")
            return 0
        endif
    
        set udg_MissileNodes = node
    else
        set udg_MissileRecycler[0] = udg_MissileRecycler[node]
    endif
    
    set udg_MissileAllocated[node] = true
    
    return node
endfunction

function Missile_RecycleNode takes integer node returns nothing
    set udg_MissileAllocated[node] = false
    set udg_MissileRecycler[node] = udg_MissileRecycler[0]
    set udg_MissileRecycler[0] = node
endfunction

//=================================================================
// Destructor & Cleanup.
//=================================================================

function Missile_Clear takes integer index returns nothing
    if udg_Missile_Effect[index] != null then
        call DestroyEffect(udg_Missile_Effect[index])
    endif
    call Missile_RecycleDummy(udg_Missile_Dummy[index])
    call Missile_ResetArrays(index)
    call FlushChildHashtable(udg_MissileHash, index)
endfunction

function Missile_Destroy takes integer index returns nothing
    if udg_MissileAllocated[index] then
        if udg_MissileLaunched[index] then
            if udg_Missile_OnRemove[index] != null then
                call Missile_RunTrigger(index, udg_Missile_OnRemove[index], udg_EVENT_MISSILE_REMOVE)
            endif
            
            call Missile_ListRemoveNode(index)
            if Missile_GetListFirstNode() == 0 then
                call PauseTimer(udg_MissileTimer)
            endif
            set udg_MissileLaunched[index] = false 
        endif
        
        call Missile_Clear(index)
        call Missile_RecycleNode(index)
    else
        call Missile_ErrorMessage(true, "Missile_Destroy", "udg_MissileAllocated", index, "Attempt to destroy invalid node!")
    endif
endfunction

//=============================================================
// Widget collision code.
//=============================================================

// For a rectangle collision z values are
// very inaccurate, hence they are completly ignored.
function Missile_IsWidgetInRectangleRad takes integer index, widget w returns boolean
    local real wx = GetWidgetX(w)
    local real wy = GetWidgetY(w) 
    local real dx = udg_Missile_PosX[index] - udg_Missile_PrevX[index]
    local real dy = udg_Missile_PosY[index] - udg_Missile_PrevY[index]
    local real s  = (dx*(wx - udg_Missile_PrevX[index]) + dy*(wy - udg_Missile_PrevY[index]))/RMaxBJ(0.0001, (dx*dx + dy*dy))
    if s < 0.00 then
        set s = 0.00
    elseif s > 1.00 then
        set s = 1.00
    endif
    set dx = (udg_Missile_PrevX[index] + s*dx) - wx
    set dy = (udg_Missile_PrevY[index] + s*dy) - wy
    return dx*dx + dy*dy <= udg_Missile_Collision[index]*udg_Missile_Collision[index] 
endfunction
                                                    
function Missile_IsWidgetInRange takes integer index, widget w, real wz, real distance returns boolean
    local real wx = GetWidgetX(w)
    local real wy = GetWidgetY(w)
    local real dz = Missile_GetLocZ(wx, wy) - udg_Missile_TerrainZ[index]
    return IsUnitInRangeXY(udg_Missile_Dummy[index], wx, wy, distance) and dz + wz >= udg_Missile_PosZ[index] and dz <= udg_Missile_PosZ[index] + distance
endfunction

function Missile_SetRectRectangle takes integer index returns rect
    local real x1 = udg_Missile_PrevX[index] 
    local real y1 = udg_Missile_PrevY[index]
    local real x2 = udg_Missile_PosX[index]
    local real y2 = udg_Missile_PosY[index]
    local real d = udg_Missile_Collision[index] + Missile_GetMaxCollision()
    // What is min, what is max ...
    if x1 < x2 then
        if y1 < y2 then
            call SetRect(udg_MissileRect, x1 - d, y1 - d, x2 + d, y2 + d)
        else
            call SetRect(udg_MissileRect, x1 - d, y2 - d, x2 + d, y1 + d)
        endif
    else
        if y1 < y2 then
            call SetRect(udg_MissileRect, x2 - d, y1 - d, x1 + d, y2 + d)
        else
            call SetRect(udg_MissileRect, x2 - d, y2 - d, x1 + d, y1 + d)
        endif
    endif 
    return udg_MissileRect
endfunction

function Missile_SetRectCircle takes integer index returns rect
    local real x = udg_Missile_PosX[index]
    local real y = udg_Missile_PosY[index] 
    local real d = udg_Missile_Collision[index] + Missile_GetMaxCollision()
    call SetRect(udg_MissileRect, x - d, y - d, x + d, y + d)
    return udg_MissileRect
endfunction

function Missile_RunUnitCollision takes integer index returns nothing
    local real    size      = udg_Missile_Collision[index]
    local boolean rectangle = size < udg_Missile_Speed[index]*Missile_GetTimerTimeout()
    local real    collideZ
    local boolean hit 
    local unit    u
    
    if rectangle then
        call GroupEnumUnitsInRect(udg_MissileGroup, Missile_SetRectRectangle(index), null)
    else
        call GroupEnumUnitsInRange(udg_MissileGroup, udg_Missile_PosX[index], udg_Missile_PosY[index], size + Missile_GetMaxCollision(), null)
    endif
        
    loop
        set u = FirstOfGroup(udg_MissileGroup)
        exitwhen u == null
        call GroupRemoveUnit(udg_MissileGroup, u)
        // Chech if missile already hit this unit.
        if not HaveSavedHandle(udg_MissileHash, index, GetHandleId(u)) then
            set hit = false

            // Evaluate rectangle collision.
            if rectangle then 
                set hit = Missile_IsWidgetInRectangleRad(index, u)   
                
            // Evaluate circular collision.
            elseif IsUnitInRange(u, udg_Missile_Dummy[index], size) then
                set collideZ = Missile_GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u) - udg_Missile_TerrainZ[index]
                set hit = (collideZ + Missile_GetUnitBodySize(u) >= udg_Missile_PosZ[index] - size) and (collideZ <= udg_Missile_PosZ[index] + size)
            endif
            
            if hit then
                call SaveUnitHandle(udg_MissileHash, index, GetHandleId(u), u)
                set udg_Missile__UnitHit = u
                if Missile_RunTrigger(index, udg_Missile_OnUnit[index], udg_EVENT_MISSILE_COLLIDE_UNIT) then
                    call Missile_Destroy(index)
                    exitwhen true
                endif
            endif
        endif
    endloop
        
    set u = null
    set udg_Missile__UnitHit = null
endfunction

function Missile_EnumDestructables takes nothing returns nothing
    local integer index = udg_Missile__Index
    local destructable d = GetEnumDestructable() 
    local boolean hit
    
    // Enum trigger action threads can't be canceled. Therefore 
    // the code must check for each trigger execution if the index is still allocated.
    if udg_MissileAllocated[index] and not HaveSavedHandle(udg_MissileHash, index, GetHandleId(d)) then
        set hit = false
            
        // Run rectangle collision.
        if udg_Missile_Collision[index] < udg_Missile_Speed[index]*Missile_GetTimerTimeout() then
            set hit = Missile_IsWidgetInRectangleRad(index, d) 
        else
            // Run circular collision.
            set hit = Missile_IsWidgetInRange(index, d, Missile_GetDestructableHeight(d), udg_Missile_Collision[index])
        endif
            
        if hit then
            call SaveDestructableHandle(udg_MissileHash, index, GetHandleId(d), d)
            set udg_Missile__DestructableHit = d
            if Missile_RunTrigger(index, udg_Missile_OnDest[index], udg_EVENT_MISSILE_COLLIDE_DEST) then
                call Missile_Destroy(index)
            endif
            set udg_Missile__DestructableHit = null
        endif
    endif
    
    set d = null
endfunction

function Missile_RunDestructableCollision takes integer index returns nothing
    set udg_Missile__Index = index
    if udg_Missile_Collision[index] < udg_Missile_Speed[index]*Missile_GetTimerTimeout() then
        call EnumDestructablesInRect(Missile_SetRectRectangle(index), null, function Missile_EnumDestructables)
    else
        call EnumDestructablesInRect(Missile_SetRectCircle(index), null, function Missile_EnumDestructables)
    endif
endfunction

//=============================================================
// Core. 
//=============================================================

function Missile_UpdatePosition takes integer index returns nothing
    local real velocity = udg_Missile_Speed[index]*Missile_GetTimerTimeout()
    local real turn     = udg_Missile_Turn[index]*Missile_GetTimerTimeout()
    local unit missile  = udg_Missile_Dummy[index]
    local real posX     = GetUnitX(missile)
    local real posY     = GetUnitY(missile)
    local real posZ     
    local real angle 
    local real point // Current point on the parabola curve.
    local real pitch
    local real square
    local real distance
    local real curveX
    local real curveY
    
    // Save previous, respectively current missile position.
    // This is required for a possible rectangle collision.
    set udg_Missile_PrevX[index] = posX
    set udg_Missile_PrevY[index] = posY
    
    // For further calculations local real velocity is used instead of speed[index]. 
    set udg_Missile_Speed[index] = udg_Missile_Speed[index] + udg_Missile_Acceleration[index]
    
    // Update missile guidance to its intended target.
    if udg_Missile_Target[index] != null then
        if GetUnitTypeId(udg_Missile_Target[index]) == 0 then
            set udg_Missile_Target[index] = null
        else
            call Missile_Math(index)
        endif
    endif
    
    // For read-abilty improvement, eventually also performance.
    set square = udg_Missile_AB_Square[index]
    set distance = udg_Missile_AB_Distance[index]
    set point = udg_Missile_AB_Traveled[index]

    // Update the missile angle dependant on the turn ratio.
    if turn != 0.00 and Cos(udg_Missile_Angle[index] - udg_Missile_AB_Angle[index]) < Cos(turn) then
        if Sin(udg_Missile_AB_Angle[index] - udg_Missile_Angle[index]) < 0.00 then
            set udg_Missile_Angle[index] = udg_Missile_Angle[index] - turn
        else
            set udg_Missile_Angle[index] = udg_Missile_Angle[index] + turn
        endif
    else
        set udg_Missile_Angle[index] = udg_Missile_AB_Angle[index]
    endif

    // Update the missile point on the parabola.
    set udg_Missile_Recycle[index] = point + velocity >= distance

    if udg_Missile_Recycle[index] then
        set udg_Missile_Distance[index] = udg_Missile_Distance[index] + distance - point
        set point = udg_Missile_AB_Distance[index]
    else
        set udg_Missile_Distance[index] = udg_Missile_Distance[index] + velocity
        set point = point + velocity
    endif
    set udg_Missile_AB_Traveled[index] = point
    
    // Set new position in plane x & y, as well as the facing angle in radians. 
    set angle = udg_Missile_Angle[index]
    set posX = posX + velocity*Cos(angle)
    set posY = posY + velocity*Sin(angle)
    
    // Update point(x/y) if a curving trajectory is defined.
    if udg_Missile_Open[index] != 0.00 then
        set velocity = 4*udg_Missile_Open[index]*point*(distance - point)/square
        set curveX   = velocity*Cos(angle + bj_PI/2) 
        set curveY   = velocity*Sin(angle + bj_PI/2) 
        set posX     = posX + velocity*Cos(angle + bj_PI/2) - udg_Missile_CurveX[index]
        set posY     = posY + velocity*Sin(angle + bj_PI/2) - udg_Missile_CurveY[index]
        set angle    = angle + Atan(-((4*udg_Missile_Open[index])*(2*point - distance))/square)
        // Save the current offset in plane x / y.
        set udg_Missile_CurveX[index] = curveX
        set udg_Missile_CurveY[index] = curveY
    endif
    
    // Update point(x,y,z) if an arc or height is defined.
    set pitch = udg_Missile_AB_Pitch[index]
    set udg_Missile_TerrainZ[index] = Missile_GetLocZ(posX, posY)
    if udg_Missile_Height[index] == 0.00 and pitch == 0.00 then
        set posZ = udg_Missile_OriginZ[index] + udg_Missile_A_Z[index] - udg_Missile_TerrainZ[index]
    else
        set posZ = udg_Missile_OriginZ[index] + udg_Missile_A_Z[index] - udg_Missile_TerrainZ[index] + pitch*point 
        set pitch = Atan(pitch)*bj_RADTODEG
        if udg_Missile_Height[index] != 0.00 then
            set posZ = posZ + 4*udg_Missile_Height[index]*point*(distance - point)/square
            set pitch = pitch - Atan(((4*udg_Missile_Height[index])*(2*point - distance))/square)*bj_RADTODEG
        endif
    endif
    // Update the pitch angle of the dummy unit. 
    if GetUnitTypeId(missile) == udg_Missile__DummyTypeId then
        call SetUnitAnimationByIndex(missile, R2I(pitch + 90.5))
    endif
    
    set udg_Missile_PosX[index] = posX
    set udg_Missile_PosY[index] = posY
    set udg_Missile_PosZ[index] = posZ
    
    // Finally update the missile dummy position and facing angle.
    call SetUnitFacing(missile, angle*bj_RADTODEG)
    call SetUnitFlyHeight(missile, posZ, 0.00)
    if posX < udg_MissileMaxX and posX > udg_MissileMinX and posY < udg_MissileMaxY and posY > udg_MissileMinY then
        call SetUnitX(missile, posX)
        call SetUnitY(missile, posY)
    endif
    
    set missile = null
endfunction

function Missile_Core takes nothing returns boolean
    local integer loops = 0   // Iteration counter.
    local integer limit = 100 // Maximum iterations per trigger evaluate.
    local integer index
    local integer next 
    loop
        exitwhen udg_Missile__Index == 0 
        exitwhen limit == loops
        
        set index = udg_Missile__Index
        set next = Missile_GetListNextNode(index)
        
        if udg_Missile_WantDestroy[index] then
            call Missile_Destroy(index) 
        else
            set udg_Missile__Index = next
            call Missile_UpdatePosition(index)
            set udg_Missile__Index = index
                
            // Run unit collision code.
            if udg_MissileAllocated[index] and udg_Missile_OnUnit[index] != null and udg_Missile_Collision[index] > 0.00 then
                call Missile_RunUnitCollision(index)
            endif

            // Run destructable collision code.
            if udg_MissileAllocated[index] and udg_Missile_OnDest[index] != null and udg_Missile_Collision[index] > 0.00 then
                call Missile_RunDestructableCollision(index)
            endif
        
            // Runs when the impact point is reached.
            if udg_MissileAllocated[index] and udg_Missile_Recycle[index] then
                if udg_Missile_OnFinish[index] != null then
                    if Missile_RunTrigger(index, udg_Missile_OnFinish[index], udg_EVENT_MISSILE_FINISH) then
                        call Missile_Destroy(index)
                    endif
                else
                    call Missile_Destroy(index)
                endif
            endif
            
            // Runs periodically every timer timeout.
            if udg_MissileAllocated[index] and udg_Missile_OnPeriodic[index] != null then 
                if Missile_RunTrigger(index, udg_Missile_OnPeriodic[index], udg_EVENT_MISSILE_PERIODIC) then
                    call Missile_Destroy(index)
                endif
            endif
        endif
        
        set loops = loops + 1
        set udg_Missile__Index = next
    endloop
    
    return udg_Missile__Index == 0
endfunction
         // Missile_GetTimerTimeout()
function Missile_OnPeriodic takes nothing returns nothing
    set udg_Missile__Index = Missile_GetListFirstNode()
    loop
        exitwhen TriggerEvaluate(udg_MissileCore) 
    endloop
    call Missile_ResetScalars()
endfunction

function Missile_InitNewMissile takes integer index returns nothing
    local real originX = GetLocationX(udg_Missile__Origin)
    local real originY = GetLocationY(udg_Missile__Origin)
    local real face    = Atan2(GetLocationY(udg_Missile__Impact) - originY, GetLocationX(udg_Missile__Impact) - originX)*bj_RADTODEG 
        
    // A user may pass his/her own units as projectile dummies.
    if udg_Missile__Dummy == null then
        set bj_lastCreatedUnit = CreateUnit(Missile_GetDummyOwner(), udg_Missile__DummyTypeId, originX, originY, face)
        set udg_Missile__Dummy = bj_lastCreatedUnit// GUI compatibility for function CreateUnitAtLocSaveLast.
        call UnitAddAbility(udg_Missile__Dummy, 'Aloc')
        call PauseUnit(udg_Missile__Dummy, true) 
    endif
    
    // Enables fly height manipulation for the dummy. 
    if UnitAddAbility(udg_Missile__Dummy, 'Amrf') and UnitRemoveAbility(udg_Missile__Dummy, 'Amrf') then
    endif
    call SetUnitFlyHeight(udg_Missile__Dummy, udg_Missile__OriginZ, 0.00)
    
    set udg_Missile_Dummy[index] = udg_Missile__Dummy
    set udg_Missile_Scale[index] = 1.00
    set udg_Missile_Angle[index] = GetUnitFacing(udg_Missile__Dummy)*bj_DEGTORAD
    
    // Prevent the missile from colliding with itself.
    call SaveUnitHandle(udg_MissileHash, index, GetHandleId(udg_Missile__Dummy), udg_Missile__Dummy)
endfunction

function Missile_BuildLocations takes nothing returns boolean
    if udg_Missile__Origin == null then
        // Build from dummy position.
        if udg_Missile__Dummy != null then
            set udg_Missile__Origin = Location(GetUnitX(udg_Missile__Dummy), GetUnitY(udg_Missile__Dummy))
        // Build from source position
        elseif udg_Missile__Source != null then
            set udg_Missile__Origin = Location(GetUnitX(udg_Missile__Source), GetUnitY(udg_Missile__Source))
        else
            call Missile_ErrorMessage(true, "Missile_BuildLocations", "Missile__Origin", udg_Missile__Index, "Missile is unable to build an origin location.
            The process requires either a target or source unit.")
            return false
        endif
    endif
    
    if udg_Missile__Impact == null then
        // Build from target position.
        if GetUnitTypeId(udg_Missile__Target) != 0 then
            set udg_Missile__Impact = Location(GetUnitX(udg_Missile__Target), GetUnitY(udg_Missile__Target))
        else
            // Build from origin using angle ( radians! ) and distance.
            set udg_Missile__Impact = PolarProjectionBJ(udg_Missile__Origin, udg_Missile__Distance, udg_Missile__Angle*bj_RADTODEG)        
        endif
    endif
    return true
endfunction

// Action function of trigger Missile.
function Missile_Launch takes nothing returns nothing
    local integer index = udg_Missile__Index 
    
    if not udg_MissileAllocated[index] or udg_MissileLaunched[index] then
        
        // This is an invalid situation caused by faulty
        // usage of the Missile API. Missile_Launch was executed
        // without a previous execution of Missile_Create.
        
        // Error identification.
        call Missile_ErrorMessage(udg_MissileLaunched[index], "Missile_Launch", "Missile_Launched", index, "Attempt to double launch a missile!
    Make sure you use Run Missile Configurate <gen> - Your missile settings - Run Missile <gen> syntax.")
    
        call Missile_ErrorMessage(not udg_MissileAllocated[index], "Missile_Launch", "Missile__Index ", index, "Attempt to launch invalid missile index!
    Make sure you use Run Missile Configurate <gen> - Your missile settings - Run Missile <gen> syntax.")

    // Check if Missile has to build an origin or impact location.
    elseif ((udg_Missile__Origin == null) or (udg_Missile__Impact == null)) and not Missile_BuildLocations() then
        call Missile_Destroy(index)
        
    else
        set udg_MissileLaunched[index] = true    
        if Missile_GetListFirstNode() == 0 then
            call TimerStart(udg_MissileTimer, Missile_GetTimerTimeout(), true, function Missile_OnPeriodic)
        endif
        call Missile_ListPushNode(index)
        call Missile_InitNewMissile(index)
        call Missile_SaveUserData(index)
    endif
    
    call Missile_ResetScalars()
    if udg_Missile__EventIndex != 0 then
        call Missile_LoadData(udg_Missile__EventIndex)
    endif
endfunction

// Action function of trigger Missile Configurate.
function Missile_Create takes nothing returns nothing
    // In most cases udg_Missile__Index is 0, thus not allocated.
    if udg_MissileAllocated[udg_Missile__Index] then
    
        // Check if Missile_Create runs from inside a Missile event trigger.
        if udg_Missile__EventIndex == udg_Missile__Index then
            // Save all data from scalar to array variables.
            call Missile_SaveUserData(udg_Missile__EventIndex)
    
        elseif not udg_MissileLaunched[udg_Missile__Index] then
                    
            // This is an invalid situation caused by faulty
            // usage of the Missile API. Missile_Create was executed twice
            // without launching the previously created index.
            // Missile compensates for that error by destroying
            // the forgotten index.
            call Missile_Destroy(udg_Missile__Index)
            
            // Print it for the user.
            call Missile_ErrorMessage(true, "Missile_Create", "Missile_Launched", udg_Missile__Index, "You forgot to launch the previous created missile!
    Make sure you use Run Missile Configurate <gen> - Your missile settings - Run Missile <gen> syntax.")
        
        endif
    endif
    
    call Missile_ResetScalars()
    set udg_Missile__Index = Missile_AllocateNode()
endfunction

//===========================================================================
function InitTrig_Missile takes nothing returns nothing
    if gg_trg_Missile != null then
        return// Missile has already been initialized.
    endif
    
    set udg_MissileHash = InitHashtable()
    set udg_MissileCore = CreateTrigger()
    set udg_MissileRect = GetWorldBounds()
    set udg_MissileLocZ = Location(0.00, 0.00)
    
    // It's necessary to prevent missiles, units in general, from leaving map boundaries.
    set udg_MissileMaxX = R2I(GetRectMaxX(udg_MissileRect)) - 197
    set udg_MissileMaxY = R2I(GetRectMaxY(udg_MissileRect)) - 197
    set udg_MissileMinX = R2I(GetRectMinX(udg_MissileRect)) + 197
    set udg_MissileMinY = R2I(GetRectMinY(udg_MissileRect)) + 197
    
    // Trigger that controlls the trajectory of all missiles.
    call TriggerAddCondition(udg_MissileCore, Condition(function Missile_Core))
    
    // Run the configuration trigger before the first missile is created.
    call TriggerExecute(gg_trg_Missile_Configurate)
    call TriggerClearActions(gg_trg_Missile_Configurate)
    call DestroyTrigger(gg_trg_Missile_Configurate)
    
    // Transform the configurate trigger into the index allocator.
    set gg_trg_Missile_Configurate = CreateTrigger()
    call TriggerAddAction(gg_trg_Missile_Configurate, function Missile_Create)
    
    // Build the launching trigger for missiles.
    set gg_trg_Missile = CreateTrigger()
    call TriggerAddAction(gg_trg_Missile, function Missile_Launch)

    // Set the missile system ready.
    call Missile_ResetScalars()
endfunction


Changelog:

Version 1.1
  • Missile now properly restores all event variables to the triggering index,
    if you create and launch a new missile within a trigger executed from missile.
  • Missile__Arc now takes degrees instead of radians.
    That's essential considering the maximum amount of digits in GUI variables
  • Missile__Curve also takes now degrees instead of radians ( same reason as arc )
  • Missile__Turn should now be set to desired turn angle per second.
    Before it was turn angle per tick. ( same reason as arc, also more read-able.
  • There was a chance for a game crash in function Missile_UpdatePosition().
    That has been fixed now.
Version 1.2
  • Fixed fly time.
  • Fixed curve.
Version 1.3
  • Seperated the parent key of hit widgets from the parent key used to load missile properties.
    Users may flush/remove entries from the widget table
  • udg_Missile__WidgetKey is the integer key to access the widget table.
  • Added a dummy delayed dummy killing stack, as timed life seems to heavily drop fps.
  • Improved overall code.
  • Missile__FlyTime has been re-named to Missile__ConvertFlyTimeToSpeed
Version 1.4
  • Replaced hashtable lookup with arrays.
Version 1.5
  • Completed variabled generator trigger.
  • Improved a few things here and there.
Version 1.6.1
  • Improved documentation and OP limit safety.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
OMG you made a GUI version?
It has less features than the "big brother", for example you can only
read and manipulate missile properties inside an event trigger of that missile.
But the code of the GUI version is probably more clean. It was really fun to code.

Does it... does it handle arcs and curves when it homes in on a target?
Either homing or arc/curve combined is not possible.

anyways, It seems that you are not using a recycler for dummies?
This one is designed for GUI users.
For full missile controll and handle recycling at its best I recommend using The Big One.
I'm not convinced that there should be a dummy recycler hardcoded inside.
The Create/RemoveUnit is a bit expensive, but the entire GUI is more or less expensive.
The documentated memory leak of unit handles is microscopically small or not existent in the current patch.

wow!!
very nice work, very useful for GUI user.
5/5
Thanks.

I still must improve the demo map, also a run stress tests to check the limits of the system.
The hashtable load/save will suffer a bit performance against arrays used in the vJass version.
 
I still must improve the demo map, also a run stress tests to test the limits of the system.

Hotkeys for the spells, and make the Blood Mage more resistant

Btw, how difficult would it be to have an integrated bounce system in Missile? I see this one has a missile that returns to the caster, which got me thinking about additional features. No saying you should add them, btw, merely curious :p
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Btw, how difficult would it be to have an integrated bounce system in Missile?
I would stay with the vJass version. Is you code still posted here? Then I will check it.

I see this one has a missile that returns to the caster, which got me thinking about additional features.
It's simply sets homing to the cast target, once there to the caster,
then again to a random target, ect ... for x times.
I added a turn rate of 8/32 ( 8 per timer tick ) so homing has better visuals.

Hotkeys for the spells, and make the Blood Mage more resistant
Amoung other things. I will do it today in the evening.
 
I would stay with the vJass version. Is you code still posted here? Then I will check it.
I took it down. My filtering was jank, and there were a lot of broken things with it - like the Spell Breaker's issues, and the fact that splits were just refusing to work. And then some.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I took it down. My filtering was jank, and there were a lot of broken things with it - like the Spell Breaker's issues, and the fact that splits were just refusing to work. And then some.
I see.
---

I will make an update to version 1.1 to improve how missile events are working.
Currently you will lose all values if you create a new missile within an
trigger action function.
In the next update these values will be restored properly after launching
the newly created missile index.

---

Updated to version 1.1 & 1.2
Changelog is in the first comment.

Now comes the demo map and I'll improve the documentation of the JASS code.
So finally there will be also a version 1.3 :)

----

I changed the text of rule #1.
You can remove location udg_Missile__Origin or udg_Missile__Impact inside a trigger action function,
if you set it to a new location before the function ends.

----

Updated to version 1.3 ( Changelog in comment #1 )

I checked the code around 5 times and it seems to be as good as it can get.
I've ran a performance test with the follwong outcome:
Note that my laptop CPU is quite powerful, so your results may be different.

Test results when having all missiles on screen:
For missiles off screen the number is much higher, as their visuals are not rendered then.

Missile GUI is around 4-x times slower than Missile vJass.
You can measure that in fps after 100+ missiles during the same time.

Missile GUI starts dropping fps from 60 to 40 at around 100+ missiles ( for me ).
Missile vJass at around 350+

That comes mainly from TriggerEvaluate + TriggerExecute ( GUI ) vs function calls ( vJass).
Also Missile GUI loads data from a hashtable and requires a few more native function calls per iteration
Simply because I didn't want to spam array variables in the Variable editor.

The slowpoke is the high frequent trigger execution ....
Missile GUI is still really performant compared to ordinary GUI code.
 
Last edited:
Level 11
Joined
Dec 3, 2011
Messages
366
I see.
---

I will make an update to version 1.1 to improve how missile events are working.
Currently you will lose all values if you create a new missile within an
trigger action function.
In the next update these values will be restored properly after launching
the newly created missile index.

---

Updated to version 1.1 & 1.2
Changelog is in the first comment.

Now comes the demo map and I'll improve the documentation of the JASS code.
So finally there will be also a version 1.3 :)

----

I changed the text of rule #1.
You can remove location udg_Missile__Origin or udg_Missile__Impact inside a trigger action function,
if you set it to a new location before the function ends.

----

Updated to version 1.3 ( Changelog in comment #1 )

I checked the code around 5 times and it seems to be as good as it can get.
I've ran a performance test with the follwong outcome:
Note that my laptop CPU is quite powerful, so your results may be different.

Test results when having all missiles on screen:
For missiles off screen the number is much higher, as their visuals are not rendered then.

Missile GUI is around 4-x times slower than Missile vJass.
You can measure that in fps after 100+ missiles during the same time.

Missile GUI starts dropping fps from 60 to 40 at around 100+ missiles ( for me ).
Missile vJass at around 350+

That comes mainly from TriggerEvaluate + TriggerExecute ( GUI ) vs function calls ( vJass).
Also Missile GUI loads data from a hashtable and requires a few more native function calls per iteration
Simply because I didn't want to spam array variables in the Variable editor.

The slowpoke is the high frequent trigger execution ....
Missile GUI is still really performant compared to ordinary GUI code.

How about storing missile data in a struct instead of using hashtable. I think It's better ^^!

Anyway, very nice!
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
How about storing missile data in a struct instead of using hashtable. I think It's better ^^!

Anyway, very nice!
Structs came with the implementation of the JassHelper into JNGP, therefore
they need JNGP to compile. That is in my eyes contra-productive as the the target
group, GUI users, partly don't use JNPG. I'm not a supporter of hybrid GUI - vJass code.

What you actually ( unintentionally? ) mean is using arrays over hashtable lookups.
True that would boost the performance, probably by quite a lot. Doesn't mean it makes the system better.
However trades in my eyes performance for read-ability and easy usage.
Keep in mind this system is designed for usage in GUI and doesn't have to be super fast.

Missile configuration would look like

set Missile__Source[Missile__Index] = TriggeringUnit
set Missile__Damage[Missile__Index] = 100

At the moment it is:

set Missile__Source = TriggeringUnit
set Missile__Damage = 100

The same syntax exists in every missile event trigger.
It would require more logic from the user.

I was hoping for input from our dedicated JASS/GUI coders,
which they think is the best approach.

Using arrays would come along with the advantage of
beeing able to easier transfer data from missile A to missile B in event triggers.

set Missile__Source[Missile__Index] = Missile__Source[Missile__EventIndex]

However you could also approach that by loading from the hashtable
or using local or global extra variables of equal type.
 
Last edited:
Level 11
Joined
Dec 3, 2011
Messages
366
Structs came with the implementation of the JassHelper into JNGP, therefore
they need JNGP to compile. That is in my eyes contra-productive as the the target
group, GUI users, partly don't use JNPG. I'm not a supporter of hybrid GUI - vJass code.

What you actually ( unintentionally? ) mean is using arrays over hashtable lookups.
True that would boost the performance, probably by quite a lot. Doesn't mean it makes the system better.
However trades in my eyes performance for read-ability and easy usage.
Keep in mind this system is designed for usage in GUI and doesn't have to be super fast.

Missile configuration would look like

set Missile__Source[Missile__Index] = TriggeringUnit
set Missile__Damage[Missile__Index] = 100

At the moment it is:

set Missile__Source = TriggeringUnit
set Missile__Damage = 100

The same syntax exists in every missile event trigger.
It would require more logic from the user.

I was hoping for input from our dedicated JASS/GUI coders,
which they think is the best approach.

Using arrays would come along with the advantage of
beeing able to easier transfer data from missile A to missile B in event triggers.

set Missile__Source[Missile__Index] = Missile__Source[Missile__EventIndex]

However you could also approach that by loading from the hashtable
or using local or global extra variables of equal type.

Yes ^^! I don't like use hashtable coz of its performance and limit. I used to make a lot of GUI resources, I know what you do and the problem of game fps dropping come from trigger execute and evaluate ^^!

I'm still using your vJass missile it's really nice jaja/

Anyway, thanks ^^!
 
Level 7
Joined
Jun 14, 2009
Messages
235
Is there a way to make a missile damage units only once on impact? Like damaged units to a group so if they're "hit" by the missile again they dont take damage twice.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Is there a way to make a missile damage units only once on impact? Like damaged units to a group so if they're "hit" by the missile again they dont take damage twice.
Yes of course.

But it's a bit different than you ( probably ) think.
Missile do not deal damage at all. At least not internally.
A missile just executes an on unit collision event. ( Example code is in the description or demo map. )
So when a missile comes into collision range of a unit, it will run the set Missile__OnCollideUnit trigger.
You can use variable Missile__Damage to save the desired amount of damage when creating the missile,
on collide event you can read the previously saved variable value to run the unit damage target function.

By default a missile only collides once with a widget ( unit, destructable ),
during the fly time a second collision event is no longer possible,
because a missile remembers all widgets it has hit in it's memory.

Multiple collision event for one widget ( unit, destructable ) can be achieved by removing the hit widget,
from the memory of the missile. That requires knowledge about hashtable API and custom script.
call RemoveSavedHandle(udg_Missile_Hash, Missile__Index, GetHandleId(widget))

@Moderators / Reviewers. Please don't approve this yet, I would like to discuss first if you think
that I should switch from hashtable to arrays and use a array based API for users instead.

Edit: I'm gonna update and remove the hashtable lookup in favour of arrays.
 
Last edited:
I don't see code, but myself would totaly accept hashtable for this I think. :)

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

What I'm thinking, but honestly I don't know if it's anyways needed at all with proper structering,
is that if it's possible to retrieve the initial event missle inside a "trigger action function" after a new missle is created within.

Like inside the action function to do something like:

  • Trigger - Run Missile Configurate <gen> (ignoring conditions)
  • Set Missile__Origin = (Point((X of Missile__TempLoc), (Y of Missile__TempLoc)))
  • Set Missile__Impact = (Missile__Origin offset by 450.00 towards (60.00 x (Real((Integer A)))) degrees)
  • Set Missile__ImpactZ = 50.00
  • Set Missile__Height = 350.00
  • Set Missile__OriginZ = 50.00
  • Set Missile__Speed = 500.00
  • Set Missile__Damage = 150.00
  • Set Missile__Source = Temp_Unit
  • Set Missile__Owner = Player 2 <-------------------------------------------------- watch this
  • Set Missile__Model = Abilities\Weapons\FireBallMissile\FireBallMissile.mdl
  • Set Missile__OnCollideUnit = Fireball Actions <gen>
  • Set Missile__OnRemove = Fireball Actions <gen>
  • Set Missile__Data = 2
  • Trigger - Run Missile <gen> (ignoring conditions)
  • ...
  • ...
  • Now we want to use the initial owner again and so we do
  • ...
  • ...
  • Custom script: call GetActionMissleData()
  • ...
After the custom function is called the "Missile__Owner" would not be "Player 2" as in the creation code, but be the true owner again of the missle that fired the event.

As I'm writing this I get unsure if this may be useful in some case. ;D
But I will think of it again or maybe for an example and just let it stay for you, too.^^

Edit:

I can't explain my imaginary examples, but I think my thought for implementation is not needed, and may be avoided with proper structure a help variable in cases. ;s
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
What I'm thinking, but honestly I don't know if it's anyways needed at all with proper structering,
is that if it's possible to retrieve the initial event missle inside a "trigger action function" after a new missle is created within.
But I understand it right away. The answer is yes and no.
Of course the value of the scalar variables are cleared when you create a new missile,
therefore you can't transfer data from missile A to missile B.

You could load it directly from the hashtable, but that's very unhandy, as
the parent keys are not represented by global variables.
You could also use helper variables ot locals to save data before creating a new missile.


I' updating to arrays. It's already written, but not optimized yet.
Then you will be able to transfer data from missile A to missile B.

set Missile__Owner = Missile_Owner[Missile__EventIndex]

Missile__Index will always represent the array index of event index,
until a new missile is created from that function.
Missile__EventIndex is always the triggering missile index.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've updated to array lookups to improve performance and etc...

I know that the scalar variables are very appreciated for GUI usage, but
also result in performance loss due to transfering data per event evaluation
from arrays to scalars then back to arrays.

I'm talking about
JASS:
function Missile_SaveUserData takes integer index returns nothing
    local boolean math = false
    
    set udg_Missile_OnUnit[index]       = udg_Missile__OnCollideUnit
    set udg_Missile_OnDest[index]       = udg_Missile__OnCollideDestructable
    set udg_Missile_OnPeriodic[index]   = udg_Missile__OnPeriodic
    set udg_Missile_OnFinish[index]     = udg_Missile__OnFinish
    set udg_Missile_OnRemove[index]     = udg_Missile__OnRemove
    set udg_Missile_Source[index]       = udg_Missile__Source 
    set udg_Missile_Target[index]       = udg_Missile__Target
    set udg_Missile_Owner[index]        = udg_Missile__Owner
    set udg_Missile_Data[index]         = udg_Missile__Data
    set udg_Missile_Angle[index]        = udg_Missile__Angle
    set udg_Missile_Distance[index]     = udg_Missile__Distance
    set udg_Missile_Collision[index]    = udg_Missile__Collision
    set udg_Missile_WantDestroy[index]  = udg_Missile__WantDestroy
    set udg_Missile_Damage[index]       = udg_Missile__Damage
    set udg_Missile_Acceleration[index] = udg_Missile__Acceleration
    set udg_Missile_Turn[index]         = udg_Missile__Turn
    
    // The user set a new origin point.
    if udg_Missile__Origin != null then
        set udg_Missile_OriginX[index] = GetLocationX(udg_Missile__Origin) 
        set udg_Missile_OriginY[index] = GetLocationY(udg_Missile__Origin) 
        call RemoveLocation(udg_Missile__Origin)
        set udg_Missile__Origin = null
        set math = true
    endif
    if udg_Missile_OriginZ[index] != udg_Missile__OriginZ then
        set udg_Missile_OriginZ[index] = udg_Missile__OriginZ
        set math = true
    endif

    if udg_Missile__Impact != null then
        set udg_Missile_ImpactX[index] = GetLocationX(udg_Missile__Impact) 
        set udg_Missile_ImpactY[index] = GetLocationY(udg_Missile__Impact)
        call RemoveLocation(udg_Missile__Impact)
        set udg_Missile__Impact = null
        set math = true
    endif
    if udg_Missile_ImpactZ[index] != udg_Missile__ImpactZ then
        set udg_Missile_ImpactZ[index] = udg_Missile__ImpactZ
        set math = true
    endif
    if math then
        call Missile_Math(index)
    endif

    // The user defined a new model.
    if udg_Missile_Model[index] != udg_Missile__Model then
        set udg_Missile_Model[index] = udg_Missile__Model
        if udg_Missile_Effect[index] != null then
            call DestroyEffect(udg_Missile_Effect[index])
        endif
        if StringLength(udg_Missile__Model) > 0 then
            set udg_Missile_Effect[index] = AddSpecialEffectTarget(udg_Missile__Model, udg_Missile_Dummy[index], "origin")
        else
            set udg_Missile_Effect[index] = null
        endif
    endif
    
    // Default scaling on Missile_Create is 1.00.
    if udg_Missile_Scale[index] != udg_Missile__Scale then
        set udg_Missile_Scale[index] = udg_Missile__Scale
        call SetUnitScale(udg_Missile_Dummy[index], udg_Missile__Scale, 0., 0.)
    endif
    
    if udg_Missile_Curve[index] != udg_Missile__Curve then
        set udg_Missile_Curve[index] = udg_Missile__Curve
        set udg_Missile_Open[index] = Missile_GetCurve(index)
    else
        set udg_Missile_Open[index] = udg_Missile__Open
    endif

    if udg_Missile_Arc[index] != udg_Missile__Arc then
        set udg_Missile_Arc[index] = udg_Missile__Arc
        set udg_Missile_Height[index] = Missile_GetArc(index)
    else
        set udg_Missile_Height[index] = udg_Missile__Height
    endif

    if udg_Missile_FlyTime[index] != udg_Missile__FlyTime then
        set udg_Missile_FlyTime[index] = udg_Missile__FlyTime
        set udg_Missile_Speed[index] = Missile_GetFlyTime(index)
    else
        set udg_Missile_Speed[index] = udg_Missile__Speed
    endif
endfunction

function Missile_LoadData takes integer index returns nothing
    set udg_Missile__Index                 = index

    // Load event triggers.
    set udg_Missile__OnCollideUnit         = udg_Missile_OnUnit[index]
    set udg_Missile__OnCollideDestructable = udg_Missile_OnDest[index]
    set udg_Missile__OnPeriodic            = udg_Missile_OnPeriodic[index]
    set udg_Missile__OnFinish              = udg_Missile_OnFinish[index]
    set udg_Missile__OnRemove              = udg_Missile_OnRemove[index]
    
    // Load other handle types.
    set udg_Missile__Dummy                 = udg_Missile_Dummy[index]
    set udg_Missile__Source                = udg_Missile_Source[index] 
    set udg_Missile__Target                = udg_Missile_Target[index]
    set udg_Missile__Owner                 = udg_Missile_Owner[index]
    
    set udg_Missile__Damage                = udg_Missile_Damage[index]
    set udg_Missile__Collision             = udg_Missile_Collision[index] 
    set udg_Missile__Model                 = udg_Missile_Model[index] 
    set udg_Missile__Data                  = udg_Missile_Data[index]
    set udg_Missile__Scale                 = udg_Missile_Scale[index]
    set udg_Missile__WantDestroy           = udg_Missile_WantDestroy[index]
    
    set udg_Missile__Speed                 = udg_Missile_Speed[index]
    set udg_Missile__Acceleration          = udg_Missile_Acceleration[index]
    set udg_Missile__Open                  = udg_Missile_Open[index]
    set udg_Missile__Turn                  = udg_Missile_Turn[index]
    set udg_Missile__Height                = udg_Missile_Height[index]
    set udg_Missile__Angle                 = udg_Missile_Angle[index]
    set udg_Missile__Distance              = udg_Missile_Distance[index]
    set udg_Missile__Arc                   = udg_Missile_Arc[index]
    set udg_Missile__Curve                 = udg_Missile_Curve[index]
    set udg_Missile__FlyTime               = udg_Missile_FlyTime[index]
    
    set udg_Missile__OriginZ               = udg_Missile_OriginZ[index]
    set udg_Missile__ImpactZ               = udg_Missile_ImpactZ[index]
endfunction

What do you think?
 
Level 7
Joined
Jun 14, 2009
Messages
235
Yes of course.

But it's a bit different than you ( probably ) think.
Missile do not deal damage at all. At least not internally.
A missile just executes an on unit collision event. ( Example code is in the description or demo map. )
So when a missile comes into collision range of a unit, it will run the set Missile__OnCollideUnit trigger.
You can use variable Missile__Damage to save the desired amount of damage when creating the missile,
on collide event you can read the previously saved variable value to run the unit damage target function.

By default a missile only collides once with a widget ( unit, destructable ),
during the fly time a second collision event is no longer possible,
because a missile remembers all widgets it has hit in it's memory.

Multiple collision event for one widget ( unit, destructable ) can be achieved by removing the hit widget,
from the memory of the missile. That requires knowledge about hashtable API and custom script.
call RemoveSavedHandle(udg_Missile_Hash, Missile__Index, GetHandleId(widget))

@Moderators / Reviewers. Please don't approve this yet, I would like to discuss first if you think
that I should switch from hashtable to arrays and use a array based API for users instead.

Edit: I'm gonna update and remove the hashtable lookup in favour of arrays.

Ok thanks! So if I just run it off of the on_collison trigger I'll be fine? Ok!


Also I update t 1.4 and now I'm getting an error.
I don't hink some of the variables got created.

EDIT: Yep it seems like all the errors were due to you switching to Arrays. I had to manually import like 20 variables
 
Last edited:
Level 7
Joined
Jun 14, 2009
Messages
235
I also am curious if theres a way to reference the missile as it is created.

Essentially Add Last Created Unit to Unit Group doesn't work in the same trigger the missiles are generated.

Would it be possible to add a "On Creation" event similar to the "On Finish" events? Otherwise I might have to create a periodic and do some funky stuff,
 
I also am curious if theres a way to reference the missile as it is created.

Essentially Add Last Created Unit to Unit Group doesn't work in the same trigger the missiles are generated.

Would it be possible to add a "On Creation" event similar to the "On Finish" events? Otherwise I might have to create a periodic and do some funky stuff,

Maybe there is variable in the system that refers to it directly, no?
 
Level 7
Joined
Jun 14, 2009
Messages
235
Maybe there is variable in the system that refers to it directly, no?

Missile__Dummy? If that's what you mean then it isn't what I need. I need to reference the missile as a dummy unit in the same trigger that it is created.

Because of the way the system is designed, you don't manually create the dummy unit, so I can't reference it with (Last Created Unit) as easily. I'm wondering if there's a way to do this already.
 
Missile__Dummy? If that's what you mean then it isn't what I need. I need to reference the missile as a dummy unit in the same trigger that it is created.

Because of the way the system is designed, you don't manually create the dummy unit, so I can't reference it with (Last Created Unit) as easily. I'm wondering if there's a way to do this already.

Just use Custom Script to refer it. Last Created Unit just doesn't refer to the same trigger :)
 
Level 7
Joined
Jun 14, 2009
Messages
235
Just use Custom Script to refer it. Last Created Unit just doesn't refer to the same trigger :)

Could you give an example? I tried referencing Missile__Dummy in the same trigger the missile is created and that doesn't work.

I basically want to be able to do this:
  • Trigger - Run Missile <gen> (ignoring conditions)
  • Unit - Remove (Last created unit) from the game
That isn't actually what I'm trying to do, but if I could reference (Last Created Unit) then I could do what I want to, and more (including the above trigger)
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
@Killd0zer: Give me some time to rule out all bugs of the current version,
after all it's still a pending submission and not guaranteed to work.
I'll try to get everything working until the weekend.

It's true, I didn't update the variable creator after switching to arrays.

That isn't actually what I'm trying to do, but if I could reference (Last Created Unit) then I could do what I want to, and more (including the above trigger)
There are ways ( a bit tricky ) to get reference of the last created missile.
I will consider implementing the lastCreatedUnit global aswell.
 
Level 7
Joined
Jun 14, 2009
Messages
235
@Killd0zer: Give me some time to rule out all bugs of the current version,
after all it's still a pending submission and not guaranteed to work.
I'll try to get everything working until the weekend.

It's true, I didn't update the variable creator after switching to arrays.


There are ways ( a bit tricky ) to get reference of the last created missile.
I will consider implementing the lastCreatedUnit global aswell.

You are the best!
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've updated to version 1.5
  • The trigger for variable generation is now complete.
    ---
  • I've added compatibility to CreateUnitAtLocSaveLast for missiles, that means you may use LastCreatedUnit after running the Missile_Create trigger.
    ---
  • You can now also set an impact z offset for homing missiles.
    ---
  • I changed the variable naming:
    1. No underscore are private and in general shouldn't be accessed by users.
    2. One underscore _ are arrays which can read at any time.
    3. Two underscores __ are scalars which represent the user API.
 
Level 7
Joined
Jun 14, 2009
Messages
235
I've updated to version 1.5
  • The trigger for variable generation is now complete.
    ---
  • I've added compatibility to CreateUnitAtLocSaveLast for missiles, that means you may use LastCreatedUnit after running the Missile_Create trigger.
    ---
  • You can now also set an impact z offset for homing missiles.
    ---
  • I changed the variable naming:
    1. No underscore are private and in general shouldn't be accessed by users.
    2. One underscore _ are arrays which can read at any time.
    3. Two underscores __ are scalars which represent the user API.

Gonna download right now and test!

EDIT: Hmm Last created Unit still isn't working for me. I could be doing it wrong

  • Trigger - Run Missile <gen> (ignoring conditions)
  • Game - Display to (All players) the text: (String((Key (Last created unit))))
This isn't working for me
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Meanwhile I've updated to v.1.5.1 because there was a small flaw with homing missiles.
I've also added some examples to the demo map.

For me the Last Created Unit field is working

  • Click Wisp
    • Events
    • Conditions
    • Actions
      • Trigger - Run Missile Configurate <gen> (ignoring conditions)
      • Set Missile__Origin = (Position of Wisp 0024 <gen>)
      • Set Missile__OriginZ = 0.00
      • Set Missile__Impact = (Position of Footman 0018 <gen>)
      • Set Missile__ImpactZ = 55.00
      • Set Missile__Speed = 500.00
      • Set Missile__Model = Abilities\Weapons\Banditmissile\Banditmissile.mdl
      • Set Missile__Target = Footman 0018 <gen>
      • Set Missile__Turn = 2.00
      • Trigger - Run Missile <gen> (ignoring conditions)
      • Game - Display to (All players) the text: (Name of (Last created unit))
Did you set the Missile__DummyUnitTypeId in the config trigger to the correct unit id?
 
Level 7
Joined
Jun 14, 2009
Messages
235
I'm not sure why but it just isn't working for me.

  • Set TempReal = ((Angle from Spell__CastPoint to Spell__TargetPoint) - 30.00)
  • For each (Integer A) from 1 to 5, do (Actions)
    • Loop - Actions
      • Trigger - Run Missile Configurate <gen> (ignoring conditions)
      • Set Missile__Source = Spell__Caster
      • Set Missile__Owner = Spell__CasterOwner
      • Set Missile__Origin = ((Position of Missile__Source) offset by 70.00 towards (TempReal + (10.00 x (Real((Integer A))))) degrees)
      • Set Missile__Impact = (Missile__Origin offset by 500.00 towards (TempReal + (10.00 x (Real((Integer A))))) degrees)
      • Set Missile__ImpactZ = 50.00
      • Set Missile__OriginZ = 50.00
      • Set Missile__Speed = 1300.00
      • Set Missile__Collision = 48.00
      • Set Missile__Damage = 0.00
      • Set Missile__Model = Abilities\Weapons\WardenMissile\WardenMissile.mdl
      • Set Missile__OnCollideUnit = Hidden Blades Collision <gen>
      • Trigger - Run Missile <gen> (ignoring conditions)
      • Game - Display to (All players) the text: (String((Key (Last created unit))))
      • -------- --------
Also got an error on this line:

call GroupEnumUnitsInRange(udg_MissileGroup, udg_Missile_PosX[index], udg_Missile_PosY[index], size + Missile_GetMaxCollision, null)
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Indeed there is a mistake, I don't know why my JassHelper doesn't detect it.
It has to be Missile_GetMaxCollision()

I made a hotfix.

  • Game - Display to (All players) the text: (String((Key (Last created unit))))
^I'm not using GUI that often. Can you tell me where the Key(unit) is located in the GUI editor fields?
I only found Get Handle ID, which only accepts handle type variables.
 
Level 7
Joined
Jun 14, 2009
Messages
235
Indeed there is a mistake, I don't know why my JassHelper doesn't detect it.
It has to be Missile_GetMaxCollision()

I made a hotfix.

Idk what type of magic you just used, but version 1.5.1 just works for some reason now! It's WORKING :DDDDDDDD
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
  • Game - Display to (All players) the text: (String((Key (Last created unit))))
^I'm not using GUI that often. Can you tell me where the Key(unit) is located in the GUI editor fields?
I only found Get Handle ID, which only accepts handle type variables.

If I'm not mistaken, JNGP doesn't have the Key() functions. You will have to use the vanilla editor for that. That is why some JNGP users who work with GUI hashtables use more custom scripts than a usual GUI user.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've updated again to version 1.6. I had a in my opinion good idea.
In version 1.5.1 and previous you must set an origin and impact location.
From version 1.6 on Missile can build those locations for you under certain circumstances.

Missile__Origin can be build from
  • Missile__Dummy ( You passed in the dummy unit yourself )
  • Missile__Source ( Coordinates of the source unit )

Missile__Impact can be build from
  • Missile__Target ( Coordiantes of the target unit )
  • Missile__Distance + Missile__Angle ( in radians )

JASS:
function Missile_BuildPoints takes nothing returns boolean
    if udg_Missile__Origin == null then
        if udg_Missile__Dummy != null then
            set udg_Missile__Origin = Location(GetUnitX(udg_Missile__Dummy), GetUnitY(udg_Missile__Dummy))
        elseif udg_Missile__Source != null then
            set udg_Missile__Origin = Location(GetUnitX(udg_Missile__Source), GetUnitY(udg_Missile__Source))
        else
            call Missile_ErrorMessage(true, "Missile_BuildPoints", "Missile__Origin", udg_Missile__Index, "Missile is unable to build
    an origin location.")
            return false
        endif
    endif
    if udg_Missile__Impact == null then
        if GetUnitTypeId(udg_Missile__Target) != 0 then
            set udg_Missile__Impact = Location(GetUnitX(udg_Missile__Target), GetUnitY(udg_Missile__Target))
        else
            set udg_Missile__Impact = PolarProjectionBJ(udg_Missile__Origin, udg_Missile__Distance, udg_Missile__Angle*bj_RADTODEG)        
        endif
    endif
    return true
endfunction
 
Level 7
Joined
Jun 14, 2009
Messages
235
If I'm not mistaken, JNGP doesn't have the Key() functions. You will have to use the vanilla editor for that. That is why some JNGP users who work with GUI hashtables use more custom scripts than a usual GUI user.

I was using the GUI function and its working now!!
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Why not merge the behavior of the two triggers "Missile Configurate" and "Missile", so that the user only has to run one of them to create a missile (once all the variables are set)?
Because the scalar variables serve for set and read data.
Before a missile event trigger fires, for example on unit collision, the data
is copied from array to scalar to serve as user API.
So a user can read i.e. Missile__Source within the trigger.


Before creating a new missile all scalars have to be set to default ( 0.00, 0 and null )
otherwise it's not obvious if the setup ( source, speed, damage, ... ) is from the event missile or the new one.

Trigger Missile Config resets all to null and allocates a new index.
Trigger Missile evaluates the user settings, launches the missile and
eventually restores the scalars to current running event index.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Then I would need an entire set of variables serving as event variables.
1.)Set of scalar variables for missile creation.
2.)Set of array variables for MUI.
3.)Set of scalar variables for missile events.
Looks like an overkill to me, as a missile has a lot of properties and each needs a variable.

Currently I just have 1.) and 2.).
As JASSer you don't have to use execute Trigger Missile Config - settings - Trigger Missile,
instead you can simply call function Missile_Create - settings - call Missile_Launch.
The vJass version of Missile works in the same way, however the create function
takes a couple of arguments.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Having extra variables is a consequence of building a resource in GUI. It is so time consuming conpared to vJass where you can just put a private variable wherever you want it.

What you could also do is just have one extra variable: PrevMissile, which stores the index of the last event missile. When a new missile is created and PrevMissile is not equal to 0, that means the scalars need to be reset once the missile is fired. You would just pull them off of the prevmissile index. This way, you achieve fewer variables while also only needing the user to run one trigger.
 
Level 7
Joined
Jun 14, 2009
Messages
235
How do I reference the Angle that the missile was using in an OnCollide trigger?

I tried Missile__Angle and I kept getting a weird number (between ~-3 to ~3)
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Yes in radians.
JASS:
// Variables for read-only:
//
//    integer udg_Missile__EventId     
//    integer udg_Missile__EventIndex  
//    unit    udg_Missile__Dummy           -    unit array udg_Missile_Dummy
//    real    udg_Missile__Angle           -    real array udg_Missile_Angle    ( In radians )
//    real    udg_Missile__Distance        -    real array udg_Missile_Distance ( Total distance traveled )
//
Full list. Copied from first page
JASS:
//=============================================================
//  Missile API. 
//=============================================================  
// Syntax for missile creation in GUI:
//    1. Run trigger Missile Configurate <gen> 
//    2. Set properties via scalar variables.
//    3. Run trigger Missile <gen>
// JASS users may call the create and launch function directly.
//
// Missile operates with custom events.
// This means that you have access to 
// a missile's properties within action functions
// of the Missile trigger interface. 
//
// "udg_Missile__EventId" indicates
// which event trigger has been dispatched.
//
// "udg_Missile__EventIndex" is the array index
// of the triggering missile.
//
// Avaliable events are:
//
//   1.) udg_EVENT_MISSILE_COLLIDE_UNIT
//        • Runs when a missile collides with a unit.
//
//   2.) udg_EVENT_MISSILE_COLLIDE_DEST
//        • Runs when a missile collides with a destructable.
//
//   3.) udg_EVENT_MISSILE_PERIODIC
//        • Runs every missile timer interval.
//
//   4.) udg_EVENT_MISSILE_FINISH
//        • Runs when a missile reaches its impact position.
//
//   5.) udg_EVENT_MISSILE_REMOVE
//        • Runs when a missile is destroyed.
//
//   6.) udg_EVENT_MISSILE_NOTHING
//        • Value of udg_Missile__EventId when accessed
//          from outside a trigger action function.
//
// During every missile event you may use the 
// following GUI generated variables.
// Every scalar variable has an equivalent array variable,
// which you can read and use at any time. 
// For exmple: udg_Missile__Source vs. udg_Missile_Source[index]
//
// Trigger variables which fire Missile events:
//
//    trigger udg_Missile__OnCollideUnit            -    trigger array udg_Missile_OnUnit 
//    trigger udg_Missile__OnCollideDestructable    -    trigger array udg_Missile_OnDest
//    trigger udg_Missile__OnPeridoic               -    trigger array udg_Missile_OnPeriodic
//    trigger udg_Missile__OnRemove                 -    trigger array udg_Missile_OnRemove
//    trigger udg_Missile__OnFinish                 -    trigger array udg_Missile_OnFinish
//
// Variables which mimic a function call:
//
//    location udg_Missile__Origin    
//    location udg_Missile__Impact      
//    boolean  udg_Missile__WantDestroy    -    boolean array udg_Missile_WantDestroy
//    boolean  udg_Missile__Recycle        -    boolean array udg_Missile_Recycle
//    real     udg_Missile__Scale          -    real    array udg_Missile_Scale
//    real     udg_Missile__FlyTime        -    real    array udg_Missile_FlyTime ( Converts time in seconds to a vector lenght )
//    real     udg_Missile__Model          -    string  array udg_Missile_Model   ( Converts a string path to a special effect )
//    real     udg_Missile__Arc            -    real    array udg_Missile_Arc     ( Converts an arc in degree to height value )
//    real     udg_Missile__Curve          -    real    array udg_Missile_Curve   ( Converts a curve in degree to an open value )
//
// Variables for read-only:
//
//    integer udg_Missile__EventId     
//    integer udg_Missile__EventIndex  
//    unit    udg_Missile__Dummy           -    unit array udg_Missile_Dummy
//    real    udg_Missile__Angle           -    real array udg_Missile_Angle    ( In radians )
//    real    udg_Missile__Distance        -    real array udg_Missile_Distance ( Total distance traveled )
//
// Variables for read and set.
//
//    unit    udg_Missile__Source          -    unit    array udg_Missile_Source
//    unit    udg_Missile__Target          -    unit    array udg_Missile_Target    ( Enables homing behaviour towards a target unit )
//    player  udg_Missile__Owner           -    unit    array udg_Missile_Owner     ( Pseudo-owner for better onCollide evaluation ) 
//    real    udg_Missile__ImpactZ         -    real    array udg_Missile_ImpactZ
//    real    udg_Missile__OriginZ         -    real    array udg_Missile_OriginZ
//    real    udg_Missile__Damage          -    real    array udg_Missile_Damage
//    real    udg_Missile__Collision       -    real    array udg_Missile_Collision
//    real    udg_Missile__Speed           -    real    array udg_Missile_Speed
//    real    udg_Missile__Acceleration    -    real    array udg_Missile_Acceleration
//    real    udg_Missile__Height          -    real    array udg_Missile_Height    ( Highest point of the parabola )
//    real    udg_Missile__Open            -    real    array udg_Missile_Open      
//    real    udg_Missile__Turn            -    real    array udg_Missile_Turn      ( Turn ratio per second )
//    real    udg_Missile__Data            -    integer array udg_Missile_Data
 
Level 7
Joined
Jun 14, 2009
Messages
235
Yes in radians.
JASS:
// Variables for read-only:
//
//    integer udg_Missile__EventId     
//    integer udg_Missile__EventIndex  
//    unit    udg_Missile__Dummy           -    unit array udg_Missile_Dummy
//    real    udg_Missile__Angle           -    real array udg_Missile_Angle    ( In radians )
//    real    udg_Missile__Distance        -    real array udg_Missile_Distance ( Total distance traveled )
//
Full list. Copied from first page
JASS:
//=============================================================
//  Missile API. 
//=============================================================  
// Syntax for missile creation in GUI:
//    1. Run trigger Missile Configurate <gen> 
//    2. Set properties via scalar variables.
//    3. Run trigger Missile <gen>
// JASS users may call the create and launch function directly.
//
// Missile operates with custom events.
// This means that you have access to 
// a missile's properties within action functions
// of the Missile trigger interface. 
//
// "udg_Missile__EventId" indicates
// which event trigger has been dispatched.
//
// "udg_Missile__EventIndex" is the array index
// of the triggering missile.
//
// Avaliable events are:
//
//   1.) udg_EVENT_MISSILE_COLLIDE_UNIT
//        • Runs when a missile collides with a unit.
//
//   2.) udg_EVENT_MISSILE_COLLIDE_DEST
//        • Runs when a missile collides with a destructable.
//
//   3.) udg_EVENT_MISSILE_PERIODIC
//        • Runs every missile timer interval.
//
//   4.) udg_EVENT_MISSILE_FINISH
//        • Runs when a missile reaches its impact position.
//
//   5.) udg_EVENT_MISSILE_REMOVE
//        • Runs when a missile is destroyed.
//
//   6.) udg_EVENT_MISSILE_NOTHING
//        • Value of udg_Missile__EventId when accessed
//          from outside a trigger action function.
//
// During every missile event you may use the 
// following GUI generated variables.
// Every scalar variable has an equivalent array variable,
// which you can read and use at any time. 
// For exmple: udg_Missile__Source vs. udg_Missile_Source[index]
//
// Trigger variables which fire Missile events:
//
//    trigger udg_Missile__OnCollideUnit            -    trigger array udg_Missile_OnUnit 
//    trigger udg_Missile__OnCollideDestructable    -    trigger array udg_Missile_OnDest
//    trigger udg_Missile__OnPeridoic               -    trigger array udg_Missile_OnPeriodic
//    trigger udg_Missile__OnRemove                 -    trigger array udg_Missile_OnRemove
//    trigger udg_Missile__OnFinish                 -    trigger array udg_Missile_OnFinish
//
// Variables which mimic a function call:
//
//    location udg_Missile__Origin    
//    location udg_Missile__Impact      
//    boolean  udg_Missile__WantDestroy    -    boolean array udg_Missile_WantDestroy
//    boolean  udg_Missile__Recycle        -    boolean array udg_Missile_Recycle
//    real     udg_Missile__Scale          -    real    array udg_Missile_Scale
//    real     udg_Missile__FlyTime        -    real    array udg_Missile_FlyTime ( Converts time in seconds to a vector lenght )
//    real     udg_Missile__Model          -    string  array udg_Missile_Model   ( Converts a string path to a special effect )
//    real     udg_Missile__Arc            -    real    array udg_Missile_Arc     ( Converts an arc in degree to height value )
//    real     udg_Missile__Curve          -    real    array udg_Missile_Curve   ( Converts a curve in degree to an open value )
//
// Variables for read-only:
//
//    integer udg_Missile__EventId     
//    integer udg_Missile__EventIndex  
//    unit    udg_Missile__Dummy           -    unit array udg_Missile_Dummy
//    real    udg_Missile__Angle           -    real array udg_Missile_Angle    ( In radians )
//    real    udg_Missile__Distance        -    real array udg_Missile_Distance ( Total distance traveled )
//
// Variables for read and set.
//
//    unit    udg_Missile__Source          -    unit    array udg_Missile_Source
//    unit    udg_Missile__Target          -    unit    array udg_Missile_Target    ( Enables homing behaviour towards a target unit )
//    player  udg_Missile__Owner           -    unit    array udg_Missile_Owner     ( Pseudo-owner for better onCollide evaluation ) 
//    real    udg_Missile__ImpactZ         -    real    array udg_Missile_ImpactZ
//    real    udg_Missile__OriginZ         -    real    array udg_Missile_OriginZ
//    real    udg_Missile__Damage          -    real    array udg_Missile_Damage
//    real    udg_Missile__Collision       -    real    array udg_Missile_Collision
//    real    udg_Missile__Speed           -    real    array udg_Missile_Speed
//    real    udg_Missile__Acceleration    -    real    array udg_Missile_Acceleration
//    real    udg_Missile__Height          -    real    array udg_Missile_Height    ( Highest point of the parabola )
//    real    udg_Missile__Open            -    real    array udg_Missile_Open      
//    real    udg_Missile__Turn            -    real    array udg_Missile_Turn      ( Turn ratio per second )
//    real    udg_Missile__Data            -    integer array udg_Missile_Data

Is there a quick formula to convert radians into WC3 Real angle values?

I tried multiplying by 180/pi but it only works until the radians go into negative values.
 
Top