Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
AMSAmount | real | Yes | |
AMSReal | real | No | |
Bark_Ability | abilcode | No | |
Bark_First_Hull_Percentage | real | Yes | |
Bark_Hull_Amount | integer | No | |
Bark_Hull_In_Life_Percentage | real | No | |
Bark_Hulls_Per_Level | integer | Yes | |
Bark_Hulls_Protection | real | Yes | |
Bark_Level | integer | No | |
Bark_Spore_Ability | abilcode | No | |
Bark_Spore_Index | integervar | No | |
Bark_Spore_Max_Index | integer | No | |
Bark_Spore_Source | unit | Yes | |
Bark_Spore_Timer | timer | No | |
Bark_Spore_Units | unit | Yes | |
Bark_Temp_Point | location | No | |
Camera_Comand_Index | integervar | No | |
Camera_Comand_String | string | No | |
Camera_Current_Index | integer | No | |
Camera_Default_Zoom | real | No | |
Camera_Entered_String | string | No | |
Camera_i_Is_Locked | boolean | Yes | |
Camera_i_Zoom | real | Yes | |
Camera_Player_Group | force | No | |
Camera_Point_Z | real | No | |
Camera_Timer | timer | No | |
Camera_Unit_Position | location | No | |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CenterPoint | location | No | |
CheckDeathList | integer | Yes | |
CheckDeathTimer | timer | No | |
ClearDamageEvent | trigger | No | |
Create_Hero_Abilitie_List | abilcode | Yes | |
Create_Hero_Index | integer | No | |
Create_Hero_Last_Unit | unit | No | |
Create_Hero_Player_Index | integer | No | |
Create_Hero_Type_List | unitcode | Yes | |
Create_Hero_UDex | integer | 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 | |
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 | |
DeathEvent | real | No | |
DetectRemoveAbility | abilcode | No | |
DetectTransformAbility | abilcode | No | |
Dipper_Phases | integer | Yes | |
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 | |
End_Game_Index | integer | No | |
Entagngeling_Roots_Level_Cost | real | Yes | |
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 | |
Forest_Movement_Ability | abilcode | No | |
Forest_Movement_Level_Cost | real | Yes | |
Guard_Ability | abilcode | No | |
Guard_Cast_Dummy | unit | No | |
Guard_Guard_Type | unitcode | Yes | |
Guard_Level_Cost | real | Yes | |
Guard_Level_Duration | real | Yes | |
Hero_Growth_Current_Index | integervar | No | |
Hero_Growth_Damage_Amount | real | No | |
Hero_Growth_Mana_Percentage | real | No | |
Hero_Growth_Period | real | No | |
Hero_Growth_Timer | timer | No | |
HideDamageFrom | boolean | Yes | |
Impal_Max_Knock_Distance | real | Yes | |
Impale_Ability | abilcode | No | |
Impale_Damage | real | Yes | |
Impale_Direction | real | Yes | |
Impale_Distance_Per_Loop | real | No | |
Impale_Level_Cost | real | Yes | |
Impale_Periode | real | No | |
Impale_Pick_Range | real | Yes | |
Impale_TargetX | real | Yes | |
Impale_TargetY | real | Yes | |
Impale_Temp_LocA | location | No | |
Impale_Temp_LocB | location | No | |
IsDamageSpell | boolean | No | |
IsUnitAlive | boolean | Yes | |
IsUnitBeingKnockedBack | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
Ivy_Ability | abilcode | No | |
Ivy_Current_Index | integervar | No | |
Ivy_FSX_String | string | No | |
Ivy_FSX_String_Prefinished | string | No | |
Ivy_i_Current_Charge | real | Yes | |
Ivy_i_Duration | real | Yes | |
Ivy_i_FSX | effect | Yes | |
Ivy_i_Level | integer | Yes | |
Ivy_i_Wards | unit | Yes | |
Ivy_Level_Cost | real | Yes | |
Ivy_Level_Duration | real | Yes | |
Ivy_Level_Heal_Max | real | Yes | |
Ivy_Level_Pick_Range | real | Yes | |
Ivy_Level_Rates | real | Yes | |
Ivy_Max_Index | integer | No | |
Ivy_Periode | real | No | |
Ivy_Target_Group | group | No | |
Ivy_Target_Unit | unit | No | |
Ivy_Temp_Point | location | No | |
Ivy_Timer | timer | No | |
Ivy_Ward_Unit_Type | unitcode | 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 | |
KillerOfUnit | unit | Yes | |
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 | |
LH_CurrentIndex | integervar | No | |
LH_Duration | real | Yes | |
LH_DurationSet | real | No | |
LH_Interval | real | No | |
LH_Lightning | lightning | Yes | |
LH_LightningAlpha | real | Yes | |
LH_LightningFadeInterval | real | Yes | |
LH_LightningName | string | Yes | |
LH_LightningNameSet | string | No | |
LH_Location | location | No | |
LH_MaxIndex | integer | No | |
LH_PointSource | location | Yes | |
LH_PointSourceSet | location | No | |
LH_PointSourceZ | real | Yes | |
LH_PointSourceZSet | real | No | |
LH_PointTarget | location | Yes | |
LH_PointTargetSet | location | No | |
LH_PointTargetZ | real | Yes | |
LH_PointTargetZSet | real | No | |
LH_UnitSource | unit | Yes | |
LH_UnitSourceSet | unit | No | |
LH_UnitTarget | unit | Yes | |
LH_UnitTargetSet | unit | No | |
LH_UnitZ | real | No | |
LoopInt | integer | No | |
Mana_Control_Ability | abilcode | No | |
Mana_Control_Ability_Display | abilcode | No | |
Mana_Control_Bonus | real | Yes | |
Mana_Flow_Ally_Reduction | real | No | |
Mana_Flow_i_FX_Caster | effect | Yes | |
Mana_Flow_i_FX_Target | effect | Yes | |
Mana_Flow_Interval | real | No | |
Mana_Flow_Level_Range | real | Yes | |
Mana_Flow_ManaPerSec | real | Yes | |
Mana_Flow_Percentage | real | No | |
Mana_Meter_Current_Index | integer | No | |
Mana_Meter_IDex2UDex | integer | No | |
Mana_Meter_Timer | timer | No | |
Mana_Meter_UDex_Is_On | boolean | Yes | |
Mana_Meter_UDex_New | real | Yes | |
Mana_Meter_UDex_Old | real | Yes | |
Mana_Orb_Collision_Range | real | No | |
Mana_Orb_Current_Index | integervar | No | |
Mana_Orb_Duration | real | No | |
Mana_Orb_i_Duration | real | Yes | |
Mana_Orb_i_Mana_Amount | real | Yes | |
Mana_Orb_i_Units | unit | Yes | |
Mana_Orb_Loc | location | No | |
Mana_Orb_Mana_Amount | real | No | |
Mana_Orb_Max_Index | integer | No | |
Mana_Orb_Owner | player | No | |
Mana_Orb_Period | real | No | |
Mana_Orb_Target | unit | No | |
Mana_Orb_Target_Group | group | No | |
Mana_Orb_Timer | timer | No | |
Mana_Orb_Unit_Loc | location | No | |
Mana_Orb_Unit_Type | unitcode | No | |
Mana_Sens_Ability | abilcode | No | |
Mana_Sens_Ability_Amount | integer | No | |
Mana_Sens_Ability_Array | abilcode | Yes | |
Mana_Sens_Ability_Cooldown | abilcode | No | |
Mana_Sens_Add_New_Caster | boolean | No | |
Mana_Sens_Cast_Aiming_Position | location | No | |
Mana_Sens_Cast_Level | integer | No | |
Mana_Sens_Cast_Range | real | No | |
Mana_Sens_Cast_Target_Position | location | No | |
Mana_Sens_Cast_UDex | integer | No | |
Mana_Sens_Check_Index | integervar | No | |
Mana_Sens_Current_Index | integervar | No | |
Mana_Sens_i_Units | unit | Yes | |
Mana_Sens_Level | integer | No | |
Mana_Sens_Level_Cooldown | real | Yes | |
Mana_Sens_Level_Cost | real | Yes | |
Mana_Sens_Level_Missile_Range | real | Yes | |
Mana_Sens_Level_Search_Range | real | Yes | |
Mana_Sens_Max_Index | integer | No | |
Mana_Sens_Nearest_Range | real | No | |
Mana_Sens_New_Ability_Index | integer | No | |
Mana_Sens_Periode | real | No | |
Mana_Sens_Range_Per_Ability | real | No | |
Mana_Sens_Target | unit | No | |
Mana_Sens_Target_Group | group | No | |
Mana_Sens_Target_Position | location | No | |
Mana_Sens_Target_Range | real | No | |
Mana_Sens_Temp_Group | group | No | |
Mana_Sens_Timer | timer | No | |
Mana_Sens_UDex | integer | No | |
Mana_Sens_UDex_Ability_Index | integer | Yes | |
Mana_Sens_UDex_CD_Duration | real | Yes | |
Mana_Sens_UDex_Target | unit | Yes | |
Mana_Sens_Unit_Position | location | No | |
Mana_Source_Type | unitcode | No | |
Mana_Sphere_Duration_Per_Level | real | Yes | |
Mana_Sphere_Level_Cost | real | Yes | |
Missile__Acceleration | real | No | |
Missile__Angle | real | No | |
Missile__Arc | real | 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__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__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_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_ImpactX | real | Yes | |
Missile_ImpactY | real | Yes | |
Missile_ImpactZ | real | Yes | |
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 | |
Multi_Board_Board | multiboard | No | |
Multi_Board_Event_Die | real | No | |
Multi_Board_Event_Fill | real | No | |
Multi_Board_Event_Gather | real | No | |
Multi_Board_Event_Icon | real | No | |
Multi_Board_Event_Kill | real | No | |
Multi_Board_Game_Is_Runing | boolean | No | |
Multi_Board_Hero_Icons | string | Yes | |
Multi_Board_Hour | integer | No | |
Multi_Board_i_Deaths | integer | Yes | |
Multi_Board_i_Filled | real | Yes | |
Multi_Board_i_Gathered | real | Yes | |
Multi_Board_i_Kills | integer | Yes | |
Multi_Board_i_Player_Row | integer | Yes | |
Multi_Board_Minute | integer | No | |
Multi_Board_Minute_String | string | No | |
Multi_Board_Player_Index | integer | No | |
Multi_Board_Row | integer | No | |
Multi_Board_Second | integer | No | |
Multi_Board_Second_String | string | No | |
Multi_Board_Time | integer | No | |
Multi_Board_Timer | timer | No | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
Overgrow_Ability | abilcode | No | |
Overgrow_Ability_Damage | abilcode | No | |
Overgrow_Ability_Slow | abilcode | No | |
Overgrow_Level_Cost | real | Yes | |
Overgrow_Level_Duration | real | Yes | |
Overgrown_Dummy_Type | unitcode | No | |
Overgrown_Temp_Ward | unit | No | |
Parasite_Ability | abilcode | No | |
Parasite_Check_Index | integervar | No | |
Parasite_Current_Index | integervar | No | |
Parasite_Damage_Hit | real | Yes | |
Parasite_Damage_Multyplicator | real | Yes | |
Parasite_Damage_Periode | real | Yes | |
Parasite_Dummy | unit | No | |
Parasite_Expl_Damage_Ability | abilcode | No | |
Parasite_Expl_Slow_Ability | abilcode | No | |
Parasite_FX | string | No | |
Parasite_Has_Multieble | boolean | No | |
Parasite_i_Current_Stage | integer | Yes | |
Parasite_i_DOT | real | Yes | |
Parasite_i_Duration | real | Yes | |
Parasite_i_Hosts | unit | Yes | |
Parasite_i_Level | integer | Yes | |
Parasite_i_Max_Stage | integer | Yes | |
Parasite_i_Source | unit | Yes | |
Parasite_Level_Cost | real | Yes | |
Parasite_Max_Index | integer | No | |
Parasite_Parasite_Duration | real | Yes | |
Parasite_Parasite_Type | unitcode | Yes | |
Parasite_Periode | real | No | |
Parasite_Slow_Ability | abilcode | No | |
Parasite_Stage_Amount_Level | integer | Yes | |
Parasite_Stage_Dur_Per_Level | real | Yes | |
Parasite_Temp_Loc | location | No | |
Parasite_Timer | timer | No | |
Players_Current_Index | integervar | No | |
Players_Die_Check_Index | integervar | No | |
Players_Dying_Index | integervar | No | |
Players_Forces | force | Yes | |
Players_Goal_Units | unit | Yes | |
Players_i_Hero_Icon_Index | integer | Yes | |
Players_i_Heros | unit | Yes | |
Players_i_Member_Of_Force | integer | Yes | |
Players_i_Player_Name | string | Yes | |
Players_i_Revive_Mana | real | Yes | |
Players_i_Revive_Timer | timer | Yes | |
Players_i_Revive_Window | timerdialog | Yes | |
Players_Index2Player | integer | Yes | |
Players_Max_Index | integer | No | |
Players_Player2Intex | integer | Yes | |
Players_Revive_Index | integervar | No | |
Players_Revive_PointA | location | No | |
Players_Revive_PointB | location | No | |
Players_String_Color | string | Yes | |
Poisen_Mana_Ability | abilcode | No | |
Poisen_Mana_Buff | buffcode | No | |
Poisen_Mana_Current_Index | integervar | No | |
Poisen_Mana_Debuff_Ability | abilcode | No | |
Poisen_Mana_i_Duration | real | Yes | |
Poisen_Mana_i_Index2Udex | integer | Yes | |
Poisen_Mana_i_Level | integer | Yes | |
Poisen_Mana_i_Souce | unit | Yes | |
Poisen_Mana_Level_Cost | real | Yes | |
Poisen_Mana_Level_Duration | real | Yes | |
Poisen_Mana_Level_Percentage | real | Yes | |
Poisen_Mana_Max_Index | integer | No | |
Poisen_Mana_Periode | real | No | |
Poisen_Mana_Target_Dex | integer | No | |
Poisen_Mana_Timer | timer | No | |
Poisen_Mana_u_Is_Poisend | boolean | Yes | |
Poisen_Mana_u_Udex2Index | integer | Yes | |
Poisen_Mana_Units | unit | Yes | |
Pop_Bean_Ability | abilcode | No | |
Pop_Bean_Bean_Type | unitcode | No | |
Pop_Bean_Current_Index | integervar | No | |
Pop_Bean_Duration_Cut | real | No | |
Pop_Bean_i_Creator | unit | Yes | |
Pop_Bean_i_Duration | real | Yes | |
Pop_Bean_i_Level | integer | Yes | |
Pop_Bean_i_Next_Duation | real | Yes | |
Pop_Bean_i_Owner | player | Yes | |
Pop_Bean_i_Units | unit | Yes | |
Pop_Bean_Is_Triggerd | boolean | No | |
Pop_Bean_Level_Chance | real | Yes | |
Pop_Bean_Level_Cost | real | Yes | |
Pop_Bean_Level_Damage | real | Yes | |
Pop_Bean_Level_Duration | real | Yes | |
Pop_Bean_Level_Range | real | Yes | |
Pop_Bean_Level_Spread_Amount | integer | Yes | |
Pop_Bean_Max_Index | integer | No | |
Pop_Bean_Periode | real | No | |
Pop_Bean_Spread_Buffer | real | No | |
Pop_Bean_Spread_Index | integervar | No | |
Pop_Bean_Spread_Position | location | No | |
Pop_Bean_Spread_Range | real | No | |
Pop_Bean_String_Special_Effect | string | No | |
Pop_Bean_Target | unit | No | |
Pop_Bean_Target_Group | group | No | |
Pop_Bean_Target_Position | location | No | |
Pop_Bean_Temp_Group | group | No | |
Pop_Bean_Timer | timer | No | |
Pop_Bean_Trigger_Distance | real | No | |
Pop_Bean_Unit_Position | location | No | |
Quicken_Ability | abilcode | No | |
Quicken_Current_Index | integer | No | |
Quicken_i_Current_Bonus | real | Yes | |
Quicken_i_Units | unit | Yes | |
Quicken_Index2UDex | integer | Yes | |
Quicken_Level | integer | No | |
Quicken_Level_Bonus_Increse | real | Yes | |
Quicken_Level_Max_Increase | real | Yes | |
Quicken_Loop_Bonus | real | No | |
Quicken_Max_Index | integer | No | |
Quicken_Periode | real | No | |
Quicken_Timer | timer | No | |
Quicken_UDex2Index | integer | Yes | |
Quicken_UDex_Is_Moving | boolean | Yes | |
Radians_QuarterPi | real | No | |
Radians_QuarterTurn | real | No | |
Radians_Turn | real | No | |
Rebark_Ability | abilcode | No | |
Rebark_AttackSpeed_Ability | abilcode | No | |
Rebark_Is_Reabarking | boolean | Yes | |
Rebark_Level_Cost | real | Yes | |
Rebark_Regen_Ability | abilcode | No | |
Rooting_Abilities | abilcode | Yes | |
Rooting_Cost | real | No | |
Rooting_Index | integer | No | |
Rooting_UDex_Is_Rooted | boolean | Yes | |
Source_Border | real | No | |
Source_Distance | real | No | |
Source_Group | group | No | |
Source_Index | integervar | No | |
Source_Max_X | real | No | |
Source_Max_Y | real | No | |
Source_Min_X | real | No | |
Source_Min_Y | real | No | |
Source_Point | location | No | |
Source_Sources_In_Range | boolean | No | |
Source_Temp_X | real | No | |
Source_Temp_Y | real | No | |
Spawn_PointA | location | No | |
Spawn_PointB | location | No | |
Spell__Ability | abilcode | No | |
Spell__Caster | unit | No | |
Spell__CasterOwner | player | No | |
Spell__CastPoint | location | No | |
Spell__Channeling | boolean | No | |
Spell__Completed | boolean | No | |
Spell__DummyOwner | player | No | |
Spell__DummyType | unitcode | No | |
Spell__Duration | real | No | |
Spell__DurationPerLevel | real | No | |
Spell__Expired | boolean | No | |
Spell__Filter_AllowAlly | boolean | No | |
Spell__Filter_AllowDead | boolean | No | |
Spell__Filter_AllowEnemy | boolean | No | |
Spell__Filter_AllowFlying | boolean | No | |
Spell__Filter_AllowHero | boolean | No | |
Spell__Filter_AllowLiving | boolean | No | |
Spell__Filter_AllowMagicImmune | boolean | No | |
Spell__Filter_AllowMechanical | boolean | No | |
Spell__Filter_AllowNonHero | boolean | No | |
Spell__Filter_AllowStructure | boolean | No | |
Spell__Hash | hashtable | No | |
Spell__Index | integer | No | |
Spell__InRange | real | No | |
Spell__InRangeCount | integer | No | |
Spell__InRangeGroup | group | No | |
Spell__InRangeMax | integer | No | |
Spell__InRangePoint | location | No | |
Spell__InRangeUnit | unit | No | |
Spell__InRangeUnits | unit | Yes | |
Spell__Interval | real | No | |
Spell__Level | integer | No | |
Spell__LevelMultiplier | real | No | |
Spell__Running | boolean | No | |
Spell__StartDuration | boolean | No | |
Spell__Target | unit | No | |
Spell__TargetGroup | group | No | |
Spell__TargetPoint | location | No | |
Spell__Time | real | No | |
Spell__Trigger_InRangeFilter | trigger | No | |
Spell__Trigger_OnCast | trigger | No | |
Spell__Trigger_OnChannel | trigger | No | |
Spell__Trigger_OnEffect | trigger | No | |
Spell__Trigger_OnFinish | trigger | No | |
Spell__Trigger_OnLoop | trigger | No | |
Spell__UseTargetGroup | boolean | No | |
Spell__WakeTargets | boolean | No | |
Spell_i_AllowAlly | boolean | Yes | |
Spell_i_AllowDead | boolean | Yes | |
Spell_i_AllowEnemy | boolean | Yes | |
Spell_i_AllowFlying | boolean | Yes | |
Spell_i_AllowHero | boolean | Yes | |
Spell_i_AllowLiving | boolean | Yes | |
Spell_i_AllowMagicImmune | boolean | Yes | |
Spell_i_AllowMechanical | boolean | Yes | |
Spell_i_AllowNonHero | boolean | Yes | |
Spell_i_AllowStructure | boolean | Yes | |
Spell_i_Caster | unit | Yes | |
Spell_i_Channeling | boolean | Yes | |
Spell_i_Completed | boolean | Yes | |
Spell_i_Duration | real | Yes | |
Spell_i_EventType | integer | Yes | |
Spell_i_GroupN | integer | No | |
Spell_i_GroupStack | group | Yes | |
Spell_i_Head | integer | Yes | |
Spell_i_InRangeFilter | trigger | Yes | |
Spell_i_Instances | integer | No | |
Spell_i_LastTime | real | Yes | |
Spell_i_Level | integer | Yes | |
Spell_i_Linked | boolean | Yes | |
Spell_i_OnCastStack | trigger | Yes | |
Spell_i_OnChannelStack | trigger | Yes | |
Spell_i_OnEffectStack | trigger | Yes | |
Spell_i_OnFinishStack | trigger | Yes | |
Spell_i_OnLoopStack | trigger | Yes | |
Spell_i_PreloadDummy | unit | No | |
Spell_i_Recycle | integer | No | |
Spell_i_RecycleList | integer | Yes | |
Spell_i_Stack | integer | Yes | |
Spell_i_StackN | integer | No | |
Spell_i_StackRef | integer | Yes | |
Spell_i_Target | unit | Yes | |
Spell_i_TargetGroup | group | Yes | |
Spell_i_TargetX | real | Yes | |
Spell_i_TargetY | real | Yes | |
Spell_i_Time | real | Yes | |
Spell_i_Timer | timer | No | |
Spell_i_UseTG | boolean | Yes | |
SpellDamageAbility | abilcode | No | |
SummonerOfUnit | unit | Yes | |
Temp_Dummy | unit | No | |
Temp_Int | integervar | No | |
TempPoint | location | No | |
TempX | real | No | |
TempY | real | No | |
Thorns_Ability | abilcode | No | |
Thorns_Ability_Ally | abilcode | No | |
Thorns_Ability_Aura | abilcode | No | |
Thorns_Ability_Enemy | abilcode | No | |
Thorns_Ability_Level | integer | No | |
Thorns_Add_New_Caster | boolean | No | |
Thorns_Caster_Current_Index | integervar | No | |
Thorns_Caster_i_Duration | real | Yes | |
Thorns_Caster_i_Growth_Level | integer | Yes | |
Thorns_Caster_i_Units | unit | Yes | |
Thorns_Caster_Max_Index | integer | No | |
Thorns_Caster_Timer | timer | No | |
Thorns_Caster_Unit_Type | unitcode | No | |
Thorns_Caster_Unit_Type_Rooted | unitcode | No | |
Thorns_Check_Index | integervar | No | |
Thorns_Current_Index | integervar | No | |
Thorns_i_Duration | real | Yes | |
Thorns_i_Jump_Amount | integer | Yes | |
Thorns_i_Level | integer | Yes | |
Thorns_i_Max_Duration | real | Yes | |
Thorns_i_Source | unit | Yes | |
Thorns_i_Units | unit | Yes | |
Thorns_Level_Duration | real | Yes | |
Thorns_Level_Increase_Duration | real | Yes | |
Thorns_Level_Jump_Amount | integer | Yes | |
Thorns_Level_Max_Duration | real | Yes | |
Thorns_Level_Range | real | Yes | |
Thorns_Max_Index | integer | No | |
Thorns_Periode | real | No | |
Thorns_Target_Group | group | No | |
Thorns_Target_Unit | unit | No | |
Thorns_Temp_Group | group | No | |
Thorns_Timer | timer | No | |
Thorns_UDex_Has_Thorns | boolean | Yes | |
Thorns_Unit_Position | location | No | |
UDex | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexUnits | unit | Yes | |
UMovNext | integer | Yes | |
UMovPrev | integer | Yes | |
UnitDamageRegistered | boolean | Yes | |
UnitInAction | boolean | Yes | |
UnitInActionEvent | real | No | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitMoving | boolean | Yes | |
UnitMovingEvent | real | No | |
UnitMovingX | real | Yes | |
UnitMovingY | real | Yes | |
UnitTypeEvent | real | No | |
Victory_Group | group | No | |
Weather_Abilities_Array | abilcode | Yes | |
Weather_Check_Index | integervar | No | |
Weather_Current_Index | integer | No | |
Weather_Effect_Array | weathereffect | Yes | |
Weather_Hero_Gain | real | Yes | |
Weather_Hero_Gain_Rooted | real | Yes | |
Weather_Index1 | integervar | No | |
Weather_Index2 | integervar | No | |
Weather_Last_Created_Source | unit | No | |
Weather_Loop_Index | integer | No | |
Weather_Loop_Periode | real | No | |
Weather_Loop_Timer | timer | No | |
Weather_Next_Integer | integer | No | |
Weather_Plus | integer | No | |
Weather_Random_Real | real | No | |
Weather_Real_Liste | real | Yes | |
Weather_Source_Abilities_Array | abilcode | Yes | |
Weather_Source_Amount | integer | No | |
Weather_Source_Distance | real | No | |
Weather_Source_Has_In_Range | boolean | No | |
Weather_Source_Point | location | No | |
Weather_Source_Temp_Group | group | No | |
Weather_Source_Timer | timer | No | |
Weather_Sources_Group | group | No | |
Weather_Timer | timer | No | |
WorldMaxX | real | No | |
WorldMaxY | real | No |
//TESH.scrollpos=49
//TESH.alwaysfold=0
//===========================================================================
function UnitEventDestroyGroup takes integer i returns nothing
if udg_CargoTransportGroup[i] != null then
call DestroyGroup(udg_CargoTransportGroup[i])
set udg_CargoTransportGroup[i] = null
endif
endfunction
function UnitEventCheckDeath takes nothing returns nothing
local integer i = udg_CheckDeathList[0]
set udg_CheckDeathList[0] = 0
loop
exitwhen i == 0
if udg_IsUnitNew[i] then
//The unit was just created.
set udg_IsUnitNew[i] = false
elseif udg_IsUnitAlive[i] then
//The unit has started reincarnating.
set udg_IsUnitReincarnating[i] = true
set udg_IsUnitAlive[i] = false
set udg_UDex = i
set udg_DeathEvent = 0.50
set udg_DeathEvent = 0.00
endif
set i = udg_CheckDeathList[i]
endloop
endfunction
function UnitEventCheckDeathProxy takes integer i returns nothing
if udg_CheckDeathList[0] == 0 then
call TimerStart(udg_CheckDeathTimer, 0.00, false, function UnitEventCheckDeath)
endif
set udg_CheckDeathList[i] = udg_CheckDeathList[0]
set udg_CheckDeathList[0] = i
endfunction
function UnitEventOnUnload takes nothing returns nothing
local integer i = udg_UDex
call GroupRemoveUnit(udg_CargoTransportGroup[GetUnitUserData(udg_CargoTransportUnit[i])], udg_UDexUnits[i])
set udg_IsUnitBeingUnloaded[i] = true
set udg_CargoEvent = 0.00
set udg_CargoEvent = 2.00
set udg_CargoEvent = 0.00
set udg_IsUnitBeingUnloaded[i] = false
if not IsUnitLoaded(udg_UDexUnits[i]) or IsUnitType(udg_CargoTransportUnit[i], UNIT_TYPE_DEAD) or GetUnitTypeId(udg_CargoTransportUnit[i]) == 0 then
set udg_CargoTransportUnit[i] = null
endif
endfunction
function UnitEventOnDeath takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_UDex != 0 then
set udg_KillerOfUnit[udg_UDex] = GetKillingUnit() //Added 29 May 2017 for GIMLI_2
set udg_IsUnitAlive[udg_UDex] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
set udg_KillerOfUnit[udg_UDex] = null
if udg_CargoTransportUnit[udg_UDex] != null then
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnOrder takes nothing returns boolean
local integer pdex = udg_UDex
local unit u = GetFilterUnit()
local integer i = GetUnitUserData(u)
if i > 0 then
set udg_UDex = i
if GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
if not udg_IsUnitRemoved[i] then
set udg_IsUnitRemoved[i] = true
set udg_IsUnitAlive[i] = false
set udg_SummonerOfUnit[i] = null
//For backwards-compatibility:
set udg_DeathEvent = 0.00
set udg_DeathEvent = 3.00
set udg_DeathEvent = 0.00
//Fire deindex event for UDex:
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 2.00
set udg_UnitIndexEvent = 0.00
set udg_UDexNext[udg_UDexPrev[i]] = udg_UDexNext[i]
set udg_UDexPrev[udg_UDexNext[i]] = udg_UDexPrev[i]
// Recycle the index for later use
set udg_UDexUnits[i] = null
set udg_UDexPrev[i] = udg_UDexLastRecycled
set udg_UDexLastRecycled = i
call UnitEventDestroyGroup(i)
endif
elseif not udg_IsUnitAlive[i] then
if not IsUnitType(u, UNIT_TYPE_DEAD) then
set udg_IsUnitAlive[i] = true
set udg_DeathEvent = 0.00
set udg_DeathEvent = 2.00
set udg_DeathEvent = 0.00
set udg_IsUnitReincarnating[i] = false
endif
elseif IsUnitType(u, UNIT_TYPE_DEAD) then
if udg_IsUnitNew[i] then
//This unit was created as a corpse.
set udg_IsUnitAlive[i] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
elseif udg_CargoTransportUnit[i] == null or not IsUnitType(u, UNIT_TYPE_HERO) then
//The unit may have just started reincarnating.
call UnitEventCheckDeathProxy(i)
endif
elseif GetUnitAbilityLevel(u, udg_DetectTransformAbility) == 0 then
call UnitAddAbility(u, udg_DetectTransformAbility)
set udg_UnitTypeEvent = 0.00
set udg_UnitTypeEvent = 1.00
set udg_UnitTypeEvent = 0.00
endif
if udg_CargoTransportUnit[i] != null and not udg_IsUnitBeingUnloaded[i] and not IsUnitLoaded(u) or IsUnitType(u, UNIT_TYPE_DEAD) then
call UnitEventOnUnload()
endif
set udg_UDex = pdex
endif
set u = null
return false
endfunction
function UnitEventOnSummon takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_IsUnitNew[udg_UDex] then
set udg_SummonerOfUnit[udg_UDex] = GetSummoningUnit()
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 0.50
set udg_UnitIndexEvent = 0.00
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnLoad takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = GetUnitUserData(GetTriggerUnit())
local integer index
if i != 0 then
set udg_UDex = i
if udg_CargoTransportUnit[i] != null then
call UnitEventOnUnload()
endif
//Loaded corpses do not issue an order when unloaded, therefore must
//use the enter-region event method taken from Jesus4Lyf's Transport.
if not udg_IsUnitAlive[i] then
call SetUnitX(udg_UDexUnits[i], udg_WorldMaxX)
call SetUnitY(udg_UDexUnits[i], udg_WorldMaxY)
endif
set udg_CargoTransportUnit[i] = GetTransportUnit()
set index = GetUnitUserData(udg_CargoTransportUnit[i])
if udg_CargoTransportGroup[index] == null then
set udg_CargoTransportGroup[index] = CreateGroup()
endif
call GroupAddUnit(udg_CargoTransportGroup[index], udg_UDexUnits[i])
set udg_CargoEvent = 0.00
set udg_CargoEvent = 1.00
set udg_CargoEvent = 0.00
set udg_UDex = pdex
endif
return false
endfunction
function UnitEventEnter takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = udg_UDexLastRecycled
local unit u = GetFilterUnit()
if udg_UnitIndexerEnabled and GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
//Generate a unique integer index for this unit
if i == 0 then
set i = udg_UDexMax + 1
set udg_UDexMax = i
else
set udg_UDexLastRecycled = udg_UDexPrev[i]
endif
//Link index to unit, unit to index
set udg_UDexUnits[i] = u
call SetUnitUserData(u, i)
//For backwards-compatibility, add the unit to a linked list
set udg_UDexNext[i] = udg_UDexNext[0]
set udg_UDexPrev[udg_UDexNext[0]] = i
set udg_UDexNext[0] = i
set udg_UDexPrev[i] = 0
call UnitAddAbility(u, udg_DetectRemoveAbility)
call UnitMakeAbilityPermanent(u, true, udg_DetectRemoveAbility)
call UnitAddAbility(u, udg_DetectTransformAbility)
set udg_IsUnitNew[i] = true
set udg_IsUnitAlive[i] = true
set udg_IsUnitRemoved[i] = false
set udg_IsUnitReincarnating[i] = false
set udg_IsUnitPreplaced[i] = udg_IsUnitPreplaced[0] //Added 29 May 2017 for Spellbound
call UnitEventCheckDeathProxy(i)
//Fire index event for UDex
set udg_UDex = i
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 1.00
set udg_UnitIndexEvent = 0.00
else
set udg_UDex = GetUnitUserData(u)
if udg_CargoTransportUnit[udg_UDex] != null and not IsUnitLoaded(u) then
//The unit was dead, but has re-entered the map.
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
set u = null
return false
endfunction
//===========================================================================
function UnitEventInit takes nothing returns nothing
local integer i = 16
local player p
local trigger t = CreateTrigger()
local trigger summon = CreateTrigger()
local trigger death = CreateTrigger()
local trigger load = CreateTrigger()
local region re = CreateRegion()
local rect r = GetWorldBounds()
set udg_WorldMaxX = GetRectMaxX(r)
set udg_WorldMaxY = GetRectMaxY(r)
call RegionAddRect(re, r)
call RemoveRect(r)
call UnitEventDestroyGroup(0)
call UnitEventDestroyGroup(1)
set udg_UnitIndexerEnabled = true
call TriggerRegisterEnterRegion(CreateTrigger(), re, Filter(function UnitEventEnter))
call TriggerAddCondition(summon, Filter(function UnitEventOnSummon))
call TriggerAddCondition(death, Filter(function UnitEventOnDeath))
call TriggerAddCondition(load, Filter(function UnitEventOnLoad))
loop
set i = i - 1
set p = Player(i)
call SetPlayerAbilityAvailable(p, udg_DetectRemoveAbility, false)
call SetPlayerAbilityAvailable(p, udg_DetectTransformAbility, false)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, Filter(function UnitEventOnOrder))
call TriggerRegisterPlayerUnitEvent(summon, p, EVENT_PLAYER_UNIT_SUMMON, null)
call TriggerRegisterPlayerUnitEvent(death, p, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(load, p, EVENT_PLAYER_UNIT_LOADED, null)
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, Filter(function UnitEventEnter))
exitwhen i == 0
endloop
set summon = null
set death = null
set load = null
set re = null
set p = null
set r = null
set t = null
endfunction
function InitTrig_Unit_Event takes nothing returns nothing
endfunction
//TESH.scrollpos=148
//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 DmgEvOnExpire takes nothing returns nothing
set udg_DmgEvStarted = false
call CheckDamagedLifeEvent(true)
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 = udg_DmgEvRecursionN - 1
local string s
local real prevAmount
local real life
local real prevLife
local unit u
call CheckDamagedLifeEvent(false) //in case the unit state event failed and the 0.00 second timer hasn't yet expired
if i >= 0 then
if 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
endif
set udg_DmgEvRecursionN = i + 2
set u = GetTriggerUnit()
set prevAmount = GetEventDamage()
set udg_DamageEventSource = GetEventDamageSource()
set udg_DamageEventAmount = prevAmount
set udg_DamageEventTarget = u
set udg_DamageEventType = udg_NextDamageType
set udg_NextDamageType = 0
set udg_DamageEventOverride = udg_NextDamageOverride
set udg_NextDamageOverride = false
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
if not udg_DmgEvStarted then
set udg_DmgEvStarted = true
call TimerStart(udg_DmgEvTimer, 0.00, false, function DmgEvOnExpire)
endif
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
set u = null
endif
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(15), 'uloc', 0, 0, 0)
local integer i = 16
//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=0
//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 SpellIndexGetVars takes integer i returns nothing
set udg_Spell__Index = i
set udg_Spell__Caster = udg_Spell_i_Caster[i]
set udg_Spell__CasterOwner = GetOwningPlayer(udg_Spell__Caster)
set udg_Spell__Level = udg_Spell_i_Level[i]
set udg_Spell__LevelMultiplier = udg_Spell__Level //Spell__LevelMultiplier is a real variable.
set udg_Spell__Target = udg_Spell_i_Target[i]
//Assign the saved coordinates to the static locations
call MoveLocation(udg_Spell__CastPoint, GetUnitX(udg_Spell__Caster), GetUnitY(udg_Spell__Caster))
if udg_Spell__Target == null then
call MoveLocation(udg_Spell__TargetPoint, udg_Spell_i_TargetX[i], udg_Spell_i_TargetY[i])
//call BJDebugMsg("Target X: "+ R2S(udg_Spell_i_TargetX[i]))
//call BJDebugMsg("Target Y: "+ R2S(udg_Spell_i_TargetY[i]))
else
call MoveLocation(udg_Spell__TargetPoint, GetUnitX(udg_Spell__Target), GetUnitY(udg_Spell__Target))
//call BJDebugMsg("Target is unit")
endif
set udg_Spell__TargetGroup = udg_Spell_i_TargetGroup[i]
set udg_Spell__Completed = udg_Spell_i_Completed[i]
set udg_Spell__Channeling = udg_Spell_i_Channeling[i]
endfunction
function SpellSetFilters takes integer i returns nothing
set udg_Spell_i_AllowEnemy[i] = udg_Spell__Filter_AllowEnemy
set udg_Spell_i_AllowAlly[i] = udg_Spell__Filter_AllowAlly
set udg_Spell_i_AllowDead[i] = udg_Spell__Filter_AllowDead
set udg_Spell_i_AllowLiving[i] = udg_Spell__Filter_AllowLiving
set udg_Spell_i_AllowMagicImmune[i] = udg_Spell__Filter_AllowMagicImmune
set udg_Spell_i_AllowMechanical[i] = udg_Spell__Filter_AllowMechanical
set udg_Spell_i_AllowStructure[i] = udg_Spell__Filter_AllowStructure
set udg_Spell_i_AllowFlying[i] = udg_Spell__Filter_AllowFlying
set udg_Spell_i_AllowHero[i] = udg_Spell__Filter_AllowHero
set udg_Spell_i_AllowNonHero[i] = udg_Spell__Filter_AllowNonHero
endfunction
function SpellIndexDestroy takes integer i returns nothing
local integer indexOf
local integer index
if udg_Spell_i_RecycleList[i] >= 0 then
return
endif
//If the caster is still channeling on the spell, don't destroy until it's finished:
if not udg_Spell_i_Channeling[i] then
set index = udg_Spell_i_Head[i]
set udg_Spell_i_RecycleList[i] = udg_Spell_i_Recycle
set udg_Spell_i_Recycle = i
//Reset things to defaults:
set udg_Spell_i_Time[i] = 0.00
set udg_Spell_i_LastTime[i] = 0.00
set udg_Spell_i_Duration[i] = 0.00
set udg_Spell_i_Completed[i] = false
set udg_Spell_i_Caster[i] = null
set udg_Spell_i_Target[i] = null
set udg_Spell_i_OnLoopStack[i] = null
//Recycle any applicable target unit group.
if udg_Spell_i_TargetGroup[i] != null then
call GroupClear(udg_Spell_i_TargetGroup[i])
set udg_Spell_i_GroupStack[udg_Spell_i_GroupN] = udg_Spell_i_TargetGroup[i]
set udg_Spell_i_GroupN = udg_Spell_i_GroupN + 1
set udg_Spell_i_TargetGroup[i] = null
endif
//Clear any user-specified data in the hashtable:
call FlushChildHashtable(udg_Spell__Hash, i)
//call BJDebugMsg("Destroying index: " + I2S(i))
endif
set indexOf = udg_Spell_i_StackRef[i]
if indexOf >= 0 then
set index = udg_Spell_i_StackN - 1
set udg_Spell_i_StackN = index
set udg_Spell_i_StackRef[udg_Spell_i_Stack[index]] = indexOf
set udg_Spell_i_Stack[indexOf] = udg_Spell_i_Stack[index]
if index == 0 then
//If no more spells require the timer, pause it.
call PauseTimer(udg_Spell_i_Timer)
endif
set udg_Spell_i_StackRef[i] = -1
endif
endfunction
function SpellTriggerExecute takes integer i, trigger t returns real
local real d = udg_Spell_i_Duration[i]
set udg_Spell__Duration = d
set udg_Spell__Time = 0.00
if t != null then
set udg_Spell__Trigger_OnLoop = null
set udg_Spell__Expired = d <= 0.00 //If the duration is <= 0, the spell has expired.
call SpellIndexGetVars(i)
if TriggerEvaluate(t) then
call TriggerExecute(t)
endif
if udg_Spell__Trigger_OnLoop != null then
set udg_Spell_i_OnLoopStack[i] = udg_Spell__Trigger_OnLoop
endif
//The remaining lines in this function process the duration specified by the user.
if udg_Spell__StartDuration then
set udg_Spell__StartDuration = false
set udg_Spell__Duration = udg_Spell_i_Duration[udg_Spell_i_Head[i]] + udg_Spell_i_LastTime[udg_Spell_i_Head[i]]*udg_Spell__LevelMultiplier
elseif (udg_Spell__Expired and d > 0.00) or (udg_Spell__Duration <= 0.00) then
set udg_Spell__Duration = 0.00
return udg_Spell__Time
//The user manually expired the spell or the spell duration ended on its own.
endif
set d = udg_Spell__Duration
set udg_Spell__Duration = 0.00
if udg_Spell__Time == 0.00 then
if udg_Spell_i_LastTime[i] == 0.00 then
if udg_Spell_i_Time[udg_Spell_i_Head[i]] > 0.00 then
//The user specified a default interval to follow:
set udg_Spell__Time = udg_Spell_i_Time[udg_Spell_i_Head[i]]
else
//Set the spell time to the minimum.
set udg_Spell__Time = udg_Spell__Interval
endif
else
//Otherwise, set it to what it was before.
set udg_Spell__Time = udg_Spell_i_LastTime[i]
endif
//else, the user is specifying a new time for the spell.
endif
set udg_Spell_i_LastTime[i] = udg_Spell__Time //Whatever the case, remember this time for next time.
set udg_Spell_i_Duration[i] = d - udg_Spell__Time //Subtract the time from the duration.
endif
return udg_Spell__Time
endfunction
//===========================================================================
// Runs every Spell__Interval seconds and handles all of the timed events.
//
function SpellTimerLoop takes nothing returns nothing
local integer i = udg_Spell_i_StackN
local integer node
local real time
set udg_Spell__Running = true
//Run stack top to bottom to avoid skipping slots when destroying.
loop
set i = i - 1
exitwhen i < 0
set node = udg_Spell_i_Stack[i]
set time = udg_Spell_i_Time[node] - udg_Spell__Interval
if time <= 0.00 then
set time = SpellTriggerExecute(node, udg_Spell_i_OnLoopStack[node])
endif
if time <= 0.00 then
call SpellIndexDestroy(node)
else
set udg_Spell_i_Time[node] = time
endif
endloop
set udg_Spell__Running = false
endfunction
//===========================================================================
// This is the meat of the system as it handles the event responses.
//
function RunSpellEvent takes nothing returns boolean
local boolean b
local integer aid = GetSpellAbilityId()
local integer head = LoadInteger(udg_Spell__Hash, 0, aid)
local integer i
local integer id
local trigger t
local playerunitevent eid
if head == 0 then
//Nothing for this ability has been registered. Skip the sequence.
return false
endif
set eid = ConvertPlayerUnitEvent(GetHandleId(GetTriggerEventId()))
set udg_Spell__Caster = GetTriggerUnit()
set id = GetHandleId(udg_Spell__Caster)
set i = LoadInteger(udg_Spell__Hash, aid, id)
if i == 0 then
//This block will almost always happen with the OnChannel event. In the
//case of Charge Gold and Lumber, only an OnEffect event will run.
set i = udg_Spell_i_Recycle
if i == 0 then
//Create a new, unique index
set i = udg_Spell_i_Instances + 1
set udg_Spell_i_Instances = i
else
//Repurpose an existing one
set udg_Spell_i_Recycle = udg_Spell_i_RecycleList[i]
endif
//call BJDebugMsg("Creating index: " + I2S(i))
set udg_Spell_i_RecycleList[i] = -1
set udg_Spell_i_StackRef[i] = -1
set udg_Spell_i_Head[i] = head
if eid == EVENT_PLAYER_UNIT_SPELL_CHANNEL then
set udg_Spell_i_Channeling[i] = true
call SaveInteger(udg_Spell__Hash, aid, id, i)
set t = udg_Spell_i_OnChannelStack[head]
else //eid == EVENT_PLAYER_UNIT_SPELL_EFFECT
set t = udg_Spell_i_OnEffectStack[head]
endif
set udg_Spell_i_Caster[i] = udg_Spell__Caster
set udg_Spell_i_Level[i] = GetUnitAbilityLevel(udg_Spell__Caster, aid)
set udg_Spell_i_Target[i] = GetSpellTargetUnit()
set udg_Spell_i_TargetX[i] = GetSpellTargetX()
set udg_Spell_i_TargetY[i] = GetSpellTargetY()
set udg_Spell_i_OnLoopStack[i] = udg_Spell_i_OnLoopStack[head]
if udg_Spell_i_UseTG[head] then
//Get a recycled unit group or create a new one.
set id = udg_Spell_i_GroupN - 1
if id >= 0 then
set udg_Spell_i_GroupN = id
set udg_Spell_i_TargetGroup[i] = udg_Spell_i_GroupStack[id]
else
set udg_Spell_i_TargetGroup[i] = CreateGroup()
endif
endif
elseif eid == EVENT_PLAYER_UNIT_SPELL_CAST then
set t = udg_Spell_i_OnCastStack[head]
elseif eid == EVENT_PLAYER_UNIT_SPELL_EFFECT then
set t = udg_Spell_i_OnEffectStack[head]
elseif eid == EVENT_PLAYER_UNIT_SPELL_FINISH then
set udg_Spell_i_Completed[i] = true
return true
else //eid == EVENT_PLAYER_UNIT_SPELL_ENDCAST
set udg_Spell_i_Channeling[i] = false
call RemoveSavedInteger(udg_Spell__Hash, aid, id)
set t = udg_Spell_i_OnFinishStack[head]
endif
if SpellTriggerExecute(i, t) > 0.00 then
//Set the spell time to the user-specified one.
set udg_Spell_i_Time[i] = udg_Spell__Time
if udg_Spell_i_StackRef[i] < 0 then
//Allocate the spell index onto the loop stack.
set aid = udg_Spell_i_StackN
set udg_Spell_i_Stack[aid] = i
set udg_Spell_i_StackRef[i] = aid
set udg_Spell_i_StackN = aid + 1
if aid == 0 then
//If this is the first spell index using the timer, start it up:
call TimerStart(udg_Spell_i_Timer, udg_Spell__Interval, true, function SpellTimerLoop)
endif
endif
elseif (not udg_Spell_i_Channeling[i]) and (t != null or udg_Spell_i_Time[i] <= 0.00) then
call SpellIndexDestroy(i)
endif
set t = null
return true
endfunction
//This function is invoked if an event was launched recursively by another event's callback.
function RunPreSpellEvent takes nothing returns nothing
local integer i = udg_Spell__Index
local real time = udg_Spell__Time
local real d = udg_Spell__Duration
local boolean expired = udg_Spell__Expired
if udg_Spell__Trigger_OnLoop != null then
set udg_Spell_i_OnLoopStack[i] = udg_Spell__Trigger_OnLoop
endif
if RunSpellEvent() then
set udg_Spell__Time = time
set udg_Spell__Duration = d
set udg_Spell__Expired = expired
call SpellIndexGetVars(i)
endif
endfunction
//===========================================================================
// Base function of the system: runs when an ability event does something.
//
function SpellSystemEvent takes nothing returns boolean
if udg_Spell__Running then
call RunPreSpellEvent()
else
set udg_Spell__Running = true
call RunSpellEvent()
set udg_Spell__Running = false
endif
return false
endfunction
//===========================================================================
// Set Spell__Ability to your spell's ability
// Set Spell__Trigger_OnChannel/Cast/Effect/Finish/Loop to any trigger(s) you
// want to automatically run.
//
// GUI-friendly: Run Spell System <gen> (ignoring conditions)
//
function SpellSystemRegister takes nothing returns nothing
local integer aid = udg_Spell__Ability
local integer head = udg_Spell_i_Instances + 1
if HaveSavedInteger(udg_Spell__Hash, 0, aid) or aid == 0 then
//The system rejects duplicate or unassigned abilities.
return
endif
set udg_Spell_i_Instances = head
//Preload the ability on dummy unit to help prevent first-instance lag
call UnitAddAbility(udg_Spell_i_PreloadDummy, aid)
//Save head index to the spell ability so it be referenced later.
call SaveInteger(udg_Spell__Hash, 0, aid, head)
//Set any applicable event triggers.
set udg_Spell_i_OnChannelStack[head]= udg_Spell__Trigger_OnChannel
set udg_Spell_i_OnCastStack[head] = udg_Spell__Trigger_OnCast
set udg_Spell_i_OnEffectStack[head] = udg_Spell__Trigger_OnEffect
set udg_Spell_i_OnFinishStack[head] = udg_Spell__Trigger_OnFinish
set udg_Spell_i_OnLoopStack[head] = udg_Spell__Trigger_OnLoop
set udg_Spell_i_InRangeFilter[head] = udg_Spell__Trigger_InRangeFilter
//Set any customized filter variables:
call SpellSetFilters(head)
//Tell the system to automatically create target groups, if needed
set udg_Spell_i_UseTG[head] = udg_Spell__UseTargetGroup
//Set the default time sequences if a duration is used:
set udg_Spell_i_Time[head] = udg_Spell__Time
set udg_Spell_i_Duration[head] = udg_Spell__Duration
set udg_Spell_i_LastTime[head] = udg_Spell__DurationPerLevel
//Set variables back to their defaults:
set udg_Spell__Trigger_OnChannel = null
set udg_Spell__Trigger_OnCast = null
set udg_Spell__Trigger_OnEffect = null
set udg_Spell__Trigger_OnFinish = null
set udg_Spell__Trigger_OnLoop = null
set udg_Spell__Trigger_InRangeFilter= null
set udg_Spell__UseTargetGroup = false
set udg_Spell__Time = 0.00
set udg_Spell__Duration = 0.00
set udg_Spell__DurationPerLevel = 0.00
set udg_Spell__Filter_AllowEnemy = udg_Spell_i_AllowEnemy[0]
set udg_Spell__Filter_AllowAlly = udg_Spell_i_AllowAlly[0]
set udg_Spell__Filter_AllowDead = udg_Spell_i_AllowDead[0]
set udg_Spell__Filter_AllowMagicImmune = udg_Spell_i_AllowMagicImmune[0]
set udg_Spell__Filter_AllowMechanical = udg_Spell_i_AllowMechanical[0]
set udg_Spell__Filter_AllowStructure = udg_Spell_i_AllowStructure[0]
set udg_Spell__Filter_AllowFlying = udg_Spell_i_AllowFlying[0]
set udg_Spell__Filter_AllowHero = udg_Spell_i_AllowHero[0]
set udg_Spell__Filter_AllowNonHero = udg_Spell_i_AllowNonHero[0]
set udg_Spell__Filter_AllowLiving = udg_Spell_i_AllowLiving[0]
endfunction
function SpellFilterCompare takes boolean is, boolean yes, boolean no returns boolean
return (is and yes) or ((not is) and no)
endfunction
//===========================================================================
// Before calling this function, set Spell__InRangePoint to whatever point
// you need, THEN set Spell__InRange to the radius you need. The system will
// enumerate the units matching the configured filter and fill them into
// Spell_InRangeGroup.
//
function SpellGroupUnitsInRange takes nothing returns boolean
local integer i = udg_Spell_i_Head[udg_Spell__Index]
local integer j = 0
local unit u
local real padding = 64.00
if udg_Spell_i_AllowStructure[i] then
//A normal unit can only have up to size 64.00 collision, but if the
//user needs to check for structures we need a padding big enough for
//the "fattest" ones: Tier 3 town halls.
set padding = 197.00
endif
call GroupEnumUnitsInRangeOfLoc(udg_Spell__InRangeGroup, udg_Spell__InRangePoint, udg_Spell__InRange + padding, null)
loop
set u = FirstOfGroup(udg_Spell__InRangeGroup)
exitwhen u == null
call GroupRemoveUnit(udg_Spell__InRangeGroup, u)
loop
exitwhen not IsUnitInRangeLoc(u, udg_Spell__InRangePoint, udg_Spell__InRange)
exitwhen not SpellFilterCompare(IsUnitType(u, UNIT_TYPE_DEAD), udg_Spell_i_AllowDead[i], udg_Spell_i_AllowLiving[i])
exitwhen not SpellFilterCompare(IsUnitAlly(u, udg_Spell__CasterOwner), udg_Spell_i_AllowAlly[i], udg_Spell_i_AllowEnemy[i])
exitwhen not SpellFilterCompare(IsUnitType(u, UNIT_TYPE_HERO) or IsUnitType(u, UNIT_TYPE_RESISTANT), udg_Spell_i_AllowHero[i], udg_Spell_i_AllowNonHero[i])
exitwhen IsUnitType(u, UNIT_TYPE_STRUCTURE) and not udg_Spell_i_AllowStructure[i]
exitwhen IsUnitType(u, UNIT_TYPE_FLYING) and not udg_Spell_i_AllowFlying[i]
exitwhen IsUnitType(u, UNIT_TYPE_MECHANICAL) and not udg_Spell_i_AllowMechanical[i]
exitwhen IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and not udg_Spell_i_AllowMagicImmune[i]
set udg_Spell__InRangeUnit = u
//Run the user's designated filter, if one exists.
exitwhen udg_Spell_i_InRangeFilter[i] != null and not TriggerEvaluate(udg_Spell_i_InRangeFilter[i])
set j = j + 1
set udg_Spell__InRangeUnits[j] = u
exitwhen true
endloop
endloop
if j > udg_Spell__InRangeMax and udg_Spell__InRangeMax > 0 then
//The user has defined a maximum number of units allowed in the group.
//Remove a random unit until the total does not exceed capacity.
loop
set i = GetRandomInt(1, j)
set udg_Spell__InRangeUnits[i] = udg_Spell__InRangeUnits[j]
set j = j - 1
exitwhen j == udg_Spell__InRangeMax
endloop
endif
set udg_Spell__InRangeCount = j
set udg_Spell__InRangeMax = 0
set udg_Spell__InRange = 0.00
loop
exitwhen j == 0
call GroupAddUnit(udg_Spell__InRangeGroup, udg_Spell__InRangeUnits[j])
if udg_Spell__WakeTargets and UnitIsSleeping(u) then
call UnitWakeUp(u)
endif
set j = j - 1
endloop
return false
endfunction
//===========================================================================
function InitTrig_Spell_System takes nothing returns nothing
local integer i = 16
local player p
local trigger t
if gg_trg_Spell_System != null then
//A JASS function call already initialized the system.
return
endif
//This runs before map init events so the hashtable is ready before then.
set udg_Spell__Hash = InitHashtable()
//Initialize these two locations which will never get removed
set udg_Spell__CastPoint = Location(0, 0)
set udg_Spell__TargetPoint = Location(0, 0)
//Recycle existing unit groups into the recycle stack to avoid needing to destroy any extras.
set udg_Spell_i_GroupStack[2] = udg_Spell__TargetGroup
set udg_Spell_i_GroupStack[3] = udg_Spell_i_TargetGroup[0]
set udg_Spell_i_GroupStack[4] = udg_Spell_i_TargetGroup[1]
set udg_Spell_i_GroupN = 5 //There are already five valid unit groups thanks to Variable Editor.
set t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_Spell__InRange", GREATER_THAN, 0.00)
call TriggerAddCondition(t, Filter(function SpellGroupUnitsInRange))
set t = CreateTrigger()
call TriggerAddCondition(t, Filter(function SpellSystemEvent))
loop
set i = i - 1
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_CHANNEL, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_CAST, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_FINISH, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
exitwhen i == 0
endloop
set p = null
set t = null
//Run the configuration trigger so its variables are ready before the
//map initialization events run.
call TriggerExecute(gg_trg_Spell_System_Config)
call SpellSetFilters(0)
//Create this trigger so it's GUI-friendly.
set gg_trg_Spell_System = CreateTrigger()
call TriggerAddAction(gg_trg_Spell_System, function SpellSystemRegister)
set gg_trg_Spell_System_Config = gg_trg_Spell_System //In case the user accidentally picks this one
//Create a dummy unit for preloading abilities.
set udg_Spell_i_PreloadDummy = CreateUnit(udg_Spell__DummyOwner, udg_Spell__DummyType, 0, 0, 0)
call UnitApplyTimedLife(udg_Spell_i_PreloadDummy, 'BTLF', 0.01)
endfunction
//TESH.scrollpos=205
//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=0
//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