//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
AccDirection | real | No | |
AccPoint1 | location | No | |
AccPoint2 | location | No | |
AccSpeed | real | No | |
AfterDamageEvent | real | No | |
AMSAmount | real | Yes | |
AMSReal | real | No | |
AOEDamageEvent | real | No | |
CenterPoint | location | No | |
ClearDamageEvent | trigger | No | |
Crash_Region | region | No | |
Creep_All_Group1 | group | No | |
Creep_All_Real1 | real | Yes | |
Creep_All_TempUnit1 | unit | No | |
Creep_TempPoint1 | location | No | |
Creep_TempPoint2 | location | No | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DamageBlockingAbility | abilcode | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageModifierEvent | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypeReduced | integer | No | |
DmgEvBracers | itemcode | No | |
DmgEvMana | real | No | |
DmgEvManaMult | real | No | |
DmgEvMSlvl | integer | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
EnhancedDamageTarget | unit | No | |
EVENT_MISSILE_COLLIDE_DEST | integer | No | |
EVENT_MISSILE_COLLIDE_UNIT | integer | No | |
EVENT_MISSILE_FINISH | integer | No | |
EVENT_MISSILE_NOTHING | integer | No | |
EVENT_MISSILE_PERIODIC | integer | No | |
EVENT_MISSILE_REMOVE | integer | No | |
Game_NumberRound | integer | No | |
Game_RandomInt | integer | No | |
Game_RoundBoolean | boolean | No | |
Game_StartBoolean | boolean | No | |
Game_TempPoint | location | No | |
HideDamageFrom | boolean | Yes | |
IsDamageSpell | boolean | No | |
IsUnitBeingKnockedBack | boolean | Yes | |
JD_Angle | real | Yes | |
JD_Animations | string | Yes | |
JD_Distances | real | Yes | |
JD_Effect | string | Yes | |
JD_Group | group | No | |
JD_HighSettings | real | Yes | |
JD_Integers | integer | Yes | |
JD_JumpHigh | real | Yes | |
JD_ReachedDistance | real | Yes | |
JD_RealTimer | real | Yes | |
JD_SpeedUnits | real | Yes | |
JD_TempPoint | location | Yes | |
JD_TreesDestroy | boolean | Yes | |
JD_Unit | unit | Yes | |
JDA_Animation | string | No | |
JDA_AnimationSpeed | real | No | |
JDA_Collusion | boolean | No | |
JDA_DestroyTrees_Dash | boolean | No | |
JDA_JumpHigh_Distance | real | No | |
JDA_SpecialEffect | string | No | |
JDA_Speed | real | No | |
JDA_TargetPoint | location | No | |
JDA_TargetPoint2 | location | No | |
JDA_Unit | unit | No | |
K2DAmphibious | boolean | Yes | |
K2DAngle | real | Yes | |
K2DBounce | boolean | Yes | |
K2DCollision | real | Yes | |
K2DCos | real | Yes | |
K2DCosD1 | real | Yes | |
K2DCosD2 | real | Yes | |
K2DCosH | real | Yes | |
K2DDebrisKiller | unit | No | |
K2DDestRadius | real | Yes | |
K2DDistanceLeft | real | Yes | |
K2DFlying | boolean | Yes | |
K2DFreeze | boolean | Yes | |
K2DFriction | real | Yes | |
K2DFXModel | string | Yes | |
K2DFXRate | real | Yes | |
K2DFXTimeLeft | real | Yes | |
K2DHeight | real | Yes | |
K2DHeightThreshold | real | Yes | |
K2DImpact | trigger | Yes | |
K2DItem | item | No | |
K2DItemOffset | boolean | No | |
K2DItemsFound | boolean | No | |
K2DKillTrees | boolean | Yes | |
K2DLastX | real | Yes | |
K2DLastY | real | Yes | |
K2DMaxDestRadius | real | No | |
K2DMaxX | real | No | |
K2DMaxY | real | No | |
K2DMinX | real | No | |
K2DMinY | real | No | |
K2DNext | integer | Yes | |
K2DOverride | boolean | Yes | |
K2DPause | boolean | Yes | |
K2DPrev | integer | Yes | |
K2DRadius | integer | Yes | |
K2DRegion | rect | No | |
K2DSimple | boolean | Yes | |
K2DSin | real | Yes | |
K2DSinD1 | real | Yes | |
K2DSinD2 | real | Yes | |
K2DSinH | real | Yes | |
K2DSource | unit | Yes | |
K2DTimeLeft | real | Yes | |
K2DTimeout | real | No | |
K2DTimer | timer | No | |
K2DUnbiasedCollision | boolean | Yes | |
K2DVelocity | real | Yes | |
K2DX | real | No | |
K2DY | real | No | |
Knockback2DAmphibious | boolean | No | |
Knockback2DAngle | real | No | |
Knockback2DBounces | boolean | No | |
Knockback2DCollision | real | No | |
Knockback2DDefaultBounce | boolean | No | |
Knockback2DDefaultDestRadius | real | No | |
Knockback2DDefaultFriction | real | No | |
Knockback2DDefaultFX | string | No | |
Knockback2DDefaultFXRate | real | No | |
Knockback2DDefaultGravity | real | No | |
Knockback2DDefaultKillTrees | boolean | No | |
Knockback2DDefaultPause | boolean | No | |
Knockback2DDestRadius | real | No | |
Knockback2DDistance | real | No | |
Knockback2DFriction | real | No | |
Knockback2DFXRate | real | No | |
Knockback2DGravity | real | No | |
Knockback2DHeight | real | No | |
Knockback2DKillTrees | boolean | No | |
Knockback2DLoopFX | string | No | |
Knockback2DOnImpact | trigger | No | |
Knockback2DOverride | boolean | No | |
Knockback2DPause | boolean | No | |
Knockback2DRobustPathing | integer | No | |
Knockback2DSimple | boolean | No | |
Knockback2DSource | unit | No | |
Knockback2DTime | real | No | |
Knockback2DTreeOrDebris | string | No | |
Knockback2DUnbiasedCollision | boolean | No | |
Knockback2DUnit | unit | No | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
LavaDisplacement_TempPoint1 | location | No | |
LavaDisplacement_TempPoint2 | location | No | |
LavaDisplacement_TempPoint3 | location | No | |
LavaDisplacement_TempZ1 | real | No | |
LavaSFX_TempPoint1 | location | No | |
LoopInt | integer | No | |
Magna_AttackTempGroup1 | group | No | |
Magna_AttackTempPoint1 | location | No | |
Magna_AttackTempPoint2 | location | No | |
Magna_AttackTempPoint3 | location | No | |
Missile__Acceleration | real | No | |
Missile__Angle | real | No | |
Missile__Arc | real | No | |
Missile__CliffLevel | integer | No | |
Missile__Collision | real | No | |
Missile__Curve | real | No | |
Missile__Damage | real | No | |
Missile__Data | integer | No | |
Missile__DestructableHit | destructable | No | |
Missile__Distance | real | No | |
Missile__Dummy | unit | No | |
Missile__DummyTypeId | unitcode | No | |
Missile__EventId | integer | No | |
Missile__EventIndex | integer | No | |
Missile__FlyTime | real | No | |
Missile__Height | real | No | |
Missile__Impact | location | No | |
Missile__ImpactZ | real | No | |
Missile__Index | integer | No | |
Missile__Model | string | No | |
Missile__OnCollideDestructable | trigger | No | |
Missile__OnCollideUnit | trigger | No | |
Missile__OnFinish | trigger | No | |
Missile__OnPeriodic | trigger | No | |
Missile__OnRemove | trigger | No | |
Missile__Open | real | No | |
Missile__Origin | location | No | |
Missile__Origin2 | location | No | |
Missile__Origin3 | location | No | |
Missile__OriginZ | real | No | |
Missile__Owner | player | No | |
Missile__Recycle | boolean | No | |
Missile__Scale | real | No | |
Missile__Source | unit | No | |
Missile__Speed | real | No | |
Missile__Target | unit | No | |
Missile__TempLoc | location | No | |
Missile__TempLoc2 | location | No | |
Missile__Turn | real | No | |
Missile__UnitHit | unit | No | |
Missile__WantDestroy | boolean | No | |
Missile_A_Z | real | Yes | |
Missile_AB_Angle | real | Yes | |
Missile_AB_Distance | real | Yes | |
Missile_AB_Pitch | real | Yes | |
Missile_AB_Square | real | Yes | |
Missile_AB_Traveled | real | Yes | |
Missile_Acceleration | real | Yes | |
Missile_Angle | real | Yes | |
Missile_Arc | real | Yes | |
Missile_B_Z | real | Yes | |
Missile_CliffLevel_TempPoint | location | No | |
Missile_Collision | real | Yes | |
Missile_Curve | real | Yes | |
Missile_CurveX | real | Yes | |
Missile_CurveY | real | Yes | |
Missile_Damage | real | Yes | |
Missile_Data | integer | Yes | |
Missile_Distance | real | Yes | |
Missile_Dummy | unit | Yes | |
Missile_Effect | effect | Yes | |
Missile_FlyTime | real | Yes | |
Missile_Height | real | Yes | |
Missile_HeightZ1 | real | No | |
Missile_HeightZ2 | real | No | |
Missile_ImpactX | real | Yes | |
Missile_ImpactY | real | Yes | |
Missile_ImpactZ | real | Yes | |
Missile_MissilePoint1 | location | No | |
Missile_MissilePoint2 | location | No | |
Missile_MissilePoint3 | location | No | |
Missile_MissileZ1 | real | No | |
Missile_MissileZ2 | real | No | |
Missile_Model | string | Yes | |
Missile_OnDest | trigger | Yes | |
Missile_OnFinish | trigger | Yes | |
Missile_OnPeriodic | trigger | Yes | |
Missile_OnRemove | trigger | Yes | |
Missile_OnUnit | trigger | Yes | |
Missile_Open | real | Yes | |
Missile_OriginX | real | Yes | |
Missile_OriginY | real | Yes | |
Missile_OriginZ | real | Yes | |
Missile_Owner | player | Yes | |
Missile_PosX | real | Yes | |
Missile_PosY | real | Yes | |
Missile_PosZ | real | Yes | |
Missile_PrevX | real | Yes | |
Missile_PrevY | real | Yes | |
Missile_Recycle | boolean | Yes | |
Missile_Scale | real | Yes | |
Missile_Source | unit | Yes | |
Missile_Speed | real | Yes | |
Missile_Target | unit | Yes | |
Missile_TerrainZ | real | Yes | |
Missile_Turn | real | Yes | |
Missile_WantDestroy | boolean | Yes | |
MissileAllocated | boolean | Yes | |
MissileCore | trigger | No | |
MissileDummyCount | integer | No | |
MissileDummyStack | unit | Yes | |
MissileDummyTime | integer | Yes | |
MissileDummyTimer | timer | No | |
MissileGroup | group | No | |
MissileHash | hashtable | No | |
MissileLaunched | boolean | Yes | |
MissileLocZ | location | No | |
MissileMaxX | integer | No | |
MissileMaxY | integer | No | |
MissileMinX | integer | No | |
MissileMinY | integer | No | |
MissileNextNode | integer | Yes | |
MissileNodes | integer | No | |
MissilePrevNode | integer | Yes | |
MissileRect | rect | No | |
MissileRecycler | integer | Yes | |
MissileTimer | timer | No | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
Player_AttachPoint1 | location | No | |
Player_CannonBoolean1 | boolean | No | |
Player_CannonBoolean2 | boolean | No | |
Player_CannonBooleanON | boolean | No | |
Player_CannonPlayer | player | No | |
Player_CannonTempPoint1 | location | No | |
Player_CannonTimer | timer | No | |
Player_CannonUnit1 | unit | No | |
Player_CannonUnit2 | unit | No | |
Player_CannonZ1 | real | No | |
Player_CannonZ2 | real | No | |
Player_CannonZ3 | real | No | |
Player_CannonZ4 | real | No | |
Player_CarMovingBoolean | boolean | No | |
Player_ChassisPlayer | player | No | |
Player_ChassisUnit1 | unit | No | |
Player_ChassisUnit2 | unit | No | |
Player_ChassisZ2 | real | No | |
Player_ChassisZPoint | location | No | |
Player_CollisionDummyFront | unit | No | |
Player_CollisionDummyRear | unit | No | |
Player_CollisionSpeedPenalty | real | No | |
Player_CollisionTempGroup1 | group | No | |
Player_CollisionTempGroup2 | group | No | |
Player_CollisionTempGroup3 | group | No | |
Player_CollisionTempGroup4 | group | No | |
Player_CollisionTempGroupAll | group | No | |
Player_CollisionTempPoint1 | location | No | |
Player_CollisionTempPoint2 | location | No | |
Player_CollisionTempPoint3 | location | No | |
Player_CollisionTempPoint4 | location | No | |
Player_CollisionTempPoint5 | location | No | |
Player_CollisionTempPoint6 | location | No | |
Player_CollisionTempPoint7 | location | No | |
Player_CollisionTempUnit1 | unit | No | |
Player_CrashCenterPoint | location | No | |
Player_CrashFront_CenterPoint | location | No | |
Player_CrashFront_LeftPoint | location | No | |
Player_CrashFront_RightPoint | location | No | |
Player_CrashONBoolean | boolean | No | |
Player_CrashPoint1 | location | No | |
Player_CrashRear_CenterPoint | location | No | |
Player_CrashRear_LeftPoint | location | No | |
Player_CrashRear_RightPoint | location | No | |
Player_CrashTurnInt1 | integer | No | |
Player_CrashTurnPoint | location | No | |
Player_CrashTurnReal1 | real | No | |
Player_CrashUnit1 | unit | No | |
Player_CrashUnitGroup1 | group | No | |
Player_FacingFrontBoolean | boolean | No | |
Player_FlameGroup1 | group | No | |
Player_FlameGroup2 | group | No | |
Player_FlamePoint1 | location | No | |
Player_FlamePoint2 | location | No | |
Player_FlamePoint3 | location | No | |
Player_FlameSFX | effect | No | |
Player_FlameTempUnit1 | unit | No | |
Player_HeroJumpTimer1 | timer | No | |
Player_HeroJumpTimer2 | timer | No | |
Player_HeroShootTimer1 | timer | No | |
Player_HeroShootTimer2 | timer | No | |
Player_Missile_PredatorZ1 | real | No | |
Player_MissilePoint1 | location | No | |
Player_MissilePoint2 | location | No | |
Player_MissileSFXBool1 | boolean | No | |
Player_MissileSFXBool2 | boolean | No | |
Player_MissileTempPoint1 | location | No | |
Player_MissileZ1 | real | No | |
Player_MissileZ2 | real | No | |
Player_MissileZ3 | real | No | |
Player_MovePoint1 | location | No | |
Player_MovePoint2 | location | No | |
Player_MovePoint3 | location | No | |
Player_MovePoint4 | location | No | |
Player_MovePoint5 | location | No | |
Player_MovePoint6 | location | No | |
Player_MovePoint7 | location | No | |
Player_MovePoint8 | location | No | |
Player_MovePoint9 | location | No | |
Player_PodTimer1 | timer | No | |
Player_PodUnit1 | unit | No | |
Player_SharedFood | integer | No | |
Player_SharedGold | integer | No | |
Player_SharedLumber | integer | No | |
Player_ShootGroup1 | group | No | |
Player_ShootGroup2 | group | No | |
Player_ShootGunSFX | effect | No | |
Player_ShootPoint1 | location | No | |
Player_ShootPoint2 | location | No | |
Player_ShootPoint3 | location | No | |
Player_ShootPoint4 | location | No | |
Player_ShootTempUnit | unit | No | |
Player_SpeedAccelerBoolean | boolean | No | |
Player_SpeedCurrent_Real | real | No | |
Player_SpeedInt1 | integer | No | |
Player_SpeedMax_Real | real | No | |
Player_SpeedMin_Real | real | No | |
Player_SpeedOvercharge_Real | real | No | |
Player_SpeedOverchargeON_Bool | boolean | No | |
Player_SpeedOverchargeON_Bool2 | boolean | No | |
Player_TempInt1 | integer | No | |
Player_TempPoint1 | location | No | |
Player_TempPoint2 | location | No | |
Player_TempReal1 | real | No | |
Player_TurnInt1 | integer | No | |
Player_TurnReal1 | real | No | |
Radians_QuarterPi | real | No | |
Radians_QuarterTurn | real | No | |
Radians_Turn | real | No | |
Spawn_EnemyGroup | group | No | |
Spawn_NumberEnemies | integer | No | |
Spawn_RandomInt | integer | No | |
Spawn_RandomInt2 | integer | No | |
Spawn_TempPoint | location | No | |
Spawn_TempPointTank | location | No | |
Spawn_TempPointTank2 | location | No | |
Spawn_TempUnit | unit | No | |
Spawn_TempUnitType | unitcode | No | |
SpellDamageAbility | abilcode | No | |
StopMovement_Timer | timer | No | |
Sw_AllDestr | destructable | Yes | |
Sw_Count | integer | No | |
Sw_hintIntegerArray | integer | Yes | |
Sw_hintMaxInteger | integer | No | |
Sw_hintStrings | string | Yes | |
Sw_LifeInt | integer | No | |
Sw_RandomInt | integer | No | |
Sw_randomInt1 | integer | No | |
Sw_randomInt2 | integer | No | |
Sw_randomIntGeneratorArray | integer | Yes | |
Sw_randomIntMaxValue | integer | No | |
Sw_randomIntMinValue | integer | No | |
Sw_tempInt | integer | No | |
Sw_tempInt1 | integer | No | |
Sw_tempInt2 | integer | No | |
SwitchHQTimer | timer | No | |
TargetPoint | location | No | |
Tauren_Shockwave | boolean | Yes | |
Tauren_WarStomp | boolean | Yes | |
Temp_Group | group | No | |
TempX | real | No | |
TempY | real | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UMovNext | integer | Yes | |
UMovPrev | integer | Yes | |
UnitDamageRegistered | boolean | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes | |
UnitMovementInterval | real | No | |
UnitMoving | boolean | Yes | |
UnitMovingEvent | real | No | |
UnitMovingX | real | Yes | |
UnitMovingY | real | Yes | |
Water_TempZReal1 | real | Yes |
//TESH.scrollpos=0
//TESH.alwaysfold=0
//===========================================================================
// Damage Engine lets you detect, amplify, block or nullify damage. It even
// lets you detect if the damage was physical or from a spell. Just reference
// DamageEventAmount/Source/Target or the boolean IsDamageSpell, to get the
// necessary damage event data.
//
// - Detect damage: use the event "DamageEvent Equal to 1.00"
// - To change damage before it's dealt: use the event "DamageModifierEvent Equal to 1.00"
// - Detect damage after it was applied, use the event "AfterDamageEvent Equal to 1.00"
// - Detect spell damage: use the condition "IsDamageSpell Equal to True"
// - Detect zero-damage: use the event "DamageEvent Equal to 2.00" (an AfterDamageEvent will not fire for this)
//
// You can specify the DamageEventType before dealing triggered damage. To prevent an already-improbable error, I recommend running the trigger "ClearDamageEvent (Checking Conditions)" after dealing triggered damage from within a damage event:
// - Set NextDamageType = DamageTypeWhatever
// - Unit - Cause...
// - Trigger - Run ClearDamageEvent (Checking Conditions)
//
// You can modify the DamageEventAmount and the DamageEventType from a "DamageModifierEvent Equal to 1.00" trigger.
// - If the amount is modified to negative, it will count as a heal.
// - If the amount is set to 0, no damage will be dealt.
//
// If you need to reference the original in-game damage, use the variable "DamageEventPrevAmt".
//
//===========================================================================
// Programming note about "integer i" and "udg_DmgEvRecursionN": integer i
// ranges from -1 upwards. "udg_DmgEvRecursionN" ranges from 0 upwards.
// "integer i" is always 1 less than "udg_DmgEvRecursionN"
//
function DmgEvResetVars takes nothing returns nothing
local integer i = udg_DmgEvRecursionN - 2
set udg_DmgEvRecursionN = i + 1
if i >= 0 then
set udg_DamageEventPrevAmt = udg_LastDmgPrevAmount[i]
set udg_DamageEventAmount = udg_LastDmgValue[i]
set udg_DamageEventSource = udg_LastDmgSource[i]
set udg_DamageEventTarget = udg_LastDmgTarget[i]
set udg_IsDamageSpell = udg_LastDmgWasSpell[i]
set udg_DamageEventType = udg_LastDmgPrevType[i]
endif
endfunction
function CheckDamagedLifeEvent takes boolean clear returns nothing
if clear then
set udg_NextDamageOverride = false
set udg_NextDamageType = 0
endif
if udg_DmgEvTrig != null then
call DestroyTrigger(udg_DmgEvTrig)
set udg_DmgEvTrig = null
if udg_IsDamageSpell then
call SetWidgetLife(udg_DamageEventTarget, RMaxBJ(udg_LastDamageHP, 0.41))
if udg_LastDamageHP <= 0.405 then
if udg_DamageEventType < 0 then
call SetUnitExploded(udg_DamageEventTarget, true)
endif
//Kill the unit
call DisableTrigger(udg_DamageEventTrigger)
call UnitDamageTarget(udg_DamageEventSource, udg_DamageEventTarget, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
call EnableTrigger(udg_DamageEventTrigger)
endif
elseif GetUnitAbilityLevel(udg_DamageEventTarget, udg_DamageBlockingAbility) > 0 then
call UnitRemoveAbility(udg_DamageEventTarget, udg_DamageBlockingAbility)
call SetWidgetLife(udg_DamageEventTarget, udg_LastDamageHP)
endif
if udg_DamageEventAmount != 0.00 and not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_AfterDamageEvent = 0.00
set udg_AfterDamageEvent = 1.00
set udg_AfterDamageEvent = 0.00
endif
call DmgEvResetVars()
endif
endfunction
function DmgEvOnAOEEnd takes nothing returns nothing
if udg_DamageEventAOE > 1 then
set udg_AOEDamageEvent = 0.00
set udg_AOEDamageEvent = 1.00
set udg_AOEDamageEvent = 0.00
set udg_DamageEventAOE = 1
endif
set udg_DamageEventLevel = 1
set udg_EnhancedDamageTarget = null
call GroupClear(udg_DamageEventAOEGroup)
endfunction
function DmgEvOnExpire takes nothing returns nothing
set udg_DmgEvStarted = false
call CheckDamagedLifeEvent(true)
//Reset things so they don't perpetuate for AoE/Level target detection
call DmgEvOnAOEEnd()
set udg_DamageEventTarget = null
set udg_DamageEventSource = null
endfunction
function PreCheckDamagedLifeEvent takes nothing returns boolean
call CheckDamagedLifeEvent(true)
return false
endfunction
function OnUnitDamage takes nothing returns boolean
local boolean override = udg_DamageEventOverride
local integer i
local integer e = udg_DamageEventLevel
local integer a = udg_DamageEventAOE
local string s
local real prevAmount
local real life
local real prevLife
local unit u
local unit f
call CheckDamagedLifeEvent(false) //in case the unit state event failed and the 0.00 second timer hasn't yet expired
set i = udg_DmgEvRecursionN - 1 //Had to be moved here due to false recursion tracking
if i < 0 then
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
set u = udg_DamageEventTarget
set f = udg_DamageEventSource
elseif i < 16 then
set udg_LastDmgPrevAmount[i]= udg_DamageEventPrevAmt
set udg_LastDmgValue[i] = udg_DamageEventAmount
set udg_LastDmgSource[i] = udg_DamageEventSource
set udg_LastDmgTarget[i] = udg_DamageEventTarget
set udg_LastDmgWasSpell[i] = udg_IsDamageSpell
set udg_LastDmgPrevType[i] = udg_DamageEventType
else
set s = "WARNING: Recursion error when dealing damage! Make sure when you deal damage from within a DamageEvent trigger, do it like this:\n\n"
set s = s + "Trigger - Turn off (This Trigger)\n"
set s = s + "Unit - Cause...\n"
set s = s + "Trigger - Turn on (This Trigger)"
//Delete the next couple of lines to disable the in-game recursion crash warnings
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 999.00, s)
return false
endif
set udg_DmgEvRecursionN = i + 2
set prevAmount = GetEventDamage()
set udg_DamageEventTarget = GetTriggerUnit()
set udg_DamageEventSource = GetEventDamageSource()
set udg_DamageEventAmount = prevAmount
set udg_DamageEventType = udg_NextDamageType
set udg_NextDamageType = 0
set udg_DamageEventOverride = udg_NextDamageOverride
set udg_NextDamageOverride = false
if i < 0 then
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
if udg_DamageEventType == 0 then
if f == udg_DamageEventSource then
//Source has damaged more than once
if IsUnitInGroup(udg_DamageEventTarget, udg_DamageEventAOEGroup) then
//Added 5 August 2017 to improve tracking of enhanced damage against, say, Pulverize
set udg_DamageEventLevel = udg_DamageEventLevel + 1
set udg_EnhancedDamageTarget = udg_DamageEventTarget
else
//Multiple targets hit by this source - flag as AOE
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
else
//New damage source - unflag everything
set u = udg_DamageEventSource
set udg_DamageEventSource = f
call DmgEvOnAOEEnd()
set udg_DamageEventSource = u
endif
call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
endif
if not udg_DmgEvStarted then
set udg_DmgEvStarted = true
call TimerStart(udg_DmgEvTimer, 0.00, false, function DmgEvOnExpire)
endif
endif
if prevAmount == 0.00 then
if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_DamageEventPrevAmt = 0.00
set udg_DamageEvent = 0.00
set udg_DamageEvent = 2.00
set udg_DamageEvent = 0.00
endif
call DmgEvResetVars()
else
set u = udg_DamageEventTarget
set udg_IsDamageSpell = prevAmount < 0.00
if udg_IsDamageSpell then
set prevAmount = -udg_DamageEventAmount
set life = 1.00
if IsUnitType(u, UNIT_TYPE_ETHEREAL) and not IsUnitType(u, UNIT_TYPE_HERO) then
set life = life*udg_DAMAGE_FACTOR_ETHEREAL //1.67
endif
if GetUnitAbilityLevel(u, 'Aegr') > 0 then
set life = life*udg_DAMAGE_FACTOR_ELUNES //0.80
endif
if udg_DmgEvBracers != 0 and IsUnitType(u, UNIT_TYPE_HERO) then
//Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
set i = 6
loop
set i = i - 1
if GetItemTypeId(UnitItemInSlot(u, i)) == udg_DmgEvBracers then
set life = life*udg_DAMAGE_FACTOR_BRACERS //0.67
exitwhen true
endif
exitwhen i == 0
endloop
endif
set udg_DamageEventAmount = prevAmount*life
endif
set udg_DamageEventPrevAmt = prevAmount
set udg_DamageModifierEvent = 0.00
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 1.00
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 2.00
set udg_DamageModifierEvent = 3.00
endif
endif
set udg_DamageEventOverride = override
if udg_DamageEventAmount > 0.00 then
set udg_DamageModifierEvent = 4.00
endif
set udg_DamageModifierEvent = 0.00
if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_DamageEvent = 0.00
set udg_DamageEvent = 1.00
set udg_DamageEvent = 0.00
endif
call CheckDamagedLifeEvent(true) //in case the unit state event failed from a recursive damage event
//All events have run and the damage amount is finalized.
set life = GetWidgetLife(u)
set udg_DmgEvTrig = CreateTrigger()
call TriggerAddCondition(udg_DmgEvTrig, Filter(function PreCheckDamagedLifeEvent))
if not udg_IsDamageSpell then
if udg_DamageEventAmount != prevAmount then
set life = life + prevAmount - udg_DamageEventAmount
if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
set udg_LastDamageHP = life - prevAmount
call UnitAddAbility(u, udg_DamageBlockingAbility)
endif
call SetWidgetLife(u, RMaxBJ(life, 0.42))
endif
call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
else
set udg_LastDamageHP = GetUnitState(u, UNIT_STATE_MAX_LIFE)
set prevLife = life
if life + prevAmount*0.75 > udg_LastDamageHP then
set life = RMaxBJ(udg_LastDamageHP - prevAmount/2.00, 1.00)
call SetWidgetLife(u, life)
set life = (life + udg_LastDamageHP)/2.00
else
set life = life + prevAmount*0.50
endif
set udg_LastDamageHP = prevLife - (prevAmount - (prevAmount - udg_DamageEventAmount))
call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, GREATER_THAN, life)
endif
endif
set u = null
set f = null
return false
endfunction
function CreateDmgEvTrg takes nothing returns nothing
set udg_DamageEventTrigger = CreateTrigger()
call TriggerAddCondition(udg_DamageEventTrigger, Filter(function OnUnitDamage))
endfunction
function SetupDmgEv takes nothing returns boolean
local integer i = udg_UDex
local unit u
if udg_UnitIndexEvent == 1.00 then
set u = udg_UDexUnits[i]
if GetUnitAbilityLevel(u, 'Aloc') == 0 and TriggerEvaluate(gg_trg_Damage_Engine_Config) then
set udg_UnitDamageRegistered[i] = true
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
call UnitAddAbility(u, udg_SpellDamageAbility)
call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
endif
set u = null
else
set udg_HideDamageFrom[i] = false
if udg_UnitDamageRegistered[i] then
set udg_UnitDamageRegistered[i] = false
set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
set udg_DamageEventsWasted = 0
//Rebuild the mass EVENT_UNIT_DAMAGED trigger:
call DestroyTrigger(udg_DamageEventTrigger)
call CreateDmgEvTrg()
set i = udg_UDexNext[0]
loop
exitwhen i == 0
if udg_UnitDamageRegistered[i] then
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
endif
set i = udg_UDexNext[i]
endloop
endif
endif
endif
return false
endfunction
//===========================================================================
function InitTrig_Damage_Engine takes nothing returns nothing
local unit u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), 'uloc', 0, 0, 0)
local integer i = bj_MAX_PLAYERS //Fixed in 3.8
//Create this trigger with UnitIndexEvents in order add and remove units
//as they are created or removed.
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Filter(function SetupDmgEv))
set t = null
//Run the configuration trigger to set all configurables:
if gg_trg_Damage_Engine_Config == null then
//It's possible this InitTrig_ function ran first, in which case use ExecuteFunc.
call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
else
call TriggerExecute(gg_trg_Damage_Engine_Config)
endif
//Create trigger for storing all EVENT_UNIT_DAMAGED events.
call CreateDmgEvTrg()
//Create GUI-friendly trigger for cleaning up after UnitDamageTarget.
set udg_ClearDamageEvent = CreateTrigger()
call TriggerAddCondition(udg_ClearDamageEvent, Filter(function PreCheckDamagedLifeEvent))
//Disable SpellDamageAbility for every player.
loop
set i = i - 1
call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
exitwhen i == 0
endloop
//Preload abilities.
call UnitAddAbility(u, udg_DamageBlockingAbility)
call UnitAddAbility(u, udg_SpellDamageAbility)
call RemoveUnit(u)
set u = null
endfunction
//TESH.scrollpos=12
//TESH.alwaysfold=0
constant function GetAMSBuffId takes nothing returns integer
return 'Bams'
endfunction
constant function GetAMSAbilId takes nothing returns integer
return 'A002'
endfunction
constant function GetAMSShieldVal takes nothing returns real
return 300.00
endfunction
function Trig_Anti_Magic_Shield_Fix_Actions takes nothing returns nothing
local integer id = GetUnitUserData(udg_DamageEventTarget)
local real shield = udg_AMSAmount[id]- udg_DamageEventAmount
if shield <= 0.00 then
set udg_DamageEventAmount = -shield
set shield = 0.00
call UnitRemoveAbility(udg_DamageEventTarget, GetAMSBuffId())
else
set udg_DamageEventAmount = 0.00
if udg_DamageEventType == 0 then
set udg_DamageEventType = udg_DamageTypeBlocked
endif
endif
set udg_AMSAmount[id] = shield
endfunction
function Trig_Anti_Magic_Shield_Fix_Conditions takes nothing returns boolean
if udg_IsDamageSpell then
if GetUnitAbilityLevel(udg_DamageEventTarget, GetAMSBuffId()) > 0 then
call Trig_Anti_Magic_Shield_Fix_Actions()
else
set udg_AMSAmount[GetUnitUserData(udg_DamageEventTarget)] = 0.00
endif
endif
return false
endfunction
function AMS_Refresh_Conditions takes nothing returns boolean
if GetSpellAbilityId() == GetAMSAbilId() then
set udg_AMSAmount[GetUnitUserData(GetSpellTargetUnit())] = GetAMSShieldVal()
endif
return false
endfunction
function InitTrig_Anti_Magic_Shield_Fix takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageModifierEvent", EQUAL, 4.00)
call TriggerAddCondition(t, Condition(function Trig_Anti_Magic_Shield_Fix_Conditions))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Filter(function AMS_Refresh_Conditions))
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
function K2DItemCheckXY takes real x, real y returns boolean
call SetItemPosition(udg_K2DItem, x, y)
return GetWidgetX(udg_K2DItem) == x and GetWidgetY(udg_K2DItem) == y
endfunction
function K2DItemCheckAxis takes real x, real y returns boolean
local real x2 = x*udg_K2DRadius[udg_UDex]
local real y2 = y*udg_K2DRadius[udg_UDex]
set x = udg_K2DX + x2
set y = udg_K2DY + y2
if K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
set x = udg_K2DX - x2
set y = udg_K2DY - y2
return K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endif
return false
endfunction
function K2DItemCheck takes nothing returns boolean
local boolean result = K2DItemCheckXY(udg_K2DX, udg_K2DY)
//Only perform additional pathing checks if the unit has a larger collision.
if result and udg_Knockback2DRobustPathing > 0 and udg_K2DRadius[udg_UDex] > 0 then
//Check horizontal axis of unit to make sure nothing is going to collide
set result = K2DItemCheckAxis(udg_K2DCosH[udg_UDex], udg_K2DSinH[udg_UDex])
//Check vertical axis of unit to ensure nothing will collide
set result = result and K2DItemCheckAxis(udg_K2DCos[udg_UDex], udg_K2DSin[udg_UDex])
if result and udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
//Check diagonal axis of unit if more thorough pathing is desired
set result = K2DItemCheckAxis(udg_K2DCosD1[udg_UDex], udg_K2DSinD1[udg_UDex])
set result = result and K2DItemCheckAxis(udg_K2DCosD2[udg_UDex], udg_K2DSinD2[udg_UDex])
endif
endif
//Reset item so it won't interfere with the map
call SetItemPosition(udg_K2DItem, udg_K2DMaxX, udg_K2DMaxY)
call SetItemVisible(udg_K2DItem, false)
return result
endfunction
function K2DItemFilter takes nothing returns boolean
//Check for visible items, temporarily hide them and add them to the filter.
if IsItemVisible(GetFilterItem()) then
call SetItemVisible(GetFilterItem(), false)
return true
endif
return false
endfunction
function K2DItemCode takes nothing returns nothing
//Perform the item-pathing check only once, then unhide those filtered items
if not udg_K2DItemsFound then
set udg_K2DItemsFound = true
set udg_K2DItemOffset = K2DItemCheck()
endif
call SetItemVisible(GetEnumItem(), true)
endfunction
function K2DKillDest takes nothing returns nothing
local real x
local real y
//Handle destruction of debris
set bj_destRandomCurrentPick = GetEnumDestructable()
if GetWidgetLife(bj_destRandomCurrentPick) > 0.405 and IssueTargetOrder(udg_K2DDebrisKiller, udg_Knockback2DTreeOrDebris, bj_destRandomCurrentPick) then
set x = GetWidgetX(bj_destRandomCurrentPick) - udg_K2DX
set y = GetWidgetY(bj_destRandomCurrentPick) - udg_K2DY
if x*x + y*y <= udg_K2DDestRadius[udg_UDex] then
call KillDestructable(bj_destRandomCurrentPick)
endif
endif
endfunction
function K2DEnumDests takes nothing returns nothing
call MoveRectTo(udg_K2DRegion, udg_K2DX, udg_K2DY)
if udg_K2DKillTrees[udg_UDex] then
call SetUnitX(udg_K2DDebrisKiller, udg_K2DX)
call SetUnitY(udg_K2DDebrisKiller, udg_K2DY)
call EnumDestructablesInRect(udg_K2DRegion, null, function K2DKillDest)
endif
endfunction
function Knockback2DCheckXY takes real x, real y returns boolean
set udg_K2DX = x + udg_K2DVelocity[udg_UDex]*udg_K2DCos[udg_UDex]
set udg_K2DY = y + udg_K2DVelocity[udg_UDex]*udg_K2DSin[udg_UDex]
if udg_K2DSimple[udg_UDex] then
//A "pull" effect or a missile system does not require complex pathing.
if udg_K2DX <= udg_K2DMaxX and udg_K2DX >= udg_K2DMinX and udg_K2DY <= udg_K2DMaxY and udg_K2DY >= udg_K2DMinY then
call K2DEnumDests()
return true
endif
return false
elseif udg_K2DFlying[udg_UDex] then
return not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLYABILITY)
elseif not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_WALKABILITY) then
call K2DEnumDests()
set udg_K2DItemOffset = false
call EnumItemsInRect(udg_K2DRegion, Filter(function K2DItemFilter), function K2DItemCode)
if udg_K2DItemsFound then
//If items were found, the check was already performed.
set udg_K2DItemsFound = false
else
//Otherwise, perform the check right now.
set udg_K2DItemOffset = K2DItemCheck()
endif
return udg_K2DItemOffset
endif
return udg_K2DAmphibious[udg_UDex] and not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLOATABILITY)
endfunction
function Knockback2DApplyAngle takes real angle returns nothing
set angle = ModuloReal(angle, udg_Radians_Turn)
set udg_K2DCos[udg_UDex] = Cos(angle)
set udg_K2DSin[udg_UDex] = Sin(angle)
set udg_K2DAngle[udg_UDex] = angle
if udg_Knockback2DRobustPathing > 0 then
set angle = ModuloReal(angle + udg_Radians_QuarterTurn, udg_Radians_Turn)
set udg_K2DCosH[udg_UDex] = Cos(angle)
set udg_K2DSinH[udg_UDex] = Sin(angle)
if udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
set angle = ModuloReal(angle + udg_Radians_QuarterPi, udg_Radians_Turn)
set udg_K2DCosD1[udg_UDex] = Cos(angle)
set udg_K2DSinD1[udg_UDex] = Sin(angle)
set angle = ModuloReal(angle + udg_Radians_QuarterTurn, udg_Radians_Turn)
set udg_K2DCosD2[udg_UDex] = Cos(angle)
set udg_K2DSinD2[udg_UDex] = Sin(angle)
endif
endif
endfunction
function Knockback2DLooper takes nothing returns nothing
local integer i = 0
local unit u
local real x
local real y
call PauseUnit(udg_K2DDebrisKiller, false)
loop
set i = udg_K2DNext[i]
exitwhen i == 0
set udg_UDex = i
set udg_K2DTimeLeft[i] = udg_K2DTimeLeft[i] - udg_K2DTimeout
set udg_K2DDistanceLeft[i] = udg_K2DDistanceLeft[i] - udg_K2DVelocity[i]
set u = udg_UDexUnits[i]
if udg_K2DTimeLeft[i] > 0.00 then
if udg_K2DTimeLeft[i] < udg_K2DHeightThreshold[i] and udg_K2DHeightThreshold[i] != 0.00 then
call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u), GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)/udg_K2DHeightThreshold[i])
set udg_K2DHeightThreshold[i] = 0.00
endif
if udg_K2DPause[i] then
set x = udg_K2DLastX[i]
set y = udg_K2DLastY[i]
else
set x = GetUnitX(u)
set y = GetUnitY(u)
endif
if not Knockback2DCheckXY(x, y) then
if not udg_K2DFreeze[i] and IsTriggerEnabled(udg_K2DImpact[i]) and TriggerEvaluate(udg_K2DImpact[i]) then
call TriggerExecute(udg_K2DImpact[i])
endif
if udg_K2DBounce[i] then
call Knockback2DApplyAngle(udg_Radians_Turn - udg_K2DAngle[i])
if not Knockback2DCheckXY(x, y) then
call Knockback2DApplyAngle(udg_K2DAngle[i] + bj_PI)
if not Knockback2DCheckXY(x, y) then
call Knockback2DApplyAngle(udg_Radians_Turn - udg_K2DAngle[i])
set udg_K2DX = x
set udg_K2DY = y
endif
endif
else
set udg_K2DX = x
set udg_K2DY = y
set udg_K2DFreeze[i] = true
endif
endif
call SetUnitX(u, udg_K2DX)
call SetUnitY(u, udg_K2DY)
set udg_K2DLastX[i] = udg_K2DX
set udg_K2DLastY[i] = udg_K2DY
if udg_K2DFXModel[i] != "" then
set udg_K2DFXTimeLeft[i] = udg_K2DFXTimeLeft[i] - udg_K2DTimeout
if udg_K2DFXTimeLeft[i] <= 0.00 then
set udg_K2DFXTimeLeft[i] = udg_K2DFXRate[i]
if udg_K2DFlying[i] then
call DestroyEffect(AddSpecialEffectTarget(udg_K2DFXModel[i], u, "origin"))
else
call DestroyEffect(AddSpecialEffect(udg_K2DFXModel[i], udg_K2DX, udg_K2DY))
endif
endif
endif
if udg_K2DCollision[i] >= 0.00 then
set udg_Knockback2DSource = u
call GroupEnumUnitsInRange(bj_lastCreatedGroup, udg_K2DX, udg_K2DY, 200.00, null)
call GroupRemoveUnit(bj_lastCreatedGroup, u)
loop
set udg_Knockback2DUnit = FirstOfGroup(bj_lastCreatedGroup)
exitwhen udg_Knockback2DUnit == null
call GroupRemoveUnit(bj_lastCreatedGroup, udg_Knockback2DUnit)
if IsUnitInRange(udg_Knockback2DUnit, u, udg_K2DCollision[i]) and udg_K2DFlying[i] == IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_FLYING) and (not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_STRUCTURE)) and not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_DEAD) and (udg_K2DUnbiasedCollision[i] or IsUnitAlly(udg_Knockback2DUnit, GetOwningPlayer(u))) and TriggerEvaluate(gg_trg_Knockback_2D) then
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(GetUnitY(udg_Knockback2DUnit) - udg_K2DY, GetUnitX(udg_Knockback2DUnit) - udg_K2DX)
set udg_Knockback2DDistance = udg_K2DDistanceLeft[i]
set udg_Knockback2DBounces = udg_K2DBounce[i]
set udg_Knockback2DCollision = udg_K2DCollision[i]
if udg_K2DHeight[i] != 0.00 then
set udg_Knockback2DHeight = GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)
endif
set udg_Knockback2DLoopFX = udg_K2DFXModel[i]
set udg_Knockback2DTime = udg_K2DTimeLeft[i]
set udg_Knockback2DUnbiasedCollision = udg_K2DUnbiasedCollision[i]
call TriggerExecute(gg_trg_Knockback_2D)
set udg_Knockback2DSource = u //in case of a recursive knockback
endif
endloop
endif
set udg_K2DVelocity[i] = udg_K2DVelocity[i] - udg_K2DFriction[i]
else
call TriggerExecute(gg_trg_Knockback_2D_Destroy)
endif
endloop
set u = null
//Disable dummy after the loop finishes so it doesn't interfere with the map
call PauseUnit(udg_K2DDebrisKiller, true)
endfunction
//===========================================================================
function StartKnockback2DTimer takes nothing returns nothing
call TimerStart(udg_K2DTimer, udg_K2DTimeout, true, function Knockback2DLooper)
endfunction
function InitTrig_Knockback_2D_System takes nothing returns nothing
endfunction
//TESH.scrollpos=102
//TESH.alwaysfold=0
//=============================================================
// 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
//TESH.scrollpos=45
//TESH.alwaysfold=0
function Trig_Fireball_Actions_Copy_Func005Func001C takes nothing returns boolean
if ( not ( udg_Missile__EventId == udg_EVENT_MISSILE_COLLIDE_UNIT ) ) then
return false
endif
if ( not ( IsUnitEnemy(udg_Missile__UnitHit, udg_Missile__Owner) == true ) ) then
return false
endif
if ( not ( IsUnitAliveBJ(udg_Missile__UnitHit) == true ) ) then
return false
endif
return true
endfunction
function Trig_Fireball_Actions_Copy_Func005Func002Func002002003001 takes nothing returns boolean
return ( IsUnitEnemy(GetFilterUnit(), udg_Missile__Owner) == true )
endfunction
function Trig_Fireball_Actions_Copy_Func005Func002Func002002003002 takes nothing returns boolean
return ( IsUnitAliveBJ(GetFilterUnit()) == true )
endfunction
function Trig_Fireball_Actions_Copy_Func005Func002Func002002003 takes nothing returns boolean
return GetBooleanAnd( Trig_Fireball_Actions_Copy_Func005Func002Func002002003001(), Trig_Fireball_Actions_Copy_Func005Func002Func002002003002() )
endfunction
function Trig_Fireball_Actions_Copy_Func005Func002Func003A takes nothing returns nothing
call UnitDamageTargetBJ( udg_Missile__Source, GetEnumUnit(), udg_Missile__Damage, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
endfunction
function Trig_Fireball_Actions_Copy_Func005Func002C takes nothing returns boolean
if ( not ( udg_Missile__Data == 1 ) ) then
return false
endif
return true
endfunction
function Trig_Fireball_Actions_Copy_Func005C takes nothing returns boolean
if ( not ( udg_Missile__EventId == udg_EVENT_MISSILE_REMOVE ) ) then
return false
endif
return true
endfunction
function Trig_Fireball_Actions_Copy_Actions takes nothing returns nothing
// 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 ( Trig_Fireball_Actions_Copy_Func005C() ) then
if ( Trig_Fireball_Actions_Copy_Func005Func002C() ) then
// Create more fireballs
// Reference to Missile__Dummy will be removed
// Once you call Run Missile Configurate <gen>
set udg_Missile__TempLoc = GetUnitLoc(udg_Missile__Dummy)
set bj_forLoopAIndex = 1
set bj_forLoopAIndexEnd = 6
loop
exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
call TriggerExecute( gg_trg_Missile_Configurate )
set udg_Missile__Origin = Location(GetLocationX(udg_Missile__TempLoc), GetLocationY(udg_Missile__TempLoc))
set udg_Missile__Impact = PolarProjectionBJ(udg_Missile__Origin, 450.00, ( 60.00 * I2R(GetForLoopIndexA()) ))
set udg_Missile__ImpactZ = 50.00
set udg_Missile__Height = 350.00
set udg_Missile__OriginZ = 50.00
set udg_Missile__Speed = 500.00
set udg_Missile__Damage = 150.00
set udg_Missile__Source = udg_Missile_Source[udg_Missile__EventIndex]
set udg_Missile__Owner = udg_Missile_Owner[udg_Missile__EventIndex]
set udg_Missile__Model = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
set udg_Missile__OnCollideUnit = gg_trg_Fireball_Actions
set udg_Missile__OnRemove = gg_trg_Fireball_Actions
set udg_Missile__Data = 2
call TriggerExecute( gg_trg_Missile )
set bj_forLoopAIndex = bj_forLoopAIndex + 1
endloop
call RemoveLocation(udg_Missile__TempLoc)
set udg_Missile__TempLoc = null
else
set udg_Missile__TempLoc = GetUnitLoc(udg_Missile__Dummy)
set udg_Temp_Group = GetUnitsInRangeOfLocMatching(128.00, udg_Missile__TempLoc, Condition(function Trig_Fireball_Actions_Copy_Func005Func002Func002002003))
call ForGroupBJ( udg_Temp_Group, function Trig_Fireball_Actions_Copy_Func005Func002Func003A )
call AddSpecialEffectLocBJ( udg_Missile__TempLoc, "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl" )
call DestroyEffectBJ( GetLastCreatedEffectBJ() )
call DestroyGroup(udg_Temp_Group)
call RemoveLocation(udg_Missile__TempLoc)
set udg_Missile__TempLoc = null
endif
else
if ( Trig_Fireball_Actions_Copy_Func005Func001C() ) then
// See how easy damaging got.
call UnitDamageTargetBJ( udg_Missile__Source, udg_Missile__UnitHit, udg_Missile__Damage, ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL )
set udg_Missile__WantDestroy = true
else
endif
endif
endfunction
//===========================================================================
function InitTrig_Fireball_Actions_Copy takes nothing returns nothing
set gg_trg_Fireball_Actions_Copy = CreateTrigger( )
call TriggerAddAction( gg_trg_Fireball_Actions_Copy, function Trig_Fireball_Actions_Copy_Actions )
endfunction