Name | Type | is_array | initial_value |
AbsorbAbility | abilcode | No | |
AccuracyUnit | unit | No | |
AcolyteDummy | unit | Yes | |
AD_Ability_Level | integer | No | |
AD_Base_AoE | real | No | |
AD_Base_Damage | real | No | |
AD_Bonus_AoE | real | No | |
AD_Dummy | unit | No | |
Adrenalin_AttackED_unit | unit | No | |
Adrenalin_CastU | unit | No | |
Adrenalin_DamageBonus_Int | integer | No | |
Adrenalin_Hit_Int | integer | No | |
Adrenalin_Text | texttag | No | |
AdrenalineTimer | timer | No | |
AdrenalineUnit | unit | No | |
AdrenalinRush_Strength_Int | integer | No | |
AdvanceForwardPoint | location | No | |
AdvanceForwardPoint2 | location | No | |
AdvanceForwardPoint3 | location | No | |
AfterDamageEvent | real | No | |
AG_Ability | abilcode | No | |
AG_AffectedUnit | unit | No | |
AG_AffectedUnitPosition | location | No | |
AG_Angle | real | Yes | |
AG_AttackType | attacktype | No | |
AG_Caster | unit | Yes | |
AG_CurrentIndex | integervar | No | |
AG_CurrentSpeed | real | Yes | |
AG_DamageType | damagetype | No | |
AG_DestroyTree | boolean | No | |
AG_DistanceCounter | real | Yes | |
AG_Dummy | unit | Yes | |
AG_DummyAbility | abilcode | No | |
AG_DummyCaster | unit | No | |
AG_DummyPosition | location | No | |
AG_DummyType | unitcode | No | |
AG_DummyX | real | No | |
AG_DummyY | real | No | |
AG_EffectScale | real | No | |
AG_ExplosionEffect | string | No | |
AG_GrenadeColor | real | Yes | |
AG_GrenadeEffect | string | No | |
AG_GrenadeEffectHandler | effect | Yes | |
AG_GrenadeSize | real | Yes | |
AG_GrenadeTransparency | real | Yes | |
AG_ImpactDamage | real | Yes | |
AG_Index | integer | No | |
AG_Level | integer | Yes | |
AG_MaxHeight | real | Yes | |
AG_Radius | real | Yes | |
AG_Speed | real | Yes | |
AG_Tree | destructable | No | |
AG_TreeDestroyer | unit | No | |
AimedStrikeDamage | real | Yes | |
AimedStrikeInteger | integer | No | |
AimedStrikePoint | location | No | |
AimedStrikeTimer | timer | No | |
AimedStrikeUnit | unit | Yes | |
AimedStrikeUnit2 | unit | Yes | |
AllianceCheckPoint | location | No | |
AllianceFloatingText | texttag | Yes | |
AllianceFloatingText2 | texttag | Yes | |
AllianceGold | integer | No | 0 |
AllianceGoldFloatingtext | texttag | No | |
AllianceMovePoint | location | No | |
AllianceNoticeAttackPoint | location | No | |
AlliancePoint | location | Yes | |
AlliancePoint2 | location | Yes | |
allocatedAttacks | integer | No | |
allocCounter | integer | No | |
amount | real | No | |
AncestralCorpsePoint | location | No | |
AncestralNecklaceGroup | group | No | |
AncestralNecklaceGroup2 | group | No | |
AncestralNecklaceGroup3 | group | No | |
AncestralNecklaceIndex | integer | No | |
AncestralNecklacePoint | location | Yes | |
AncestralNecklaceUser | unit | Yes | |
AOEDamageEvent | real | No | |
AOEDamageEvent_Copy | real | No | |
AOEDamageSource | unit | No | |
AOK_Item | item | Yes | |
AOK_Timer | timer | Yes | |
AOKBoolean | boolean | Yes | |
AOKInteger | integer | No | |
AOKItem | item | Yes | |
AOKUser | unit | Yes | |
ARBoolean | boolean | Yes | |
ArcherHero | unit | No | |
ArcherPoint | location | No | |
ArcticBlast | abilcode | No | |
ArcticBlastAttackType | attacktype | No | |
ArcticBlastCaster | unit | Yes | |
ArcticBlastDamage | real | Yes | |
ArcticBlastDamageAttach | string | No | |
ArcticBlastDamagedGroup | group | Yes | |
ArcticBlastDamagedUnit | unit | No | |
ArcticBlastDamageEffect | string | No | |
ArcticBlastDamageRadius | real | Yes | |
ArcticBlastDamageType | damagetype | No | |
ArcticBlastDistance | real | Yes | |
ArcticBlastDummy | unitcode | No | |
ArcticBlastDummyGroup | group | Yes | |
ArcticBlastDummySlowUnit | unit | Yes | |
ArcticBlastEffectAmount | integer | Yes | |
ArcticBlastGroup | group | No | |
ArcticBlastHeal | real | Yes | |
ArcticBlastIndex | integer | No | |
ArcticBlastInteger | integervar | No | |
ArcticBlastLevel | integer | Yes | |
ArcticBlastLoopIndex | integer | No | |
ArcticBlastMaxAoE | real | Yes | |
ArcticBlastOffset | location | No | |
ArcticBlastPoint | location | No | |
ArcticBlastReal | real | No | |
ArcticBlastSlow | abilcode | No | |
ArcticBlastSpeed | real | Yes | |
ArcticBlastSpreadOutSpeed | real | Yes | |
ArcticBlastTriggerPlayer | player | Yes | |
ArcticBlastVisualDummy | unitcode | No | |
ArcticBlastVisualDummyUnit | unit | No | |
AreaHeal | group | Yes | |
AreaHealPoint | location | No | |
ARGroup | group | No | |
ARItem | item | Yes | |
ARMOR_TYPE_ETHEREAL | integer | No | |
ARMOR_TYPE_FLESH | integer | No | |
ARMOR_TYPE_METAL | integer | No | |
ARMOR_TYPE_NONE | integer | No | |
ARMOR_TYPE_STONE | integer | No | |
ARMOR_TYPE_WOOD | integer | No | |
ArmorTypeDebugStr | string | Yes | |
ArmourAdaptFX | effect | No | |
ArmsRaceCaster | unit | No | |
ArmsRacePoint | location | No | |
ArmsRaceSFX | effect | No | |
ArmsRaceUnit | unit | No | |
ArrowCastPoint | location | No | |
ArrowCastPoint2 | location | No | |
ArrowCastPoint3 | location | No | |
ArrowCastPoint4 | location | No | |
ArrowCastPoint5 | location | No | |
ArrowCastPoint6 | location | No | |
ArrowCastPoint7 | location | No | |
ArrowCastPoint8 | location | No | |
ARStats | integer | Yes | |
AttachmentPoints | string | Yes | |
ATTACK_TYPE_CHAOS | integer | No | |
ATTACK_TYPE_HERO | integer | No | |
ATTACK_TYPE_MAGIC | integer | No | |
ATTACK_TYPE_NORMAL | integer | No | |
ATTACK_TYPE_PIERCE | integer | No | |
ATTACK_TYPE_SIEGE | integer | No | |
ATTACK_TYPE_SPELLS | integer | No | |
ATTACK_TYPE_UNIVERSAL | attacktype | No | |
AttackTypeDebugStr | string | Yes | |
BackOff_Target | unit | No | |
BackOffDamage | real | No | |
BackOffDummy | unit | Yes | |
BackOffGroup | group | No | |
BackOffGroup1 | group | No | |
BackOffPoint | location | No | |
BackOffPoint2 | location | No | |
BackOffPoint3 | location | No | |
Backstabber | unit | No | |
BackstabDamage | real | Yes | |
BackstabDefend | unit | No | |
BackstabOPoint | location | No | |
BackstabOPoint2 | location | No | |
BackstabOPoint3 | location | No | |
BackstabPoint | location | Yes | |
BackStabSkillCaster | unit | No | |
BackStabSkillTarget | unit | No | |
BackStabTimer | timer | No | |
BanditPermanentInvisi | unit | No | |
BansheeCheckGroup | group | No | |
BansheeFreezer | unit | No | |
BansheeGroup2 | group | No | |
BansheePoint | location | No | |
BansheeUnit | unit | Yes | |
BattleFlagGroup | group | Yes | |
BattleFlagGroup2 | group | Yes | |
BattleFlagGroup3 | group | Yes | |
BattleFlagHolder | unit | Yes | |
BattleFlagInteger | integer | Yes | |
BattleFlagInteger2 | integer | Yes | |
BattleFlagPoint | location | Yes | |
BB | integer | No | |
BB2 | integer | No | |
BB_Angle | real | Yes | |
BB_Caster | unit | Yes | |
BB_CastNumber | integer | No | |
BB_Distance | real | Yes | |
BB_Height | real | Yes | |
BB_Missile | unit | Yes | |
BB_Off | boolean | Yes | |
BB_Point | location | Yes | |
BB_Speed | real | Yes | |
BB_Switch | integer | No | |
BB_Up | boolean | Yes | |
BC_SFX | effect | Yes | |
BCCaster | unit | No | |
BCDummy | unit | No | |
BCGroup3 | group | No | |
BCGroup4 | group | No | |
BCInteger | integer | No | |
BCInteger2 | integer | No | |
BCPoint | location | No | |
BCTimer | timer | No | |
BeamAIGroup | group | No | |
BeamAITargetPoint | location | No | |
BeamAITimer | timer | No | |
BeamAngle | real | No | |
BeamAux | location | No | |
BeamCanonL | unit | No | |
BeamCanonMid | unit | No | |
BeamCanonR | unit | No | |
BeamGrow | real | No | 100.00 |
BeamPointCast | location | No | |
BeamPointTarg | location | No | |
BeamTImer | timer | No | |
BeamTimer2 | timer | No | |
BeamTimer3 | timer | No | |
BearIsAttacking | boolean | No | |
BearIsAttackingTimer | timer | No | |
BearPoint | location | No | |
BearRandomPoint | location | No | |
BearUnit | unit | No | |
BeastMaster | unit | No | |
BeastMasterPoint | location | No | |
BF_DefautInterval | integer | No | |
BL | integervar | No | |
BL_Angle | real | Yes | |
BL_AoE | real | Yes | |
BL_Collision | real | Yes | |
BL_Distance | real | Yes | |
BL_Dmg | real | Yes | |
BL_Effect1 | effect | Yes | |
BL_Effect2 | effect | Yes | |
BL_Effect3 | effect | Yes | |
BL_Group | group | No | |
BL_Hero | unit | Yes | |
BL_Missile | unit | Yes | |
BL_Off | boolean | Yes | |
BL_Point | location | Yes | |
BL_Scale | real | No | |
BL_Skip | integer | No | |
BL_Speed | real | Yes | |
BL_Times | integer | No | |
BladebaneDamageAmount | real | No | |
BladebaneGroup | group | No | |
BladebaneTarget_Copy | unit | No | |
BladebaneUser | unit | No | |
BladeGreenCaster | unit | No | |
BladeGreenCounter | integer | No | |
BladeGreenDummy1 | unit | No | |
BladeGreenDummy2 | unit | No | |
BladeGreenGrow | real | No | |
BladeGreenPoint1 | location | No | |
BladeGreenPoint2 | location | No | |
BladeGreenPoint3 | location | No | |
BladeGreenPoint4 | location | No | |
BladeGreenTimer | timer | No | |
BladeGreenTimer2 | timer | No | |
BladeGreenTimer3 | timer | No | |
BlademasterCycloneGCheck | group | No | |
BladeMasterDebugTimer | timer | No | |
BladeMasterGroup | group | No | |
BladeMasterGroup2 | group | No | |
BlademasterPointAI | location | No | |
BladeMasterSoundTimer | timer | No | |
BladestormCaster | unit | No | |
BladestormDummy | unit | No | |
BladestormDummy2 | unit | No | |
BladestormGroup | group | No | |
BladestormPoint1 | location | No | |
BladestormPoint2 | location | No | |
BladestormPoint3 | location | No | |
BladestormTimer | timer | No | |
BlizzardDamageGroup | group | No | |
BlizzardJainaCaster | unit | No | |
BlizzardJainaCastPoint | location | No | |
BlizzardJainaDamage | real | No | |
BlizzardJainaDummy | unit | No | |
BlizzardJainaDummy2 | unit | No | |
BlizzardJainaDummyPoint | location | No | |
BloodgemBool | boolean | Yes | |
BloodgemCharge | integer | Yes | |
BloodgemIndex | integer | No | |
BloodgemInt | integer | No | |
BloodgemUser | unit | Yes | |
BookOfThothInteger1 | integer | No | |
BookOfThothInteger2 | integer | Yes | |
BookOfThothInteger3 | integer | Yes | |
BookOfThothInteger4 | integer | Yes | |
BookOfThothTimer1 | timer | Yes | |
BookOfThothTimer2 | timer | Yes | |
BookOfThothTimer3 | timer | Yes | |
BookOfThothUnit | unit | Yes | |
BoomL | unit | No | |
Boot_CancelButton | button | No | |
Boot_DialogWindow | dialog | No | |
Boot_PlayerBootedName | player | No | |
Boot_PlayerButton | button | Yes | |
Boot_TriggerPlayer | player | No | |
BossDeathPoint | location | No | |
BossDeathPoint2 | location | No | |
BossDeathUnit | unit | No | |
BRACERS_SPELL_DAMAGE_REDUCTION | real | No | |
BS | integer | No | |
BS_Angle | real | Yes | |
BS_AoE | boolean | Yes | |
BS_AoE_Radius | real | Yes | |
BS_ArcHeight | real | Yes | |
BS_Colision | real | Yes | |
BS_Damage | real | Yes | |
BS_Debug | real | Yes | |
BS_DebugZ | real | Yes | |
BS_DistanceToTravel | real | Yes | |
BS_Group | group | Yes | |
BS_Hero | unit | Yes | |
BS_HYPSpeed | real | Yes | |
BS_Level | integer | Yes | |
BS_MaxDistance | real | Yes | |
BS_MC | integervar | No | |
BS_Missile | unit | Yes | |
BS_MissileCount | integer | No | |
BS_On | boolean | Yes | |
BS_Parabola | real | Yes | |
BS_Point | location | Yes | |
BS_Range | real | Yes | |
BS_Skip | integer | No | |
BS_Speed | real | Yes | |
BS_SpeedZ | real | Yes | |
BS_StartZ | real | Yes | |
BS_Times | integer | No | |
BS_Up | boolean | Yes | |
BSGroup | group | No | |
Builder | unit | No | |
BuildRegenTowerHealReal | real | No | |
BuildStructureFlameTurretInt | integer | No | |
BuildStructureRealDamage | real | No | |
BuildStructureRealDamage2 | real | No | |
BuildStructureRealDamage3 | real | No | |
BuildStructureScaling | integer | Yes | |
Burn_CheckGroup | group | No | |
BurnCast | unit | No | |
BurnCasterPoint | location | No | |
BurnDistDL | real | No | |
BurnDistDR | real | No | |
BurnDistUL | real | No | |
BurnDistUR | real | No | |
BurnDL | unit | No | |
BurnDLA | real | No | |
BurnDLAux | location | No | |
BurnDR | unit | No | |
BurnDRA | real | No | |
BurnDRAux | location | No | |
BurnMid | location | No | |
BurnTarg | location | No | |
BurnTargCast | unit | No | |
BurnUL | unit | No | |
BurnULA | real | No | |
BurnULAux | location | No | |
BurnUR | unit | No | |
BurnURA | real | No | |
BurnURAux | location | No | |
BurrowedStrikeCaster | unit | No | |
BurrowedStrikeDummy | unit | Yes | |
BurrowedStrikePoint | location | No | |
BurrowedStrikeTimer | timer | No | |
BurrowedStrikeTimer2 | timer | No | |
BurrowStrikeCaster | unit | No | |
BurrowStrikeGroup | group | No | |
BurrowStrikePoint | location | No | |
BurrowStrikePoint2 | location | No | |
BurrowStrikePoint3 | location | No | |
BurrowStrikePoint4 | location | No | |
C_AoE | integer | No | |
C_AoE_A | real | No | |
C_Caster | unit | Yes | |
C_CasterPosition | location | No | |
C_Damage | real | No | |
C_DamageArea_int | integer | No | |
C_DamagePoints | location | No | |
C_DamageUnits | group | No | |
C_Index | integer | Yes | |
C_IndexMaxSize | integer | No | |
C_IndexSize | integer | No | |
C_Integer | integer | No | |
C_Loop | integer | No | |
C_RealDamage | real | No | |
C_RealShardAmount | real | Yes | |
C_SFX_SpellPathString | string | No | |
C_ShardAmount | real | No | 5.00 |
Captain | boolean | No | |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CarrionSwarm | abilcode | Yes | |
Caster | unit | No | |
Caster1 | unit | No | |
Casters | group | No | |
CB_Points | location | Yes | |
CB_Timer | timer | Yes | |
CB_Units | unit | Yes | |
CBBoolean | boolean | Yes | |
CBCastPoint | location | No | |
CBGroup2 | group | No | |
CBHTS | integer | Yes | |
CBUHNumber | integer | Yes | |
cc_endtag | string | No | |
cc_players | string | Yes | |
CG_Angle | real | Yes | |
CG_AoE1 | real | No | |
CG_AoE2 | real | No | |
CG_Caster | unit | Yes | |
CG_Check | boolean | No | |
CG_Damage | real | Yes | |
CG_Distance | real | Yes | |
CG_Dummy | unit | Yes | |
CG_Index | integer | No | |
CG_Index2 | integer | No | |
CG_Index3 | integer | No | |
CG_Jumps | integer | Yes | |
CG_Point1 | location | No | |
CG_Point2 | location | No | |
CG_Point3 | location | No | |
CG_Sound | sound | Yes | |
CG_Speed | real | No | |
CG_Target | unit | Yes | |
CG_UnitGroup | group | Yes | |
ChronosPendantBool | boolean | Yes | |
ClawsOfDeath | boolean | Yes | |
CleanedItem | item | Yes | |
ClearDamageEvent | trigger | No | |
ClockMinute | integer | No | |
ClockSeconds | integer | No | |
ClSpellNumber | integer | No | |
ClsR_Ability | abilcode | No | |
ClsR_AbilityLevels | integer | No | |
ClsR_AbilityOrder | ordercode | No | |
ClsR_AreaOfEffect | real | Yes | |
ClsR_areaOfTarget | real | Yes | |
ClsR_Caster | unit | Yes | |
ClsR_casterOwner | player | Yes | |
ClsR_Counter | integer | Yes | |
ClsR_Counter2 | integer | Yes | |
ClsR_CountRockets | boolean | No | |
ClsR_DamagePerRocket | real | Yes | |
ClsR_DelayPerWave | real | No | |
ClsR_FirstWaveDelay | real | No | |
ClsR_Hashtable | hashtable | No | |
ClsR_HitTargetOnly | boolean | No | |
ClsR_Last | integer | No | |
ClsR_lastPitch | real | Yes | |
ClsR_lastX | real | Yes | |
ClsR_lastY | real | Yes | |
ClsR_lastZ | real | Yes | |
ClsR_Level | integer | Yes | |
ClsR_MakeUnitExplode | boolean | No | |
ClsR_mapBorder | real | Yes | |
ClsR_MaxIndex | integer | No | |
ClsR_Next | integer | Yes | |
ClsR_NoTarget | boolean | No | |
ClsR_OrientationPitch | real | No | |
ClsR_OrientationRoll | real | No | |
ClsR_OrientationYaw | real | No | |
ClsR_Pitch | real | Yes | |
ClsR_posX | real | Yes | |
ClsR_posY | real | Yes | |
ClsR_posZ | real | Yes | |
ClsR_PreferTargetUnit | boolean | No | |
ClsR_Prev | integer | Yes | |
ClsR_realTimer | real | Yes | |
ClsR_realTimer2 | real | Yes | |
ClsR_Recyclable | integer | No | |
ClsR_Recycle | integer | Yes | |
ClsR_RocketAttackType | attacktype | No | |
ClsR_RocketDamageType | damagetype | No | |
ClsR_rocketFx | effect | Yes | |
ClsR_RocketsCollisionSize | real | No | |
ClsR_RocketsCount | integer | Yes | |
ClsR_RocketsCrashHeight | real | No | |
ClsR_RocketsDamageRange | real | Yes | |
ClsR_RocketsExpirationTime | real | No | |
ClsR_RocketsFacingWidth | real | No | |
ClsR_RocketsInterval | real | No | |
ClsR_RocketsMaxFacingVariation | real | No | |
ClsR_RocketsMaxPitchVariation | real | No | |
ClsR_RocketsMinFacingVariation | real | No | |
ClsR_RocketsMinPitchVariation | real | No | |
ClsR_RocketsModel | string | No | |
ClsR_RocketsPerSecond | integer | No | |
ClsR_RocketsPerWave | integer | No | |
ClsR_RocketsSize | real | No | |
ClsR_RocketsSpawnHeightBonus | real | No | |
ClsR_RocketsSpawnOffset | real | No | |
ClsR_RocketsSpeedBase | real | Yes | |
ClsR_RocketsTurnAcceleration | real | Yes | |
ClsR_RocketsUnitsHeightBonus | real | No | |
ClsR_Roll | real | Yes | |
ClsR_Stage | integer | Yes | |
ClsR_Target | unit | Yes | |
ClsR_targetCollision | real | Yes | |
ClsR_TargetEffectAttachment | string | No | |
ClsR_TargetEffectModel | string | No | |
ClsR_targetType | integer | Yes | |
ClsR_tempGroup | group | No | |
ClsR_Timer | timer | No | |
ClsR_ZLocator | location | No | |
ClusterBombInteger | integervar | No | |
ClusterBombPoint | location | No | |
ClusterBombPoint2 | location | No | |
ClusterBombRealDamage | real | No | |
ClusterBpmber | unit | Yes | |
ClusterDropArea | boolean | Yes | |
COB_Counter | integer | No | |
COB_Damage | real | No | |
COB_Dummy2 | unit | No | |
COB_EffectCounter | integer | No | |
COB_Heal | real | No | |
COB_Integer | integer | No | |
COB_Timer | timer | No | |
COBCaster | unit | No | |
COBCounter2 | integer | No | |
COBDummy | unit | No | |
COBgroup | group | No | |
COBgroup2 | group | No | |
COBInteger | integer | Yes | |
COBPoint | location | No | |
COBPoint2 | location | No | |
COBPointO | location | No | |
ComboCounter | integer | Yes | |
ComboTimer | timer | Yes | |
CONVERTED_ATTACK_TYPE | attacktype | Yes | |
CONVERTED_DAMAGE_TYPE | damagetype | Yes | |
CP_HiddenItems | item | Yes | |
CP_HiddenItemsIndex | integer | No | |
CP_Item | item | No | |
CP_PointIsWalkable | boolean | No | |
CP_Rect | rect | No | |
CPS_Angle | real | No | |
CPS_AoE | real | Yes | |
CPS_ArcReal | real | Yes | |
CPS_AttackType | attacktype | Yes | |
CPS_Damage | real | Yes | |
CPS_DamageType | damagetype | Yes | |
CPS_Destroy | boolean | Yes | |
CPS_Dummy | unit | No | |
CPS_DummyType | unitcode | No | |
CPS_HArc | boolean | Yes | |
CPS_HArcLeftRight | boolean | Yes | |
CPS_HArcWidth | real | Yes | |
CPS_Height | real | Yes | |
CPS_Impact | modelfile | Yes | |
CPS_Loop | integervar | No | |
CPS_Missile | unit | Yes | |
CPS_MissileDistance | real | Yes | |
CPS_MUI | integer | No | |
CPS_Off | boolean | No | |
CPS_Point | location | Yes | |
CPS_Source | unit | Yes | |
CPS_SourcePoint | location | Yes | |
CPS_Speed | real | Yes | |
CPS_Target | unit | No | |
CPS_TargetUnit | unit | Yes | |
CPS_TraveledDistance | real | Yes | |
CPS_Tree | destructable | No | |
CPS_Trigger | trigger | Yes | |
CPS_UnitOrPoint | boolean | Yes | |
CPS_VArc | boolean | Yes | |
CPS_VArcHeight | real | Yes | |
CriticalStrikeAmount | real | Yes | 1.50 |
CriticalStrikeChance | real | Yes | 0.00 |
CS_Damage | real | No | |
CS_Points2 | location | Yes | |
CS_Points3 | location | Yes | |
CS_Points4 | location | Yes | |
CS_Points5 | location | Yes | |
CS_Points6 | location | Yes | |
CS_Points7 | location | Yes | |
CS_Units5 | unit | Yes | |
CS_Units6 | unit | Yes | |
CS_Units7 | unit | Yes | |
CSEAOPoint | location | No | |
CSEAOPoint2 | location | No | |
CSEAOUnit | unit | Yes | |
CSGroup2_2 | group | No | |
CSGroup2_3 | group | No | |
CSGroup2_4 | group | No | |
CSGroup2_5 | group | No | |
CSGroup2_6 | group | No | |
CSGroup2_7 | group | No | |
CSS_Abilities | abilcode | Yes | |
CSS_Hashtable | hashtable | No | |
CSS_Power | integer | Yes | |
CSS_PreloadBoolean | boolean | No | |
CWCastPoint | location | No | |
CWGroup | group | No | |
DAcaster | unit | No | |
DADummy | unit | No | |
DaggerCooldown | boolean | Yes | |
DaggerDummy | unit | Yes | |
DaggerIndex | integer | No | |
DaggerItem | item | Yes | |
DaggerPoint | location | No | |
DaggerTimer | timer | Yes | |
DaggerUnit | unit | Yes | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DAMAGE_TYPE_ACID | integer | No | |
DAMAGE_TYPE_COLD | integer | No | |
DAMAGE_TYPE_DEATH | integer | No | |
DAMAGE_TYPE_DEFENSIVE | integer | No | |
DAMAGE_TYPE_DEMOLITION | integer | No | |
DAMAGE_TYPE_DETECTOR | integer | No | |
DAMAGE_TYPE_DISEASE | integer | No | |
DAMAGE_TYPE_DIVINE | integer | No | |
DAMAGE_TYPE_ENHANCED | integer | No | |
DAMAGE_TYPE_FIRE | integer | No | |
DAMAGE_TYPE_FORCE | integer | No | |
DAMAGE_TYPE_LIGHTNING | integer | No | |
DAMAGE_TYPE_MAGIC | integer | No | |
DAMAGE_TYPE_MIND | integer | No | |
DAMAGE_TYPE_NORMAL | integer | No | |
DAMAGE_TYPE_PLANT | integer | No | |
DAMAGE_TYPE_POISON | integer | No | |
DAMAGE_TYPE_SHADOW_STRIKE | integer | No | |
DAMAGE_TYPE_SLOW_POISON | integer | No | |
DAMAGE_TYPE_SONIC | integer | No | |
DAMAGE_TYPE_SPIRIT_LINK | integer | No | |
DAMAGE_TYPE_UNIVERSAL | integer | No | |
DAMAGE_TYPE_UNKNOWN | integer | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventArmorPierced | real | No | |
DamageEventArmorT | integer | No | |
DamageEventAttackT | integer | No | |
DamageEventDamageT | integer | No | |
DamageEventDefenseT | integer | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventType | integer | No | |
DamageEventWeaponT | integer | No | |
DamageModifierEvent | real | No | |
DamageScalingUser | real | No | |
DamageScalingWC3 | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCode | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeDebugStr | string | Yes | |
DamageTypeEnhanced | integer | No | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypePure | integer | No | |
DamageTypePureExplosive | integer | No | |
DamageTypeReduced | integer | No | |
DAPoint | location | No | |
DAPoint2 | location | No | |
DAPoint3 | location | No | |
DashAssasinCaster | unit | No | |
DashAssasinCasterPoint | location | No | |
DashAssasinDamageDoneGroup | group | No | |
DashAssasinDummy | unit | No | |
DashAssasinDummyPoint | location | No | |
DashAssasinGroup | group | No | |
DashAssasinInteger | integer | No | |
DashAssasinOriginalCastPoint | location | No | |
DashAssasinPoint | location | No | |
DashAssasinRealAngle | real | No | |
DashAssasinRealDamage | real | No | |
DashAssasinTargetPoint | location | No | |
DashCaster | unit | Yes | |
DashDamage | real | Yes | |
DashDistance | integer | Yes | |
DashDistanceReal | real | Yes | |
DashDistCount | integer | No | |
DashGroup1 | group | No | |
DashGroup2 | group | No | |
DashGroup3 | group | No | |
DashInteger | integer | Yes | |
DashPoint | location | No | |
DashPoint2 | location | No | |
DashPoint3 | location | Yes | |
DashPoint4 | location | Yes | |
DashPoint5 | location | Yes | |
DashReal1 | real | Yes | |
DashReal2 | real | Yes | |
DashRealDistance | real | No | |
DashRealDistance2 | real | No | |
DashSpeed | real | Yes | |
DATimer | timer | No | |
DATimer2t | timer | No | |
DATimer3 | timer | No | |
DAUnit | unit | No | |
DAUnits | unitcode | Yes | |
DAuraGroup1 | group | No | |
DAuraGroup2 | group | No | |
DAuraGroup3 | group | No | |
DAuraGroup4 | group | No | |
DAuraGroup5 | group | No | |
DAuraGroup6 | group | No | |
DDDGroup | group | No | |
DeathCoilCaster | unit | Yes | |
DeathCoilCounter | integer | Yes | |
DeathCoilCurrentIndex | integervar | No | |
DeathCoilDummy | unit | Yes | |
DeathCoilDummy3 | unit | Yes | |
DeathCoilGroup | group | Yes | |
DeathCoilGroup2 | group | Yes | |
DeathCoilMaxIndex | integer | No | |
DeathCoilMissile | unit | Yes | |
DeathCoilPoint | location | Yes | |
DeathCoilPoint1 | location | Yes | |
DeathCoilPoint2 | location | Yes | |
DeathCoilPoint3 | location | Yes | |
DeathEvent | real | No | |
Deaths | integer | Yes | |
DebugDummy | unit | No | |
DebugDummy2 | unit | Yes | |
Decoy_DecoyU | unit | No | |
Decoy_SpellLevel_Int | integer | No | |
Decoy_SpellLevel_Int2 | integer | No | |
Decoy_SpellLevel_Int3 | integer | No | |
Decoy_SpellLevel_Int4 | integer | No | |
DecoyGroup | group | No | |
DecoyPoint | location | No | |
DecoyPoint2 | location | No | |
Defeated | boolean | Yes | |
DefeatIntegerSpawnCount | integer | Yes | |
DefeatMusicTimer | timer | No | |
DefeatSpawnPoint | location | No | |
DEFENSE_TYPE_DIVINE | integer | No | |
DEFENSE_TYPE_FORTIFIED | integer | No | |
DEFENSE_TYPE_HEAVY | integer | No | |
DEFENSE_TYPE_HERO | integer | No | |
DEFENSE_TYPE_LIGHT | integer | No | |
DEFENSE_TYPE_MEDIUM | integer | No | |
DEFENSE_TYPE_NORMAL | integer | No | |
DEFENSE_TYPE_UNARMORED | integer | No | |
DefenseTypeDebugStr | string | Yes | |
DGInteger | integer | No | |
DifficultyDialog | dialog | No | |
DifficultyLevel | integer | No | |
DiffificultyMode | boolean | Yes | |
Dig_2nd_Posi | location | No | |
Dig_AlternatePointLock_Int | integer | No | |
Dig_ArrowSE | effect | Yes | |
Dig_CamTimer | timer | No | |
Dig_CasterU | unit | No | |
Dig_DistanceHoleDmgCompare | real | Yes | |
Dig_DummyU | unit | No | |
Dig_HoleDiePosi | location | No | |
Dig_HoleU | unit | Yes | |
Dig_HoleUForDmg | unit | No | |
Dig_InRange_Posi | location | No | |
Dig_InRangeU | unit | No | |
Dig_Posi | location | No | |
Dig_SpellLvL | integer | No | |
Dig_TerrainDeformEffect | terraindeformation | No | |
Dig_Timer | timer | No | |
Disruptor | unit | No | |
DmgStr | string | No | |
DoomGuardDeathPoint | location | No | |
DR_Ability | abilcode | No | |
DR_AbilityLvl | integer | Yes | |
DR_ActiveOrbs | integer | Yes | |
DR_Cache_ID | integer | Yes | |
DR_Count | integer | No | |
DR_Counter | real | Yes | |
DR_DummyOwner | player | No | |
DR_DummyUnitType | unitcode | No | |
DR_Group | group | No | |
DR_Hero | unit | Yes | |
DR_HeroAbilityLvl | integer | Yes | |
DR_LoopInt | integer | No | |
DR_ManaRestored | real | Yes | |
DR_MaxIndex | integer | No | |
DR_NodeNext | integer | Yes | |
DR_NodePrev | integer | Yes | |
DR_OrbDuration | real | Yes | |
DR_OrbLoc | location | Yes | |
DR_OrbMax | integer | Yes | |
DR_OrbSFX | effect | Yes | |
DR_OrbSize | real | No | |
DR_OrbUnit | unit | Yes | |
DR_PeriodicTimer | real | No | |
DR_PickUpRadius | real | Yes | |
DR_ReachedOrb | boolean | Yes | |
DR_RecycledSize | integer | No | |
DR_RecycledStack | integer | Yes | |
DR_SFXManaRestore | string | No | |
DR_SFXManaRestore_AP | string | No | |
DR_SFXOrb | string | No | |
DR_SFXOrb_AP | string | No | |
DR_Spell_ID | integer | No | |
DR_SpellCount | integer | No | |
DR_TempHero | unit | No | |
DR_TempLoc | location | No | |
DR_TempUnit | unit | No | |
DR_Unit | unit | Yes | |
DR_Unit_ID | integer | No | |
DS_Absorb | real | No | |
DS_Max | real | No | |
DS_StatIncrease | real | Yes | |
DS_Target | unit | No | |
DS_Timer | timer | No | |
DSTimer | timer | No | |
DT_Attacktype | attacktype | Yes | |
DT_Damage | real | Yes | |
DT_DamagePerInterval | real | Yes | |
DT_DamageTypes | damagetype | Yes | |
DT_Dealers | unit | Yes | |
DT_Effects | effect | Yes | |
DT_Integers | integer | Yes | |
DT_IntervalReals | real | Yes | |
DT_ReachedDamage | real | Yes | |
DT_ReachedInterval | real | Yes | |
DT_Targets | unit | Yes | |
DTA_Attacktype | attacktype | No | |
DTA_DamageType | damagetype | No | |
DTA_DmgDealer | unit | No | |
DTA_EffectAttachmentPoint | string | No | |
DTA_Interval | real | No | |
DTA_SpecialEffect | modelfile | No | |
DTA_Target | unit | No | |
DTA_Time | real | No | |
DTA_TotalDamageDealt | real | No | |
EarthPanda | unit | No | |
EarthPandaDeathPoint | location | No | |
EarthPandaDeathPoint2 | location | No | |
EasyDialogButton | button | No | |
EB_Points | location | Yes | |
EB_Units | unit | Yes | |
EBCastPoint | location | No | |
EBDamage | real | No | |
EBGroup | group | No | |
EBRuneGroup | group | No | |
Effects | effect | Yes | |
EL_Ability_Level | integer | No | |
EL_Base_Damage | real | No | |
EL_Base_Number_of_Ligtnings | integer | No | |
EL_Caster | unit | No | |
EL_Damage_Area_of_Effect | real | No | |
EL_Damage_Group | group | No | |
EL_Dummy_Ability_Clap | abilcode | No | |
EL_Dummy_Ability_Ligtning | abilcode | No | |
EL_Loop_Location | location | No | |
EL_Owner | player | No | |
EL_Point1 | location | No | |
EL_Point2 | location | No | |
EL_Seek_Area_of_Effect | real | No | |
EL_Seek_Group | group | No | |
EL_Seek_Unit | unit | No | |
EL_Targeted_Location | location | No | |
EL_Total_Damage | real | No | |
EL_Total_Number_of_Ligtnings | integer | No | |
ElectroBall | unit | No | |
ElectroBall2 | unit | No | |
ElectroBallCaster | unit | No | |
ElectroBallCastPoint | location | No | |
ElectroBallDummyPoint | location | No | |
ElectroballPoint | location | No | |
ElectroballPoint2 | location | No | |
ElectroBallTargetGroup | group | No | |
ElectroBallTargetPoint | location | No | |
ElectroBallTimer | timer | No | |
ElectrolBallDummyPoint2 | location | No | |
EndGameTimer | timer | No | |
EndGameTimerWindow | timerdialog | No | |
EnhancedDamageTarget | unit | No | |
EnhancedDamageTarget_Copy | unit | No | |
EntanglingRootDamage | real | No | |
ES_Caster | unit | No | |
ES_Group | group | No | |
ES_Integer | integervar | No | |
ES_Point | location | Yes | |
ES_Target | unit | No | |
ESDummy | unit | No | |
ESPoint | location | No | |
ESPoint2 | location | No | |
EssenceShiftActivate | boolean | No | false |
EssenceShiftDamage | real | Yes | |
EssenceShiftDrainGroup | group | No | |
EssenceShiftDrainGroup2 | group | No | |
EssenceShiftDrainGroup3 | group | No | |
EssenceShiftDrainGroup4 | group | No | |
EssenceShiftLife | boolean | No | true |
EssenceShiftReal | real | Yes | |
EssenceShiftUnit | unit | Yes | |
EXPGloveAttack | integer | Yes | |
ExploPoint | location | No | |
Explosives | unit | No | |
ExplosivesGroup | group | No | |
ExtendLightningCounter | integer | No | |
F_Integers | integer | Yes | |
F_ReachedFading | real | Yes | |
F_Time | real | Yes | |
F_Unit | unit | Yes | |
FA_Caster | unit | No | |
FA_Point | location | No | |
FA_Target | unit | No | |
FA_Time | real | No | |
FA_Unit | unit | No | |
FAGroup | group | No | |
FB_Ability | abilcode | No | |
FB_AoE | real | Yes | |
FB_Dmg | real | Yes | |
FB_Dummies | unitcode | Yes | |
FB_Dummy_Ability | abilcode | No | |
FB_Fx_Blow | string | No | |
FB_Fx_Missile | string | No | |
FB_Fx_Split | string | No | |
FB_Group | group | No | |
FB_HA | hashtable | No | |
FB_MissileSpeed | real | Yes | |
FB_Random_Splitting | boolean | No | |
FB_Spread_Arrows | integer | Yes | |
FB_SpreadAoE | real | Yes | |
FB_SpreadDmg | real | Yes | |
FB_SpreadMissileSpeed | real | Yes | |
FB_Unit | unit | No | |
FBCaster | unit | Yes | |
FBCurrentIndex | integer | No | |
FBGroup | group | Yes | |
FBGroup1 | group | Yes | |
FBMaxIndex | integer | No | |
FBMissile | unit | Yes | |
FBPoint1 | location | Yes | |
FBPoint2 | location | Yes | |
FBPoint3 | location | Yes | |
FC_Ability | abilcode | No | |
FC_AoE1 | real | No | |
FC_AoE2 | real | No | |
FC_Caster | unit | Yes | |
FC_Check | boolean | No | |
FC_Damage1 | real | Yes | |
FC_Damage2 | real | Yes | |
FC_Damage3 | real | Yes | |
FC_Duration | real | Yes | |
FC_Index1 | integer | No | |
FC_Index2 | integer | No | |
FC_Index3 | integer | No | |
FC_Level | integer | No | |
FC_Lightning | lightning | Yes | |
FC_Number | integer | No | |
FC_Point1 | location | No | |
FC_Point2 | location | No | |
FC_Point3 | location | No | |
FC_Real | real | Yes | |
FC_Speed | real | No | |
FC_Target | unit | Yes | |
FC_UnitGroup | group | Yes | |
FC_x1 | location | No | |
FC_x2 | location | No | |
FC_y1 | location | No | |
FC_y2 | location | No | |
FelMeteorAIGroup | group | No | |
FelMeteorAIPoint | location | No | |
FelMeteorAIPoint2 | location | No | |
fi | integer | No | |
FI_Angle | real | Yes | |
FI_AnimSpeed | real | Yes | |
FI_DefaultHeight | real | No | |
FI_EffectA | modelfile | No | |
FI_EffectB | modelfile | No | |
FI_FallHeight | real | No | |
FI_Height | real | Yes | |
FI_Hero | unit | Yes | |
FI_Max | integer | No | |
FI_Sound | string | No | |
FI_Vp | real | Yes | |
FI_Vz | real | Yes | |
FI_z | real | Yes | |
FinalInvasionPortal | destructable | No | |
FinalInvasionTimer | timer | No | |
FinalInvasionTimerWindow | timerdialog | No | |
FinalInvasionUnitRandom | unitcode | Yes | |
FirebossStrikeGroup | group | No | |
FirelordPoint | location | No | |
FirelordPoint2 | location | No | |
FL_Damage | real | No | |
FlameTowerDamage | integer | No | |
FlameTowerLife | integer | No | |
FLOriginalPoint | location | No | |
FM_Ability | abilcode | No | |
FM_AbilityLvl | integer | Yes | |
FM_AbilitySlow | abilcode | No | |
FM_AttackType | attacktype | No | |
FM_Caster | unit | Yes | |
FM_CasterSFX | effect | Yes | |
FM_ChannelTime | real | Yes | |
FM_Cos | real | Yes | |
FM_Counter | real | Yes | |
FM_CurrentHeight | real | Yes | |
FM_CurrentOrder | ordercode | No | |
FM_DamageType | damagetype | No | |
FM_DistanceTraveled | real | Yes | |
FM_DummyOwner | player | No | |
FM_DummyUnitType | unitcode | No | |
FM_FallRateXY | real | Yes | |
FM_FallRateZ | real | Yes | |
FM_FallTime | real | Yes | |
FM_IndicatorSFX | effect | Yes | |
FM_IndicatorSize | real | No | |
FM_IndicatorUnit | unit | Yes | |
FM_LoopInt | integer | No | |
FM_MaxIndex | integer | No | |
FM_MaxLevel | integer | No | |
FM_MeteorDamage | real | Yes | |
FM_MeteorRadius | real | Yes | |
FM_MeteorSFX | effect | Yes | |
FM_MeteorSize | real | No | |
FM_MeteorSpawnDistance | real | No | |
FM_MeteorSpawnHeight | real | No | |
FM_MeteorUnit | unit | Yes | |
FM_NodeNext | integer | Yes | |
FM_NodePrev | integer | Yes | |
FM_Order_ID | string | No | |
FM_Owner | player | Yes | |
FM_PeriodicTimer | real | No | |
FM_Phase | integer | Yes | |
FM_RecycledSize | integer | No | |
FM_RecycledStack | integer | Yes | |
FM_SFXCaster | string | No | |
FM_SFXCaster_AP | string | No | |
FM_SFXIndicator | string | No | |
FM_SFXIndicator_AP | string | No | |
FM_SFXMeteor | string | No | |
FM_SFXMeteor_AP | string | No | |
FM_Sin | real | Yes | |
FM_Spell_ID | integer | No | |
FM_SpellCount | integer | No | |
FM_TargetLoc | location | Yes | |
FM_TempLoc | location | No | |
FM_TempReal | real | No | |
FM_TempUnit | unit | No | |
FODCaster | unit | No | |
FODDamage | real | Yes | |
FODDummy | unit | No | |
FODGroup | group | No | |
FODGroup2 | group | No | |
FODPoint | location | No | |
FODTImer | timer | No | |
FODUnit | unit | Yes | |
FollowMaster_Pet | unit | Yes | |
FollowMasterBool | boolean | Yes | |
FONCaster | unit | No | |
FONDummy | unit | No | |
FONDummy2 | unit | No | |
FONFacing | real | No | |
FONGroup | group | No | |
FONPoint | location | No | |
FortificationGroup | group | No | |
FortificationTimer | timer | No | |
FoV_Caster | unit | No | |
FoV_Damage_Group1 | group | No | |
FoV_Damage_Group2 | group | No | |
FoV_Location | location | No | |
FoV_Total_AoE | real | No | |
FoV_Total_Damage | real | No | |
FreezeBansheeDamagedGroup | group | No | |
FrostBlastAiPoint | location | No | |
FrostBlastAiPoint2 | location | No | |
FrostPathCaster | unit | No | |
FrostPathCasterPoint | location | No | |
FrostPathGroup | group | No | |
FrostPathRandPoint | location | No | |
FrostPathTimer | timer | No | |
FS_Angle | real | Yes | |
FS_AoE | real | No | |
FS_C_Point | location | Yes | |
FS_Caster | unit | Yes | |
FS_Counter | integer | Yes | |
FS_Damage | integer | No | |
FS_Distance | real | Yes | |
FS_Dummy | unit | Yes | |
FS_Effect | effect | Yes | |
FS_effects | string | Yes | |
FS_Group | group | Yes | |
FS_Index | integer | Yes | |
FS_Level | integer | Yes | |
FS_Loop_Integer | integervar | Yes | |
FS_Number_Units | integer | Yes | |
FS_Random | unit | Yes | |
FS_T_Point | location | Yes | |
FS_Total_Counter | integer | Yes | |
FW_Ability_Level | integer | No | |
FW_Base_AoE | real | No | |
FW_Base_Damage | real | No | |
FW_Bonus_AoE | real | No | |
FW_Bonus_Damage | real | No | |
FW_Caster | unit | No | |
FW_Damage_Group | group | No | |
FW_Destroy_Trees | boolean | No | |
FW_Destruct_Kill_AoE | real | No | |
FW_Dummy_Ability | abilcode | No | |
FW_KB_Angle | real | No | |
FW_KB_Base_Distance | real | No | |
FW_KB_Bonus_Distance | real | No | |
FW_KB_Distance | real | No | |
FW_KB_Group | group | No | |
FW_KB_LocationI | location | No | |
FW_KB_LocationII | location | No | |
FW_KB_LocationIII | location | No | |
FW_KB_Picked_Distance | real | No | |
FW_KB_Speed | real | No | |
FW_KnockBack_On | boolean | No | |
FW_Location | location | No | |
FW_Owner | player | No | |
FW_Picked_Location | location | No | |
FW_SFX_Base_Scale | real | No | |
FW_SFX_Bonus_Scale | real | No | |
FW_SFX_Total_Scale | real | No | |
FW_Table | hashtable | No | |
FW_Target_Dummy | unit | No | |
FW_Total_AoE | real | No | |
FW_Total_Damage | real | No | |
g | real | No | |
GeneratorPoint | location | No | |
GhoulSpawnPoint | location | Yes | |
GiantPlantDummy | unit | Yes | |
GiantPlantEQGroup | group | No | |
GiantPlantEQRandomPoint | location | No | |
GiantPlantPoint | location | No | |
GiantPlantRootGroup | group | No | |
GiantPlantSFX | effect | Yes | |
GiantPlantSFXInteger | integer | No | |
GiantPlantSFXPoint | location | Yes | |
GiantPlantSFXPoint2 | location | Yes | |
GiantPlantTimer | timer | No | |
GiantPlantUnit | unit | No | |
GIG_Boolean | boolean | Yes | |
GIGItemCount | integer | No | |
GOM_Integer | integer | Yes | |
GOM_Unit | unit | Yes | |
GOMBonus | integer | Yes | |
GOMCap | integer | Yes | |
GOMCharge | integer | No | |
GOMInteger | integer | No | |
GOMInteger2 | integer | No | |
GOMItem | item | Yes | |
GOMKill | integer | Yes | |
GOMUnit | unit | No | |
group | group | No | |
GT_Angle | real | Yes | |
GT_Caster | unit | No | |
GT_CasterPoint | location | No | |
GT_Damage | real | No | |
GT_DamageGroup | group | No | |
GT_HitGroup | group | No | |
GT_Integer | integer | No | |
GT_Sound | sound | Yes | |
GT_SoundGroup | group | No | |
GT_TempPoint | location | No | |
GT_Timer | timer | No | |
GT_Units | unit | Yes | |
GU_AbilHealing | abilcode | No | |
GU_AbilHero | abilcode | No | |
GU_AbilLevel | integer | No | |
GU_AbilMultiHealing | abilcode | No | |
GU_Caster | unit | No | |
GU_Counter | integer | Yes | |
GU_DamageAdd | real | No | |
GU_DamageMultiplier | real | No | |
GU_Distance | real | No | |
GU_DistMax | real | No | |
GU_EaglesLife | real | No | |
GU_Grp | group | No | |
GU_Guardian | unit | Yes | |
GU_GuardianTy | unitcode | No | |
GU_Heal | real | Yes | |
GU_Index1 | integer | No | |
GU_Index2 | integer | No | |
GU_IndexAr | integer | Yes | |
GU_IndexL | integervar | No | |
GU_IndexMAX | integer | No | |
GU_Level | integer | No | |
GU_Loc1 | location | No | |
GU_Loc2 | location | No | |
GU_Loc3 | location | No | |
GU_Sfx | string | No | |
GU_Target | unit | Yes | |
GU_TargetLife | real | No | |
GU_TargetMaxLife | real | No | |
GU_Timer | timer | No | |
GW_Hero | unit | No | |
GW_TempGroup | group | No | |
GW_TempPoint | location | No | |
GW_TempPoint2 | location | No | |
HardDialogButton | button | No | |
Hashtable | hashtable | No | |
HeaingHashtable | hashtable | No | |
heal_amount | real | No | |
heal_check | boolean | No | |
HEAL_CHECK_INTERVAL | real | No | |
heal_count | integer | No | |
heal_diff | real | No | |
heal_exitwhen | integer | No | |
heal_indexRef | integer | Yes | |
heal_indices | integer | Yes | |
heal_inSys | boolean | Yes | |
heal_integer | integer | No | |
heal_lastLife | real | Yes | |
heal_life | real | No | |
Heal_Real | real | No | |
heal_regen | real | Yes | |
heal_source | unit | No | |
heal_target | unit | No | |
HEAL_THRESHOLD | real | No | |
heal_timer | timer | No | |
HealAdjustInteger | integer | No | |
HealEvent | real | No | |
HealingGroup | group | No | |
HealingRemainingTime | real | No | |
HealingSpiritC | unit | No | |
HealingSpiritD | unit | No | |
HealingSpiritInteger | integer | No | |
HealingSpiritPoint | location | No | |
HealingSpiritPoint2 | location | No | |
HealingSpiritSFX | effect | No | |
HealingSpiritT | timer | No | |
HealInteger | integer | Yes | |
HealPoint | location | No | |
HealReal | real | No | |
HealTarget | unit | No | |
HelmofBattlethirstBool | boolean | Yes | |
Hero | unit | Yes | |
Hero_Pick | unit | Yes | |
HeroAGIBoolean | boolean | Yes | |
herodeathtimer | timer | Yes | |
herodeathtimer1 | timerdialog | Yes | |
herodeathtimer2 | timerdialog | Yes | |
herodeathtimer3 | timerdialog | Yes | |
HeroINTBoolean | boolean | Yes | |
HeroIsAttacked | boolean | Yes | |
HeroIsAttackedTimer | timer | Yes | |
HeroIsAttacking | boolean | Yes | |
HeroIsAttackingTimer | timer | Yes | |
HeroLoopInt | integer | No | |
HeroSTRBoolean | boolean | Yes | |
HeroTokenGroup | group | Yes | |
HeroTokenPoint | location | No | |
HexDummy | unit | No | |
HideDamageFrom | boolean | Yes | |
HideOfLeviatanGroup | group | No | |
HMDamage | real | No | |
HMGroup | group | No | |
HMGroup2 | group | No | |
HMGroup3 | group | No | |
HMHealAmount | real | No | |
HMPoint | location | No | |
HolyFlame | group | No | |
HolyFlameDamage | real | Yes | |
HolyLightGroup | group | No | |
HolylightReals | real | Yes | |
HolylightReals2 | real | Yes | |
HolylightRessurrectLvl | integer | No | |
HolyLightTarget | unit | No | |
HolyLightUnit | unit | No | |
HOTLBoolean | boolean | Yes | |
HOTLGroup | group | No | |
HOTLHP | integer | No | |
HOTLInteger | integer | Yes | |
HOTLItem | item | Yes | |
HOTLPoint | location | No | |
IceWallPoint | location | No | |
IllusionCaster | unit | No | |
InitPoint | location | No | |
InnerFireDamageBuff | boolean | Yes | |
IntegerFB | integer | No | |
IntelligenceScaled | abilcode | Yes | |
InvisibiltyTImer | timer | No | |
InvulBoolean | boolean | Yes | |
InvulSFX | effect | Yes | |
InvulTimer | timer | Yes | |
InvulTimer2 | timer | Yes | |
IS_Ability | abilcode | No | |
IS_Caster | unit | Yes | |
IS_CasterPosition | location | No | |
IS_ChanceEffect | integer | No | |
IS_ChanceLeadEffect | integer | No | |
IS_CollideUnit | unit | No | |
IS_CollideUnitEffect | string | No | |
IS_CollideUnitEffectAttach | string | No | |
IS_CollideUnitPosition | location | No | |
IS_CurrentIndex | integervar | No | |
IS_DummyImpaleAbility | abilcode | No | |
IS_DummyStunAbility | abilcode | No | |
IS_DummyUnitType | unitcode | No | |
IS_GroupContainer | group | Yes | |
IS_ImpactAttackType | attacktype | No | |
IS_ImpactDamage | real | Yes | |
IS_ImpactDamageType | damagetype | No | |
IS_ImpactDummyUnit | unit | No | |
IS_ImpactEffect | string | No | |
IS_ImpactImpaleUnit | unit | No | |
IS_ImpactRadius | real | Yes | |
IS_ImpactScaleRatio | real | No | |
IS_ImpaleAngle | real | Yes | |
IS_ImpaleAttackType | attacktype | No | |
IS_ImpaleBonusRange | real | Yes | |
IS_ImpaleCollisionDamage | real | Yes | |
IS_ImpaleCollisionSize | real | Yes | |
IS_ImpaleDamageType | damagetype | No | |
IS_ImpaleDummyUnit | unit | Yes | |
IS_ImpaleDummyUnitImpaleCaster | unit | No | |
IS_ImpaleDummyUnitPosition | location | No | |
IS_ImpaleDummyUnitStunCaster | unit | No | |
IS_ImpaleEffect | string | No | |
IS_ImpaleEffectDummyUnit | unit | No | |
IS_ImpaleEffectSize | real | Yes | |
IS_ImpaleLeadDummyUnit | unit | No | |
IS_ImpaleLeadEffect | string | No | |
IS_ImpaleLeadSize | real | Yes | |
IS_ImpaleMovementLocation | location | No | |
IS_ImpaleRangeDistance | real | Yes | |
IS_ImpaleReleaseAngle | real | No | |
IS_ImpaleReleaseLocation | location | No | |
IS_ImpaleReleaseOffsetFactor | real | Yes | |
IS_ImpaleScaleRatio | real | No | |
IS_ImpaleSpeed | real | Yes | |
IS_ImpaleSpeedCalculation | real | Yes | |
IS_ImpaleSpeedCounter | real | Yes | |
IS_KB_Acceleration | real | Yes | |
IS_KB_AllowThroughBorders | boolean | No | |
IS_KB_AttackType | attacktype | No | |
IS_KB_DamageType | damagetype | No | |
IS_KB_DestroyTree | boolean | No | |
IS_KB_DisableUnit | boolean | No | |
IS_KB_LoopDamage | real | Yes | |
IS_KB_LoopEffect | string | No | |
IS_KB_LoopEffectAttach | string | No | |
IS_KB_MaxHeight | real | Yes | |
IS_KB_Range | real | Yes | |
IS_KB_Speed | real | Yes | |
IS_KB_TrailEffect | string | No | |
IS_KB_UnitCollision | real | Yes | |
IS_KB_UnpathableStop | boolean | No | |
IS_Level | integer | Yes | |
IS_MaxIndex | integer | No | |
IS_PickedUnit | unit | No | |
IS_PickedUnitPosition | location | No | |
IS_Player | player | Yes | |
IS_SecondaryImpaleReleaseTime | real | Yes | |
IS_SecondaryImpaleTimeCounter | real | Yes | |
IS_TargetLocation | location | Yes | |
IsDamageAttack | boolean | No | |
IsDamageCode | boolean | No | |
IsDamageMelee | boolean | No | |
IsDamageRanged | boolean | No | |
IsDamageSpell | boolean | No | |
IsUnitAlive | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
Item_CanBeDisassemble | boolean | Yes | |
Item_Check | integer | No | |
Item_Count | integer | No | |
Item_CustomValue | integer | No | |
Item_DisassembleAbility | abilcode | No | |
Item_DisassembleEffect | string | No | |
Item_DisassemblePoint | location | Yes | |
Item_Effect | string | No | |
Item_EnableOS | boolean | No | |
Item_Fake | itemcode | Yes | |
Item_LoopA | integer | No | |
Item_LoopB | integer | No | |
Item_LoopC | integer | No | |
Item_Mat1 | itemcode | Yes | |
Item_Mat1Amount | integer | Yes | |
Item_Mat2 | itemcode | Yes | |
Item_Mat2Amount | integer | Yes | |
Item_Mat3 | itemcode | Yes | |
Item_Mat3Amount | integer | Yes | |
Item_Mat4 | itemcode | Yes | |
Item_Mat4Amount | integer | Yes | |
Item_Mat5 | itemcode | Yes | |
Item_Mat5Amount | integer | Yes | |
Item_MatMax | integer | Yes | |
Item_Max | integer | No | |
Item_Owner | player | Yes | |
Item_OwneredItem | item | Yes | |
Item_OwnershipException | group | No | |
Item_Point | location | Yes | |
Item_Real | itemcode | Yes | |
Item_RecipeMax | integer | No | |
Item_Result | itemcode | Yes | |
ItemCleanupFlag | boolean | No | |
ItemCleanupTimer | timer | No | |
ItemCount | integer | No | |
ItemDropChance | integer | No | |
Itemdropposition | location | No | |
Items | itemcode | Yes | |
ItemsToClean | integer | No | |
JD_Angle | real | Yes | |
JD_Animations | string | Yes | |
JD_Distances | real | Yes | |
JD_Effect | string | Yes | |
JD_Group | group | No | |
JD_HighSettings | real | Yes | |
JD_Integers | integer | Yes | |
JD_JumpHigh | real | Yes | |
JD_ReachedDistance | real | Yes | |
JD_RealTimer | real | Yes | |
JD_SpeedUnits | real | Yes | |
JD_TempGroup | group | No | |
JD_TempPoint | location | Yes | |
JD_TreesDestroy | boolean | Yes | |
JD_Unit | unit | Yes | |
JDA_Animation | string | No | |
JDA_AnimationSpeed | real | No | |
JDA_CasterPoint | location | No | |
JDA_Collusion | boolean | No | |
JDA_DestroyTrees_Dash | boolean | No | |
JDA_JumpHigh_Distance | real | No | |
JDA_SpecialEffect | string | No | |
JDA_Speed | real | No | |
JDA_TargetPoint | location | No | |
JDA_TargetUnit | unit | No | |
JDA_Unit | unit | No | |
K_Angle | real | Yes | |
K_Cast_Point | location | No | |
K_Caster | unit | Yes | |
K_Counter | integer | Yes | |
K_Damage | integer | No | |
K_Distance_Times | integer | No | |
K_effects | string | Yes | |
K_Index | integer | Yes | |
K_Level | integer | Yes | |
K_Move_Point | location | Yes | |
K_Speed_Distance | real | No | |
K_Target | unit | Yes | |
K_Target_Point | location | Yes | |
KageBunshinCount | integer | No | |
KageBunshinDummy | unit | No | |
KageBunshinGroup | group | No | |
kb | integer | No | |
KB_Angle | real | Yes | |
KB_Casters | unit | Yes | |
KB_CountBuffs | integer | No | |
KB_DestroyTrees | boolean | Yes | |
KB_Dist | real | Yes | |
KB_EffectCounter | integer | Yes | |
KB_EffectCounter2 | integer | Yes | |
KB_Effects_1 | string | Yes | |
KB_Effects_2 | string | Yes | |
KB_GeneralIntegers | integervar | Yes | |
KB_KnockbackedUnits | group | No | |
KB_Levels | integer | Yes | |
KB_Max | integer | No | |
KB_MaxDist | real | Yes | |
KB_MaxDistance | real | Yes | |
KB_ReachedDistance | real | Yes | |
KB_ReducedReal | real | No | |
KB_ReduceSpeedReal | real | Yes | |
KB_SpecificSpeed | real | Yes | |
KB_Speed | real | Yes | |
KB_StartPositions | location | Yes | |
KB_TempPoint | location | Yes | |
KB_TempReal | real | No | |
KB_TotalKnockUnits | integer | No | |
KB_Unit | unit | Yes | |
KB_Units | unit | Yes | |
KBA_Caster | unit | No | |
KBA_DestroyTrees | boolean | No | |
KBA_DistancePerLevel | real | No | |
KBA_Level | integer | No | |
KBA_SlowDummy | unit | No | |
KBA_SpecialEffects | string | Yes | |
KBA_Speed | real | No | |
KBA_StartingPosition | location | No | |
KBA_TargetUnit | unit | No | |
KeeperofGrove | unit | No | |
Key1 | integer | No | |
Key1FB | integer | No | |
KeyFB | integer | No | |
KillCombo | integer | Yes | |
KillerOfUnit | unit | Yes | |
Kills | integer | Yes | |
KnifeBombDummy | unit | No | |
KnifeBombInteger | integer | No | |
KnifeBombPoint | location | No | |
KnockbackAngle | real | No | |
KnockBackCaster | unit | No | |
KnockbackDistance | real | No | |
KnockBackInteger | integer | No | |
KnockBackPoint | location | No | |
KnockbackSpeed | real | No | |
knockbackTable | hashtable | No | |
KnockbackUnits | group | No | |
LastBossGroup | group | No | |
LastBossMovePoint | location | No | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
LastRights_SE | effect | No | |
LastRights_TargetU | unit | No | |
LastRights_Timer | timer | No | |
LC_Stats | integer | Yes | |
LeaverPlayer_CountRemaining | integer | No | |
LeaverPlayer_Divide_Gold | integer | No | |
LeaverPlayer_Divide_Lumber | integer | No | |
LethalDamageEvent | real | No | |
LethalDamageHP | real | No | |
LethalShotCritChanceBonus | real | No | |
LethalShotCritDamageBonus | real | No | |
levitationon | abilcode | No | |
LichAIBansheeGroup | group | No | |
LichBansheeFreezeCaster | unit | No | |
LichBansheeInteger | integer | No | |
LichBansheePoint | location | No | |
LichBansheePoint2 | location | No | |
LichBansheeTimer | timer | No | |
LichBlinkAIGroup1 | group | No | |
LichBlinkAIGroup2 | group | No | |
LichBlinkPoint1 | location | No | |
LichBlinkPoint2 | location | No | |
LichBlinkTimer | timer | No | |
LichIceShardTimer | timer | No | |
LichPoint | location | Yes | |
LichRandom | real | Yes | |
LichZombieSpawnPoint1 | location | No | |
LichZombieSpawnPoint2 | location | No | |
LifeBonus | abilcode | Yes | |
LifeBonus100 | abilcode | Yes | |
LifeBonus25 | abilcode | Yes | |
LifeBonusDummy | abilcode | Yes | |
LifeBuyer | unit | No | |
LifeDrainAreaCaster | unit | Yes | |
LifeDrainAreaCounter | integer | Yes | |
LifeDrainAreaGroup | group | Yes | |
LifeDrainAreaGroup2 | group | Yes | |
LifeDrainAreaLightning | lightning | Yes | |
LifeDrainAreaPoint | location | Yes | |
LifeDrainAreaPointCaster | location | Yes | |
LifeDrainAreaPointTarget | location | Yes | |
LifeDrainCaster | unit | Yes | |
LifeDrainCurrentInteger | integer | No | |
LifeDrainDummy | unit | No | |
LifeDrainDummy2 | unit | Yes | |
LifeDrainIndex | integer | No | |
LifeDrainIndex2 | integer | No | |
LifeDrainLoop | integer | No | |
LifeDrainMaxInteger | integer | No | |
LifeDrainTempPoint | location | No | |
LifeDrainUnit2 | unit | Yes | |
Lightning | lightning | Yes | |
Lightning1 | unit | Yes | |
Lightning1Counter | integer | No | |
Lightning1FacingAng | real | No | |
Lightning1GROUP | group | Yes | |
Lightning1Integer | integer | No | |
Lightning1Integer2 | integer | No | |
Lightning1Point1 | location | No | |
Lightning1Point2 | location | No | |
Lightning1Point3 | location | No | |
LightningBlinkGroup | group | No | |
LightningBlinkPoint | location | Yes | |
LightningBlinkTimer | timer | No | |
LightningBoltTarget | unit | No | |
LightningFieldCaster | unit | No | |
LightningFieldDamage | real | No | |
LightningFieldDamageDumy | unit | Yes | |
LightningFieldDamageIndex | integer | No | |
LightningFieldDummy | unit | No | |
LightningFieldGroup | group | No | |
LightningFieldGroup2 | group | No | |
LightningFieldLearnGroup | group | No | |
LightningFieldLevel | integer | No | |
LightningFieldLightning | lightning | Yes | |
LightningFieldPoint | location | No | |
LightningFieldPoint2 | location | No | |
LimitedLives | button | No | |
LiveDialog | dialog | No | |
LKRU | unitcode | Yes | |
loc1FB | location | No | |
loc2 | location | No | |
loc2FB | location | No | |
loc3 | location | No | |
loc3FB | location | No | |
LocalPlayer | player | No | |
locFB | location | No | |
Loop | integervar | No | |
LS_Caster | unit | No | |
LS_Distance | integer | No | |
LS_Dummy | unit | No | |
LS_Integer | integer | No | |
LS_TargetPoint | location | No | |
LS_Timer | timer | Yes | |
LSGroup | group | No | |
LSHealingGroup | group | No | |
LSTempPoint | location | No | |
LSTempPoint2 | location | No | |
M_Angle | real | No | |
M_AnglePlus | real | No | |
M_Caster | unit | No | |
M_Damage | real | Yes | |
M_DamageLoop | real | No | |
M_Denominator | real | No | |
M_DistanceBase | real | No | |
M_Duration | real | Yes | |
M_Hash | hashtable | No | |
M_Levels | integer | No | |
M_Loops | real | No | |
M_Numerator | real | No | |
M_Picked | unit | No | |
M_PickedHandle | integer | No | |
M_PickedLoc | location | No | |
M_PickedV | unit | No | |
M_PickedVLoc | location | No | |
M_Radius | real | No | |
M_SFXOffset | location | No | |
M_Speed | real | No | |
M_SpeedBase | real | No | |
M_SuckOffset | location | No | |
M_SuckSFX | string | No | |
M_TargetLoc | location | No | |
M_THandle | integer | No | |
M_Tornado | unit | No | |
M_TornadoGroup | group | No | |
M_TornadoSFX | string | No | |
M_VictimGroup | group | No | |
MagicPotionBoolean | boolean | Yes | |
MagicPotionBoolean2 | boolean | Yes | |
MagicPotionFinished | item | Yes | |
MagicTrinketGroup | group | No | |
MagicTrinketTempGroup | group | No | |
MagicTrinUnit | unit | No | |
MagicWandGroup | group | No | |
ManaStoneBoolean | boolean | Yes | |
ManaStoneCounter | integer | No | |
Map_Point | location | No | |
Map_Point2 | location | No | |
Map_Point3 | location | No | |
MassHexTempPoint | location | No | |
MassRootCaster | unit | No | |
MassRootCount | integer | No | |
MassRootDummy | unit | No | |
MassRootGroup | group | No | |
MassRootGroup2 | group | No | |
MassRootSFX | effect | Yes | |
MassRootSFX2 | effect | No | |
MassRootSFX3 | effect | No | |
MassRootSFXInteger | integer | No | |
MassRootSFXPoint | location | No | |
MassRootTargetPoint | location | No | |
MassRootTimer | timer | No | |
MazeCheckGroup | group | No | |
MazeTester | unit | No | |
MazeTestPoint1 | location | No | |
MazeTestPoint2 | location | No | |
MazeTestTimer | timer | No | |
MazeTestTimer2 | timer | No | |
MB_DyingU | unit | Yes | |
MB_KillingU | unit | Yes | |
MB_KillingU_Owner | player | No | |
MB_Owner_Killing_PNumber | integer | No | |
MB_TeamOne | force | No | |
MechDeathPoint | location | No | |
MegaTreantCaster | unit | No | |
MegaTreantCounter | integer | Yes | |
MegaTreantCounter2 | integer | Yes | |
MegaTreantGroup | group | No | |
MegaTreantKillCount | integer | Yes | |
MegaTreantTargetPoint | location | No | |
MegaTreantUnit | unit | Yes | |
MessageTips | string | Yes | |
MessageUnits | string | Yes | |
MindBurstAIPoint | location | No | |
MindBurstAIPoint2 | location | No | |
MindBurstGroup | group | No | |
MoonStaffDamage | real | No | |
MoonStaffGroup | group | No | |
MoonStaffTarget | unit | No | |
MoonStaffUser | unit | No | |
MortarTeam | boolean | No | |
MousePoint | location | No | |
MoveSpeedHero | unit | No | |
MS_Points | location | Yes | |
MS_TempPoint | location | No | |
MS_Units | unit | Yes | |
MSandSBGloveCombineDMG | real | No | |
MSCheckGroup | group | No | |
MSGroup | group | No | |
MSGroup2 | group | No | |
Multiboard | multiboard | No | |
MW_Caster | unit | No | |
MW_DamageTaken | real | No | |
MW_Heal | integer | No | |
MW_SecondHealing | real | No | |
MW_SecondHealing3 | real | No | |
MW_SecondHealing4 | integer | No | |
MW_SFX | effect | No | |
MW_Timer | timer | No | |
MW_Timer2 | timer | No | |
MWSecondHealing2 | real | Yes | |
NextDamageIsAttack | boolean | No | |
NextDamageIsMelee | boolean | No | |
NextDamageIsRanged | boolean | No | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
NextDamageType_Copy | integer | No | |
NextDamageWeaponT | integer | No | |
NextHealAmount | real | No | |
NextHealSource | unit | No | |
NextHealTarget | unit | No | |
NoOfPlayer | integer | No | |
NormalDialogButton_Copy | button | No | |
OffMapDestruction | location | No | |
OgreSwingArcAngle | real | Yes | |
OgreSwingCaster | unit | Yes | |
OgreSwingCasterPoint | location | Yes | |
OgreSwingCurrentIndex | integer | No | |
OgreSwingCurrentSpeed | real | Yes | |
OgreSwingDeceleration | real | No | |
OgreSwingDistance | real | No | |
OgreSwingDummy | unit | No | |
OgreSwingFacingAngle | real | Yes | |
OgreSwingMaxIndex | integer | No | |
OgreSwingMinSpeed | real | No | |
OgreSwingTarget | unit | Yes | |
OgreSwingTargetPoint | location | Yes | |
OgreSwingTempGroup | group | No | |
OgreSwingTempPoint | location | Yes | |
OnPoint | location | Yes | |
OODGroup2 | group | Yes | |
OODGroupAGI | group | No | |
OODGroupINT | group | No | |
OODGroupSTR | group | No | |
PC_AoE | real | No | |
PC_Caster | unit | No | |
PC_Caster_Loc | location | No | |
PC_Detect | real | No | |
PC_Distance | real | No | |
PC_Dummy | unit | No | |
PC_Dummy_Loc | location | No | |
PC_Group | group | No | |
PC_Jumps | integer | No | |
PC_Movement | real | No | |
PC_Offset | location | No | |
PC_StartTimer | timer | No | |
PC_Target | unit | No | |
PC_Target_Loc | location | No | |
PC_Timer | real | No | |
PD_Angle | real | Yes | |
PD_Distances | real | Yes | |
PD_Integers | integer | Yes | |
PD_ReachedDistance | real | Yes | |
PD_RealTimer | real | Yes | |
PD_SpeedUnits | real | Yes | |
PD_TempPoint | location | Yes | |
PD_TestGroup | group | No | |
PD_TreesDestroy | boolean | Yes | |
PD_Unit | unit | Yes | |
PeasantPoint | location | No | |
PHYSICAL | integer | No | |
PitlordChargeAIGrp | group | No | |
PitlordSFX | effect | No | |
PlantMissile_Point | location | No | |
PlantMissile_Target | unit | No | |
PlantMissileGroup | group | No | |
PlantMissileRealDamage | real | No | |
PlantSeedRandomChance | integer | No | |
Player_Kills | integer | Yes | |
PlayerLife | unit | Yes | |
players_playing | force | No | |
point | location | No | |
point2 | location | No | |
POLDamage | real | Yes | |
POLDUMMY | unit | No | |
POLFacing | real | No | |
POLFinalGroup | group | No | |
POLGroup | group | Yes | |
POLPoint | location | No | |
POLPoint2 | location | No | |
POLSFX | effect | Yes | |
POLTarget | location | Yes | |
Power_Slam_Ability_Level | integer | No | |
Power_Slam_Caster | unit | No | |
Power_Slam_Damage | real | No | |
Power_Slam_Damage_Multiplier | real | No | |
Power_Slam_Dummy | unit | No | |
Power_Slam_Dummy_Ability | abilcode | No | |
Power_Slam_Dummy_Ability2 | abilcode | No | |
Power_Slam_Location | location | No | |
Power_Slam_Owner | player | No | |
Power_Slam_Strength | integer | No | |
Power_Slam_Target | unit | No | |
PowerSlamCaster2 | unit | No | |
PowerSlamGroup | group | No | |
PowerSlamPoint2 | location | No | |
PR | integer | No | |
PR_CenterPoint | location | Yes | |
PR_Chance | integer | No | |
PR_Damage | real | Yes | |
PR_DebugGroup | group | Yes | |
PR_Effects | integer | No | |
PR_Group | group | No | |
PR_Hero | unit | Yes | |
PR_Interval | real | Yes | |
PR_IntervalSkip | real | Yes | |
PR_MaxRadius | real | Yes | |
PR_On | boolean | Yes | |
PR_Perimeter | real | Yes | |
PR_Point | location | Yes | |
PR_Radius | real | Yes | |
PR_RingWide | real | Yes | |
PR_Skip | integer | No | |
PR_Times | integer | No | |
PR_Waves | integer | Yes | |
PRE | integervar | No | |
Projectile_AoE | real | No | |
Projectile_AttackType | attacktype | No | |
Projectile_Damage | real | No | |
Projectile_DamageType | damagetype | No | |
Projectile_DestroyTree | boolean | No | |
Projectile_HArc | boolean | No | |
Projectile_HArcLeftOrRight | boolean | No | |
Projectile_HArcWidth | real | No | |
Projectile_Height | real | No | |
Projectile_Impact | string | No | |
Projectile_Model | modelfile | No | |
Projectile_Size | real | No | |
Projectile_Source | unit | No | |
Projectile_Speed | real | No | |
Projectile_TargetPoint | location | No | |
Projectile_TargetUnit | unit | No | |
Projectile_Trigger | trigger | No | |
Projectile_UnitOrPoint | boolean | No | |
Projectile_VArc | boolean | No | |
Projectile_VArcHeight | real | No | |
PS_Damage | integer | No | |
PS_Target | unit | No | |
PugnaAI2 | group | No | |
PugnaAIPoint | location | Yes | |
PugnaDATimer | timer | No | |
PugnaRandom | integer | No | |
PugnaROXGroup | group | No | |
PugnaROXPoint | location | No | |
PugnaTrapTimer | timer | No | |
PummelDummy | unit | No | |
PummelGroup | group | No | |
PummelPoint | location | No | |
PummelReal | real | No | |
PummelReal2 | real | No | |
PummelUnit | unit | No | |
pureAmount | real | No | |
PurificationCaster | unit | No | |
PurificationDamageAmount | real | Yes | |
PurificationDamageCap | real | No | |
PurificationDamageIncrease | real | Yes | |
PurificationDamageTaken | real | No | |
PurificationDummy | unit | No | |
PurificationInteger | integer | Yes | |
PurificationPoint | location | No | |
PurificationPoint2 | location | No | |
PurificationPoint3 | location | No | |
PurificationSFX | effect | Yes | |
PurificationTarget | unit | No | |
PurificationTempGroup | group | No | |
PurificationTime | real | Yes | |
PurificationTimer | timer | No | |
PurifyCaster | unit | No | |
PurifyCastPoint | location | Yes | |
PurifyDummy | unit | Yes | |
PurifyDummy2 | unit | No | |
PurifyGroup | group | No | |
PurifyPoint | location | No | |
PurifyPoint2 | location | No | |
PurifyTimer | timer | No | |
PurifyTimer2 | timer | No | |
PurplePotDamageInteger | integer | Yes | |
PurplePotDefenceInteger | integer | Yes | |
PurplePotHealthInteger | integer | Yes | |
QJC_Ally | boolean | No | |
QJC_Amount | real | No | |
QJC_AmountReduce | real | No | |
QJC_AoE | boolean | No | |
QJC_AoERadius | real | No | |
QJC_AttackType | attacktype | No | |
QJC_Caster | unit | No | |
QJC_ChainSFX | lightningtype | No | |
QJC_Damage | boolean | No | |
QJC_DamageType | damagetype | No | |
QJC_Enemy | boolean | No | |
QJC_Gold | boolean | No | |
QJC_Heal | boolean | No | |
QJC_Heatlh | boolean | No | |
QJC_JumpCount | integer | No | |
QJC_JumpDelayTime | real | No | |
QJC_JumpRadius | real | No | |
QJC_Leech | boolean | No | |
QJC_Mana | boolean | No | |
QJC_NoTarget | boolean | No | |
QJC_OnePerUnit | boolean | No | |
QJC_Priority | boolean | No | |
QJC_Slow | boolean | No | |
QJC_SlowEffect | abilcode | No | |
QJC_Stun | boolean | No | |
QJC_StunEffect | abilcode | No | |
QJC_TargetPoint | location | No | |
QJC_TargetSFX | string | No | |
QJC_TargetUnit | unit | No | |
QJCS_aGroup | group | Yes | |
QJCS_Ally | boolean | Yes | |
QJCS_Amount | real | Yes | |
QJCS_AmountReduce | real | Yes | |
QJCS_AoE | boolean | Yes | |
QJCS_AoERadius | real | Yes | |
QJCS_AttackType | attacktype | Yes | |
QJCS_baseDummy | unit | Yes | |
QJCS_Caster | unit | Yes | |
QJCS_ChainSFX | lightningtype | Yes | |
QJCS_ChainX | real | Yes | |
QJCS_ChainY | real | Yes | |
QJCS_ChainZ | real | Yes | |
QJCS_currUnit | unit | No | |
QJCS_Damage | boolean | Yes | |
QJCS_DamageType | damagetype | Yes | |
QJCS_dGroup | group | Yes | |
QJCS_dummyGroup | group | No | |
QJCS_Enemy | boolean | Yes | |
QJCS_Gold | boolean | Yes | |
QJCS_Heal | boolean | Yes | |
QJCS_Health | boolean | Yes | |
QJCS_index | integer | Yes | |
QJCS_JumpCount | integer | Yes | |
QJCS_JumpDelayTime | real | Yes | |
QJCS_JumpRadius | real | Yes | |
QJCS_Leech | boolean | Yes | |
QJCS_LightningDPos | unit | Yes | |
QJCS_LightningDur | real | Yes | |
QJCS_LightningIndex | integer | Yes | |
QJCS_LightningPos | location | Yes | |
QJCS_LightningSFX | lightning | Yes | |
QJCS_LightningTarget | unit | Yes | |
QJCS_Locust | abilcode | No | |
QJCS_Mana | boolean | Yes | |
QJCS_OnePerUnit | boolean | Yes | |
QJCS_prevTarget | unit | Yes | |
QJCS_Priority | boolean | Yes | |
QJCS_Slow | boolean | Yes | |
QJCS_SlowEffect | abilcode | Yes | |
QJCS_Stun | boolean | Yes | |
QJCS_StunEffect | abilcode | Yes | |
QJCS_SystemCount | integer | Yes | |
QJCS_SystemTime | real | Yes | |
QJCS_TargetSFX | string | Yes | |
QJCS_TargetUnit | unit | Yes | |
QJCS_tempGroup | group | No | |
QJCS_tempPos | location | Yes | |
QJCS_victimGroup | group | No | |
real | real | No | |
real2 | real | No | |
RecipeSystem_TempUnit | unit | No | |
ReflectGroup | group | No | |
regen_buildup | real | Yes | |
REGEN_EVENT_INTERVAL | real | No | |
REGEN_STRENGTH_VALUE | real | No | |
REGEN_THRESHOLD | real | No | |
regen_timeleft | real | Yes | |
RegenIncrease | abilcode | Yes | |
RegenLevel | integer | No | |
RegenTowerGroup | group | No | |
RemainingTime | real | No | |
ReportLife | real | No | |
RESCheck | boolean | No | |
Reseter | unit | No | |
RESPoint | location | No | |
RESPoint2 | location | No | |
RESSFX | effect | No | |
RESSFX2 | effect | No | |
ReturnEssenceGroup | group | No | |
ReturnEssencePoint | location | No | |
ReturnEssenceReal | real | No | |
RevivalDialog | dialog | No | |
ReviveNo | button | No | |
ReviveTime | integer | Yes | |
ReviveYes | button | No | |
RoarCaster | unit | No | |
RoarDummy | unit | No | |
RoarGroup | group | No | |
RoarPoint | location | No | |
RoarPoint2 | location | No | |
RoarTarget | unit | No | |
RoarTimer | timer | No | |
RocketTowerBlasterUnit | unit | No | |
RocketTowerMissileGroup | group | No | |
RocketTowerMissilePoint | location | No | |
RocketTowerTargetedUnit | unit | No | |
RocketTurretCaster | unit | No | |
RocketTurretRealDamage | real | No | |
RocketTurretUnit | unit | No | |
RockTossAbilityLvl | integer | No | |
RockTossCaster | unit | No | |
RockTossCastPoint | location | No | |
RockTossDummy | unit | No | |
RockTossGroup | group | No | |
RockTossGroup2 | group | No | |
RockTossTargetPoint | location | No | |
RogueAssasin | unit | No | |
ROT_Caster | unit | No | |
ROT_Damage | real | No | |
ROT_Dummy | unit | No | |
ROT_Dummy2 | unit | Yes | |
ROT_FaceAngle | real | No | |
ROT_Point1 | location | No | |
ROT_Point2 | location | No | |
ROT_Point3 | location | No | |
ROT_Point4 | location | No | |
ROT_Timer | timer | Yes | |
ROT_Timer2 | timer | No | |
RoX_AoE | real | Yes | |
RoX_Caster | unit | Yes | |
RoX_Damage | real | Yes | |
RoX_DelayEachWave | real | Yes | |
RoX_DelayTimer | real | Yes | |
RoX_HitAllies | boolean | Yes | |
RoX_HitBuildings | boolean | Yes | |
RoX_Index | integer | Yes | |
RoX_NumberOfShards | integer | Yes | |
RoX_NumberOfWaves | integer | Yes | |
RoX_Off | boolean | Yes | |
RoX_OnHitEffect | string | Yes | |
RoX_Player | player | Yes | |
RoX_Point | location | Yes | |
RoX_ShardFallSpeed | real | Yes | |
RoX_ShardHeight | real | Yes | |
RoX_ShardModel | string | Yes | |
RoX_ShardsSpawnCounter | integer | Yes | |
RoX_TargetPoint | location | Yes | |
RoX_UnitGroup | group | Yes | |
RoXS_Height | real | Yes | |
RoXS_Index | integer | Yes | |
RoXS_ModelEffect | effect | Yes | |
RoXS_Off | boolean | Yes | |
RoXS_ShardFallSpeed | real | Yes | |
RoXS_Unit | unit | Yes | |
RS_BloodEffectInt | integer | No | |
RS_Dummy | unit | No | |
RSCaster | unit | Yes | |
RSCount | integer | No | |
RSDamage | real | Yes | |
RSDamage2 | real | No | |
RSDistance | real | Yes | |
RSDummyEffect | abilcode | No | |
RSDummySkill | abilcode | No | |
RSEffectDelayer | integer | Yes | |
RSHas | boolean | Yes | |
RSHeroEffect | abilcode | No | |
RSIndex | integer | No | |
RSInteger | integer | No | |
RSLastRecycled | integer | No | |
RSMax | integer | No | |
RSRecycledList | integer | Yes | |
RSRemaining | integer | Yes | |
RSTarget | unit | Yes | |
RSTempInt | integer | No | |
RSTempLoc | location | No | |
RSTempLoc2 | location | No | |
RSTempLoc3 | location | No | |
RSTempUnit | unit | No | |
RunicBraceletArmor | abilcode | Yes | |
RunicBraceletCounter | integer | Yes | |
RunicBraceletGroup | group | No | |
RunicBraceletGroup2 | group | No | |
RunicBraceletHealth | abilcode | Yes | |
RunicBraceletInteger | integer | Yes | |
RunicBraceletItems | itemcode | Yes | |
RunicBraceletMana | abilcode | Yes | |
RunicBraceletPoint | location | No | |
RunicBraceletRegen | abilcode | Yes | |
Sample_Group | group | No | |
Sample_Group2 | group | No | |
Sample_Point | location | No | |
Sample_Point2 | location | No | |
SB_AttackPoint | location | No | |
SB_TImer | timer | No | |
SBB_AbilityLvl | integer | No | |
SBB_CastPoint | location | No | |
SBB_Damage | real | No | |
SBB_Dummy | unit | No | |
SBB_Group | group | No | |
SBB_Group2 | group | No | |
SBB_TargetPoint | location | No | |
SBBCaster | unit | No | |
SBBDamage | real | Yes | |
SBBDummy | unit | Yes | |
SBBIndex | integer | No | |
ScepterOfProtectionTargetUnit | unit | No | |
ScepterOfProtectionTimer | timer | No | |
SE_Attach_Point | string | Yes | |
SE_AttackType | attacktype | No | |
SE_DamageType | damagetype | No | |
SE_Effect | modelfile | Yes | |
SE_ExplosionDamages | real | Yes | |
SE_ExplosionDamagesAoe | real | Yes | |
SE_ExplosionModel | string | No | |
SE_Hash | hashtable | No | |
SE_Index1 | integervar | No | |
SE_Index2 | integer | No | |
SE_Integer | integer | No | |
SE_Integer2 | integer | No | |
SE_Integer3 | integer | No | |
SE_Integer4 | integer | No | |
SE_Point | location | No | |
SE_Point2 | location | No | |
SE_Real | real | No | |
SE_Real2 | real | No | |
SE_Real3 | real | No | |
SE_RockGroup | group | No | |
SE_RockHeightDecrement | real | Yes | |
SE_RockHeightSpeed | real | Yes | |
SE_RocksAngle | real | Yes | |
SE_RocksAngleRandom | real | Yes | |
SE_RockScaleMax | real | Yes | |
SE_RockScaleMin | real | Yes | |
SE_RocksDamages | real | Yes | |
SE_RocksDamagesAoe | real | Yes | |
SE_RocksIntervalAngle | real | Yes | |
SE_RocksNumber | integer | Yes | |
SE_RockSpeed | real | Yes | |
SE_RocksUnitType | unitcode | No | |
SE_SandInterval | integer | Yes | |
SE_SandModel | string | No | |
SE_SandSpeed | real | Yes | |
SE_SandUnitType | unitcode | No | |
SE_Special_Effect | effect | Yes | |
SE_Target | unit | Yes | |
SE_Time | real | Yes | |
SE_Unit | unit | No | |
SE_Unit2 | unit | No | |
SE_WaveGroup | group | No | |
SeedCaster | unit | No | |
SeedDamageGroup | group | No | |
SeedLevelInteger | integer | No | |
SeedLevelInteger2 | integer | No | |
SeedLevelInteger3 | integer | No | |
SeedRealDamage | real | No | |
SeigeGolem | boolean | No | |
SeismicSlamDummy | unit | No | |
SelectedBoss | unit | No | |
SET_MAX_LIFE | integer | No | |
SFH_SpawnPoint | location | No | |
SFH_SpawnPoint2 | location | No | |
SGArrow | unit | Yes | |
SGCaster | unit | Yes | |
SGCurrentIndex | integervar | No | |
SGGroup | group | Yes | |
SGGroup1 | group | Yes | |
SGMaxIndex | integer | No | |
SGPoint | location | Yes | |
SGPoint1 | location | Yes | |
SGPoint2 | location | Yes | |
SGTarget | unit | Yes | |
ShackleCaster | unit | No | |
ShackleTarget | unit | No | |
ShackleTimer | timer | No | |
ShadowCallAIGroup | group | No | |
ShadowStrikeD | unit | No | |
ShadowStrikeDummy | unit | No | |
ShadowStrikeGroup | group | No | |
ShadowStrikeGroup2 | group | No | |
ShadowStrikeInteger | integer | No | |
ShadowStrikePoint | location | No | |
ShadowStrikePoint2 | location | No | |
ShadowStriker | unit | No | |
ShadowStrikeTimer | timer | Yes | |
ShadowStrikeTImer | timer | No | |
ShadowWOlf | unit | No | |
ShadowWolfGroup | group | No | |
ShadowWolfPoint | location | No | |
Shake | real | No | |
ShakeTimer | timer | No | |
ShamanBallPoint | location | No | |
ShamanBallPoint2 | location | No | |
SHC_Integer | integer | No | |
SHC_Point | location | No | |
SHC_Point2 | location | No | |
SHC_Random | real | Yes | |
ShifterShieldBoolean | boolean | Yes | |
ShifterShieldBoolean2 | boolean | Yes | |
ShifterShieldIndex | integer | No | |
ShifterShieldInteger | integer | No | |
ShifterShieldMaxIndex | integer | No | |
ShifterShieldUnit | unit | Yes | |
Shockwave_TempUnit | unit | No | |
Skill_FeralImpact | abilcode | No | |
SOG_Boolean | boolean | Yes | |
SOGGroup | group | Yes | |
SOGGroup2 | group | No | |
SOGHero | unit | No | |
SOGPoint | location | Yes | |
SOGReal | real | Yes | |
SoulEaterBoolean | boolean | Yes | |
SoulEaterCharge | integer | Yes | |
SoulEaterCharge2 | integer | Yes | |
SoulEaterCounter | integer | No | |
SoulEaterItem | item | Yes | |
SoulEaterReal | real | No | |
SoulGatherSFX | effect | No | |
SoulGatherTimer | timer | No | |
SoulGatherTimer2 | timer | No | |
SoulgemBoolean | boolean | Yes | |
SoulGemGroup | group | No | |
SpeBombsCount | integer | Yes | |
SpecCaster | unit | No | |
SpecCirCenter | location | No | |
SpecialEffecTargetNoArray | effect | Yes | |
SpecLocPoint | location | Yes | |
SPELL | integer | No | |
SPELL_DAMAGE_REDUCTION_ITEM | integer | No | |
SPELL_RESISTANCE_AUTO_DETECT | boolean | No | |
SpellbindGloveDamageAmount | real | No | |
SpellBindGloveGroup | group | No | |
SpellbindGloveTarget | unit | No | |
SpellbindGloveUser | unit | No | |
SpellDamageAbility | abilcode | No | |
SpellLevel | integer | No | |
SpellShieldboolean | boolean | Yes | true |
SpellShieldPoint | location | No | |
SpellShieldTimer | timer | Yes | |
SpiritNature | unit | No | |
SpiritNatureGAIN | real | No | |
SpiritWolfPoint | location | No | |
SpiritWolfRandomPoint | location | No | |
SPSD_Caster | unit | No | |
SPSD_TargetPoint | location | No | |
SPSD_TargetUnit | unit | No | |
ST_Arrow | unit | Yes | |
ST_Debris | unit | Yes | |
ST_heightVelocity | integer | Yes | |
StartWaveTimer | timer | No | |
StartWaveTimerWindow | timerdialog | No | |
StasisTimer | timer | No | |
StasisUnit | unit | No | |
StatGained | integer | Yes | |
STCaster | unit | Yes | |
STCurrentIndex | integer | No | |
STDistance | real | Yes | |
STDistance2 | real | Yes | |
STDistance3 | real | Yes | |
STGroup | group | Yes | |
STGroup2 | group | Yes | |
STHeight | real | Yes | |
STMaxIndex | integer | No | |
StoneSkinDummy | unit | No | |
StoneSkinGroup | group | No | |
StoneSkinPoint | location | No | |
StormBoltDummyCaster | unit | No | |
StormBoltTempPoint | location | No | |
STPoint | location | Yes | |
STPoint1 | location | Yes | |
STPoint2 | location | Yes | |
StrafeCaster | unit | No | |
StrafeInteger | integer | Yes | |
StrafeSFX | effect | Yes | |
StrafeStart | boolean | No | |
StrafeTimer | timer | No | |
StrengthIncrease | abilcode | Yes | |
SummonBossCounter | group | No | |
Summoner | unit | No | |
SummonerOfUnit | unit | Yes | |
Summons | location | Yes | |
Summons2 | location | No | |
Summons3 | location | No | |
SW_AffectAir | boolean | Yes | |
SW_AffectAlly | boolean | Yes | |
SW_AffectEnemy | boolean | Yes | |
SW_AffectGround | boolean | Yes | |
SW_AffectStructure | boolean | Yes | |
SW_Angle | real | No | |
SW_AreaCurrent | real | Yes | |
SW_AreaFinal | real | Yes | |
SW_AreaGrowth | real | Yes | |
SW_AreaInitial | real | Yes | |
SW_Attachment | string | No | |
SW_AttackType | attacktype | Yes | |
SW_Caster | unit | Yes | |
SW_Damage | real | Yes | |
SW_DamageType | damagetype | Yes | |
SW_DamageX | real | Yes | |
SW_Distance | real | Yes | |
SW_DistanceX | real | Yes | |
SW_Dummy | unit | Yes | |
SW_ExtraEffect | trigger | Yes | |
SW_ExtraEffectCheck | boolean | Yes | |
SW_ExtraEffectTarget | trigger | Yes | |
SW_ExtraEffectTargetCheck | boolean | Yes | |
SW_Group | group | No | |
SW_GroupDone | group | Yes | |
SW_Loop | integervar | No | |
SW_LoopingSfx | boolean | Yes | |
SW_Lvl | integer | Yes | |
SW_Max | integer | No | |
SW_NextPoint | location | Yes | |
SW_OneInstance | boolean | Yes | |
SW_Point | location | Yes | |
SW_Speed | real | Yes | |
SW_SpeedX | real | Yes | |
SW_Wave | effect | Yes | |
SW_WaveEffect | modelfile | Yes | |
SW_WaveEffectEnd | modelfile | Yes | |
SW_WaveEffectLoop | modelfile | Yes | |
TASability | abilcode | No | |
TASdur | real | Yes | |
TASduration | real | No | |
TASend | boolean | Yes | |
TAShash | hashtable | No | |
TASids | integer | Yes | |
TASindex | integer | No | |
TASmaxstack | integer | No | |
TASref | boolean | Yes | |
TASrefresh | boolean | No | |
TAStimer | timer | No | |
TASunit | unit | No | |
TASunits | unit | Yes | |
TCGroup | group | No | |
Te_A | real | No | |
Te_AbCode | abilcode | No | |
Te_AbConvert | abilcode | No | |
Te_AboutToBreak | boolean | Yes | |
Te_AboutToBreakDist | real | Yes | |
Te_AffectedUnits | group | Yes | |
Te_AttachCasterSFX | string | No | |
Te_AttachTargetSFX | string | No | |
Te_AttackType | attacktype | No | |
Te_B | real | No | |
Te_BreakDist | real | Yes | |
Te_C | real | No | |
Te_Caster | unit | Yes | |
Te_CasterHP | real | Yes | |
Te_CasterMana | real | Yes | |
Te_Damage | real | Yes | |
Te_DamageType | damagetype | No | |
Te_Duration | real | Yes | |
Te_IndexedLevel | integer | Yes | |
Te_LatchDistance | real | Yes | |
Te_Latching | boolean | Yes | |
Te_LatchSpeed | real | No | |
Te_LatchStopDist | real | No | |
Te_Level | integer | No | |
Te_LifeIncrease | real | No | |
Te_LightningType | lightningtype | No | |
Te_LightningTypeBreaking | lightningtype | No | |
Te_LineDist | real | No | |
Te_LinkEffect | lightning | Yes | |
Te_LoopInt | integer | No | |
Te_LoopInt2 | integer | No | |
Te_ManaIncrease | real | No | |
Te_MaxIndex | integer | No | |
Te_Owner | player | Yes | |
Te_SFXCaster | effect | Yes | |
Te_SFXTarget | effect | Yes | |
Te_Slow_Duration | real | Yes | |
Te_Target | unit | Yes | |
Te_TargetGain | real | Yes | |
Te_TempLoc1 | location | No | |
Te_TempLoc2 | location | No | |
Te_TempLoc3 | location | No | |
Te_TempReal | real | No | |
Te_TempReal2 | real | No | |
Te_TempUnit | unit | No | |
Te_TimeLeft | real | Yes | |
Te_X0 | real | No | |
Te_X1 | real | No | |
Te_X2 | real | No | |
Te_Y0 | real | No | |
Te_Y1 | real | No | |
Te_Y2 | real | No | |
TeBr_AbCode | abilcode | No | |
TeCounter | integer | Yes | |
TeleportBoolean | boolean | Yes | |
TeleportCaster | unit | Yes | |
TeleportCounter | integer | Yes | |
TeleportCurrentIndex | integervar | No | |
TeleportMaxIndex | integer | No | |
TeleportPoint | location | Yes | |
TeleportPoint2 | location | No | |
tempGroup | group | No | |
TempIntRecipeSystem | integer | No | |
TempItem | item | No | |
TempLoc | location | No | |
TempPoint | location | No | |
tempUnit | unit | No | |
TeS_AbCode | abilcode | No | |
TeS_BuffCode | buffcode | No | |
TeS_LoopInt | integer | No | |
Tes_LoopInt2 | integer | No | |
TeS_MaxIndex | integer | No | |
TeS_TimeLeft | real | Yes | |
TeS_Unit | unit | Yes | |
TeSpd_AbCode | abilcode | No | |
TeSpd_BuffCode | buffcode | No | |
TetheredBoolean | boolean | Yes | |
ThunderStormTimer | timer | No | |
Timestamp | timer | No | |
TipsMessage | string | Yes | |
TipsShowGroup | force | No | |
tmpCasterZ | real | No | |
tmpDiffHeight | real | No | |
tmpDistance | real | No | |
tmpHeight | real | No | |
tmpSound | sound | No | |
tmpTargetZ | real | No | |
tmpTime | real | No | |
TPEffect | effect | Yes | |
TpUnit | unit | Yes | |
TR_caster | unit | No | |
TR_Dummy | unit | Yes | |
TR_Target | unit | No | |
Treant | unit | No | |
Treant2 | unit | No | |
TreantGroup | group | No | |
TreantGroup2 | group | No | |
TreantGroup3 | group | No | |
TreantGroup4 | group | Yes | |
TreantHerdPoint | location | No | |
TreantLevel | integer | No | |
TreantLevel2 | integer | No | |
TreantLifeGain | real | No | |
TreantSpawnPoint | location | No | |
TrebuchetGroup | group | No | |
TRPoint | location | Yes | |
TRTimer | timer | No | |
TurretCheckGroup | group | No | |
UC_Counter | integer | Yes | |
UC_Groups | group | Yes | |
UC_Inv | boolean | Yes | |
UC_SETTINGS_AreaOfEffect | real | Yes | |
UC_SETTINGS_Collosion | boolean | No | |
UC_SETTINGS_DestroyTrees_Dash | boolean | No | |
UC_SETTINGS_Invulnerable | boolean | No | |
UC_SETTINGS_Speed | real | No | |
UC_Target | unit | No | |
UC_TempPoint | location | Yes | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UGroup | group | No | |
UGroupFB | group | No | |
UM_Caster | unit | No | |
UM_DamageInteger | integer | No | |
UM_Dummy | unit | Yes | |
UM_IntegerLoops | integervar | No | |
UM_Point | location | Yes | |
UM_Real | real | No | |
UM_SpawnType | unitcode | Yes | |
UM_Timer | timer | No | |
UMGroup | group | Yes | |
UMovNext | integer | Yes | |
UMovPrev | integer | Yes | |
Unit1FB | unit | No | |
Unit2FB | unit | No | |
UNIT_MIN_LIFE | real | No | |
UnitDamageRegistered | boolean | Yes | |
UnitFB | unit | No | |
UnitGroup | group | No | |
UnitInAction | boolean | Yes | |
UnitInActionEvent | real | No | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes | |
UnitMovementInterval | real | No | |
UnitMoving | boolean | Yes | |
UnitMovingEvent | real | No | |
UnitMovingX | real | Yes | |
UnitMovingY | real | Yes | |
UnitTypeEvent | real | No | |
UnlimitedLives | button | No | |
UnlimitedLivetrue | boolean | No | |
UpgradeUnitPoint | location | No | |
VampireKillPoint | location | No | |
VenomBladePoint | location | No | |
VijayaBowGroup | group | No | |
VoidShieldGroup | group | No | |
VoidShieldMana | real | No | |
VoidShieldUnit | unit | Yes | |
VoodooAIPoint | location | No | |
VoodooAITimer | timer | No | |
VoodooGroup1 | group | No | |
VoodooGroup2 | group | No | |
VoodooGroup3 | group | No | |
VoodooGroup4 | group | No | |
VoodooHero | unit | Yes | |
VoodooPoint | location | No | |
VoodooPoint2 | location | No | |
VoodooRandom | real | Yes | |
VoodooRandomPoint | location | No | |
VoodooTimer | timer | No | |
VoodooTimer2 | timer | Yes | |
VoodooTrap | unit | No | |
VoodooTrapCaster | unit | No | |
VoodooTrapHeroIntegerCounter | integer | Yes | |
VoodooTrapHeroIntegerCounter2 | integer | Yes | |
VoodooTrapInteger | integer | No | |
VoodooTrapLoopIndex | integer | Yes | |
VoodooTrapNumber | integer | No | |
VoodooTrapPoint | location | No | |
VP_AcoPoint | location | No | |
VP_AcoPoint2 | location | No | |
VP_AIPoint | location | No | |
VP_Caster | unit | No | |
VP_Dummy | unit | No | |
VP_Effect | lightning | Yes | |
VP_Group | group | No | |
VP_Group2 | group | No | |
VP_Point | location | No | |
VP_Timer | timer | No | |
VP_Timer4 | timer | No | |
VPDetectGroup | group | No | |
VPRandomExplode | location | No | |
VPRandomExplode2 | location | No | |
VPRandomExplode3 | location | No | |
VPRandomExplode4 | location | No | |
VPtimer2 | timer | No | |
VPtimer3 | timer | No | |
VPtimerRepeat | timer | No | |
VR_Caster | unit | No | |
VR_Dummy | unit | Yes | |
VR_Target | unit | No | |
VRPoint | location | Yes | |
VRPoint2 | location | No | |
VRTimer | timer | No | |
WarlockScrollCap | integer | Yes | |
WarlockScrollCharge | integer | Yes | |
WarlockScrollItem | item | Yes | |
WarlockScrollKill | integer | Yes | |
WarlockScrollUnit | unit | Yes | |
WarsongDrumPoint | location | No | |
WarsongDrumUnitGroup | group | No | |
WatcherGiftCheck | group | No | |
WatcherGiftGroup | group | No | |
WatcherGiftPoint | location | No | |
WayGatePoint | location | Yes | |
WC_Group | group | No | |
WEAPON_TYPE_AM_CHOP | integer | No | |
WEAPON_TYPE_CH_SLICE | integer | No | |
WEAPON_TYPE_CL_SLICE | integer | No | |
WEAPON_TYPE_CM_SLICE | integer | No | |
WEAPON_TYPE_MH_BASH | integer | No | |
WEAPON_TYPE_MH_CHOP | integer | No | |
WEAPON_TYPE_MH_SLICE | integer | No | |
WEAPON_TYPE_MH_STAB | integer | No | |
WEAPON_TYPE_ML_CHOP | integer | No | |
WEAPON_TYPE_ML_SLICE | integer | No | |
WEAPON_TYPE_MM_BASH | integer | No | |
WEAPON_TYPE_MM_CHOP | integer | No | |
WEAPON_TYPE_MM_SLICE | integer | No | |
WEAPON_TYPE_MM_STAB | integer | No | |
WEAPON_TYPE_NONE | integer | No | |
WEAPON_TYPE_RH_BASH | integer | No | |
WEAPON_TYPE_WH_BASH | integer | No | |
WEAPON_TYPE_WH_SLICE | integer | No | |
WEAPON_TYPE_WL_BASH | integer | No | |
WEAPON_TYPE_WL_SLICE | integer | No | |
WEAPON_TYPE_WL_STAB | integer | No | |
WEAPON_TYPE_WM_BASH | integer | No | |
WEAPON_TYPE_WM_SLICE | integer | No | |
WEAPON_TYPE_WM_STAB | integer | No | |
WeaponTypeDebugStr | string | Yes | |
WindCycloneAOE | real | No | |
WindCycloneBoolean | boolean | Yes | |
WindCycloneCaster | unit | No | |
WindCycloneCritChance | integer | No | |
WindCycloneDamage | real | No | |
WindCycloneDummy1 | unit | No | |
WindCyclonePoint | location | No | |
WindCyclonePoint2 | location | No | |
WindCyclonePoint3 | location | No | |
WindCycloneSFX | effect | No | |
WindCycloneTimer | timer | No | |
WindCylconeDummy2 | unit | No | |
WindWalkCaster | unit | No | |
WindWalkDamageShow | real | No | |
WindWalkPoint | location | No | |
WindWalkTimer | timer | No | |
WispChoose_Center_Posi | location | No | |
WolfAttackDamageInt | integer | No | |
WorldMaxX | real | No | |
WorldMaxY | real | No | |
x2FB | integer | No | |
yFB | real | No |
//TESH.scrollpos=146
//TESH.alwaysfold=0
scope StoneSkin initializer Init
globals
// Configurables:
// Skills:
private constant integer Abil_id = 'A03T'
// Raw code of the main hero ability.
private constant integer Abil2_id = 'A03V'
// Raw code of the mini-stun unit ability.
private constant integer Dummy_id = 'h01J'
// Raw code of the dummy unit that casts war stomp.
private constant real MinDamage = 1.00
// Minimum damage that can trigger the spell.
private constant real MinHp = 25.00
// If the hero is under this hp, the spell will not trigger.
private constant string Absorb_sfx = "SandWaveDamage.mdx"
// Special effect created on the hero when triggered.
private constant string Absorb_point = "origin"
// Attachment point of the above effect.
private constant string DamageDirt_sfx = "SandWaveDamage.mdx"
// Special effect created on each damaged unit when they are on land.
private constant string DamageWater_sfx = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"
// Same as above but for when units are on shallow water.
private constant string Damage_point = "origin"
// Attachment point of the above effects.
private constant boolean AllowPreload = true
// Whether or not to allow preloading of the effects.
// Attack/Damage/Weapon types of damage dealt.
private constant attacktype A_type = ATTACK_TYPE_CHAOS
private constant damagetype D_type = DAMAGE_TYPE_UNIVERSAL
private constant weapontype W_type = WEAPON_TYPE_WHOKNOWS
// Do not touch these.
private boolexpr Truefilt = null
private boolexpr Enemyfilt = null
private group Group = null
private unit Dummy = null
endglobals
//=======================================================================
private function Radius takes integer lvl returns real
return 150.00+(50.00*lvl) // Area of effect in which units are damaged.
endfunction
private function AbsorbPercent takes integer lvl returns real
return 0.10+(0.15*lvl) // Percentage of damage absorbed.
endfunction
private function DamagePercent takes integer lvl returns real
return 0.60+(0.10*lvl) // Percentage of absorbed damage dealt to units.
endfunction
private function DamageCap takes real str,integer lvl returns real
return str * (0.7 + 0.2*lvl) // Damage cap of above damage.
endfunction
//=======================================================================//
// DO NOT TOUCH PAST THIS POINT UNLESS YOU KNOW WHAT YOUR ARE DOING!! //
//=======================================================================//
private function Chance takes unit u returns real
return (GetUnitState(u,UNIT_STATE_LIFE) / GetUnitState(u,UNIT_STATE_MAX_LIFE)) / 2.00
endfunction
private function EnemyFilt takes nothing returns boolean
return GetWidgetLife(GetFilterUnit()) > 0.405 and IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) == false
endfunction
//=======================================================================
private function Update takes nothing returns boolean
local unit cast = GetTriggerUnit()
local integer lvl = GetUnitAbilityLevel(cast,Abil_id)
local real str = GetHeroStr(cast, true)
local real dam = GetEventDamage()*AbsorbPercent(lvl)
local unit u = null
if dam > MinDamage and GetWidgetLife(cast) > MinHp then
if GetRandomReal(0.00,1.00) <= Chance(cast) then
call SetUnitAnimation(cast,"spell slam")
call SetWidgetLife(cast,GetWidgetLife(cast)+dam)
call DestroyEffect(AddSpecialEffectTarget(Absorb_sfx,cast,Absorb_point))
set dam = dam*DamagePercent(lvl)
if dam > DamageCap(str,lvl) then
set dam = DamageCap(str,lvl)
endif
set Dummy = CreateUnit(GetOwningPlayer(cast),Dummy_id,GetUnitX(cast),GetUnitY(cast),0.00)
call UnitAddAbility(Dummy,Abil2_id)
call SetUnitAbilityLevel(Dummy,Abil2_id,lvl)
call IssueImmediateOrder(Dummy,"fanofknives")
call RemoveUnit(Dummy)
set Dummy = null
call GroupEnumUnitsInRange(Group,GetUnitX(cast),GetUnitY(cast),Radius(lvl),Enemyfilt)
loop
set u = FirstOfGroup(Group)
exitwhen u == null
call GroupRemoveUnit(Group,u)
if IsUnitEnemy(u,GetOwningPlayer(cast)) then
call UnitDamageTarget(cast,u,dam,false,false,A_type,D_type,W_type)
if IsTerrainPathable(GetUnitX(u),GetUnitY(u),PATHING_TYPE_FLOATABILITY) then
call DestroyEffect(AddSpecialEffectTarget(DamageDirt_sfx,u,Damage_point))
elseif not IsTerrainPathable(GetUnitX(u),GetUnitY(u),PATHING_TYPE_WALKABILITY) then
call DestroyEffect(AddSpecialEffectTarget(DamageWater_sfx,u,Damage_point))
endif
endif
endloop
endif
endif
set cast = null
return false
endfunction
//=======================================================================
private function Actions takes nothing returns nothing
local unit cast = GetTriggerUnit()
local trigger trig
if GetLearnedSkill() == Abil_id and GetUnitAbilityLevel(cast,Abil_id) == 1 and IsUnitIllusion(cast) == false then
set trig = CreateTrigger()
call TriggerRegisterUnitEvent(trig,cast,EVENT_UNIT_DAMAGED)
call TriggerAddCondition(trig,Condition(function Update))
endif
set cast = null
set trig = null
endfunction
//=======================================================================
private function TrueFilt takes nothing returns boolean
return true
endfunction
//=======================================================================
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
local integer i = 0
local unit dum = null
set Group = CreateGroup()
set Truefilt = Filter(function TrueFilt)
set Enemyfilt = Filter(function EnemyFilt)
loop
call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_HERO_SKILL,Truefilt)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddAction(trig,function Actions)
if AllowPreload then
set dum = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),Dummy_id,0.00,0.00,0.00)
call UnitAddAbility(dum,Abil2_id)
call IssueImmediateOrder(dum,"fanofknives")
call UnitApplyTimedLife(dum,'BTLF',1.00)
call DestroyEffect(AddSpecialEffect(DamageDirt_sfx,0.00,0.00))
call DestroyEffect(AddSpecialEffect(DamageWater_sfx,0.00,0.00))
endif
set dum = null
endfunction
endscope
//TESH.scrollpos=147
//TESH.alwaysfold=0
scope SeismicSlam initializer Init
globals
// Configurables:
// Skills:
private constant integer Abil_id = 'A024'
// Raw code of the main hero ability.
private constant integer Abil2_id = 'A032'
// Raw code of the stomp used at the end.
private constant integer Flytrick = 'Amrf'
// Raw code for "Crow Form". Does not need to be changed in most cases.
// Units:
private constant integer Dummy_id = 'u00H'
// Raw code of the dummy "spinning" unit used for the leap.
private constant integer Treedummy_id = 'h01J'
// Raw code of the dummy unit used to destroy trees.
// Others:
private constant real Interval = 0.04 // Interval for periodic timer.
private constant real Diff = 0.00 // Distance from cast point that the unit will land.
private constant real Timescale = 3.00 // How fast the unit spins.
private constant string End_anim = "spell slam"
// End animation of the unit.
private constant string Dirt_sfx = "NewDirtEXNofire.mdx"
// Effect made at the end of the spell on land.
private constant string Water_sfx = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
// Same as above but for landing on water.
private constant boolean Destroytrees = true
// Whether or not to destroy trees.
private constant boolean AllowPreload = true
// Whether or not to allow preloading of the effects.
endglobals
//=======================================================================
private function Area takes integer lvl returns real
return 225.00 // Area of effect for destroying trees.
endfunction
private function Airtime takes integer lvl returns real
return 0.85 // Air time of the leap.
endfunction
//=======================================================================//
// DO NOT TOUCH PAST THIS POINT UNLESS YOU KNOW WHAT YOUR ARE DOING!! //
//=======================================================================//
private keyword Data
globals
private integer Total = 0
private Data array D
private timer Timer = null
private rect Rect1 = null
private boolexpr Treefilt = null
private boolexpr Truefilt = null
private unit Treedummy = null
private real Game_maxX = 0.00
private real Game_minX = 0.00
private real Game_maxY = 0.00
private real Game_minY = 0.00
endglobals
//=======================================================================
private function KillEnumDestructables takes nothing returns nothing
call KillDestructable(GetEnumDestructable())
endfunction
private function TreeCheck takes nothing returns boolean
local destructable dest = GetFilterDestructable()
local boolean result
if GetDestructableLife(dest) > 0.405 then
call ShowUnit(Treedummy,true)
call SetUnitX(Treedummy,GetWidgetX(dest))
call SetUnitY(Treedummy,GetWidgetY(dest))
set result = IssueTargetOrder(Treedummy,"harvest",dest)
call IssueImmediateOrder(Treedummy,"stop")
call ShowUnit(Treedummy,false)
return result
endif
set dest = null
return false
endfunction
//=======================================================================
private function SafeX takes real x returns real
if x<Game_minX then
return Game_minX
elseif x>Game_maxX then
return Game_maxX
endif
return x
endfunction
private function SafeY takes real y returns real
if y<Game_minY then
return Game_minY
elseif y>Game_maxY then
return Game_maxY
endif
return y
endfunction
//=======================================================================
private function MoveDist takes real Maxdist, integer lvl returns real
return Maxdist/(Airtime(lvl)/Interval)
endfunction
private function ConvertCount takes integer i, integer lvl returns real
return (50.00/(Airtime(lvl)/Interval))*i
endfunction
//=======================================================================
private struct Data
unit cast
unit dum
real castx
real casty
real cos
real sin
real maxdist
real movedist
integer i = 1
integer lvl
static method create takes nothing returns Data
local Data d = Data.allocate()
local location loc
local real angle
local real targx
local real targy
set d.cast = GetTriggerUnit()
set d.castx = GetUnitX(d.cast)
set d.casty = GetUnitY(d.cast)
set loc = GetSpellTargetLoc()
set angle = Atan2((GetLocationY(loc)-d.casty),(GetLocationX(loc)-d.castx))
set d.cos = Cos(angle)
set d.sin = Sin(angle)
set targx = GetLocationX(loc)-Diff*d.cos
set targy = GetLocationY(loc)-Diff*d.sin
set d.lvl = GetUnitAbilityLevel(d.cast,Abil_id)
set d.maxdist = SquareRoot((targx-d.castx)*(targx-d.castx)+(targy-d.casty)*(targy-d.casty))
set d.movedist = MoveDist(d.maxdist,d.lvl)
set d.dum = CreateUnit(GetOwningPlayer(d.cast),Dummy_id,d.castx,d.casty,angle*bj_RADTODEG)
call UnitAddAbility(d.dum,Flytrick)
call UnitRemoveAbility(d.dum,Flytrick)
call SetUnitTimeScale(d.dum,Timescale)
call SetUnitPathing(d.dum,false)
call PauseUnit(d.dum,true)
call ShowUnit(d.cast,false)
set Total = Total + 1
if Total == 1 then
call TimerStart(Timer,Interval,true,function Data.Update)
endif
set D[Total] = d
call RemoveLocation(loc)
set loc = null
return d
endmethod
static method Update takes nothing returns nothing
local integer i = 1
local real count
local real height
local real speed
local real newx
local real newy
loop
exitwhen i > Total
if D[i].i < (Airtime(D[i].lvl) / Interval) then
set count = ConvertCount(D[i].i,D[i].lvl)
set height = (count-25.00) * (count-25.00)
set speed = D[i].i * D[i].movedist
set newx = D[i].castx + speed * D[i].cos
set newy = D[i].casty + speed * D[i].sin
call SetUnitPosition(D[i].dum,SafeX(newx),SafeY(newy))
call SetUnitPosition(D[i].cast,SafeX(newx),SafeY(newy))
call SetUnitFlyHeight(D[i].dum,625.00-height,0.00)
call SetUnitFlyHeight(D[i].cast,625.00-height,0.00)
set D[i].i = D[i].i + 1
else
call D[i].destroy()
set D[i] = D[Total]
set Total = Total - 1
set i = i - 1
endif
set i = i + 1
endloop
if Total <= 0 then
call PauseTimer(Timer)
set Total = 0
endif
endmethod
private method onDestroy takes nothing returns nothing
local real newx = GetUnitX(.dum) + Diff * .cos
local real newy = GetUnitY(.dum) + Diff * .sin
local unit u = CreateUnit(GetOwningPlayer(.cast),Treedummy_id,newx,newy,0.00)
call SetUnitFlyHeight(.dum,GetUnitDefaultFlyHeight(.cast),0.00)
call PauseUnit(.dum,false)
call SetUnitPathing(.dum,true)
if Destroytrees then
call SetRect(Rect1,newx-Area(.lvl),newy-Area(.lvl),newx+Area(.lvl),newy+Area(.lvl))
call EnumDestructablesInRect(Rect1,Treefilt,function KillEnumDestructables)
endif
call UnitAddAbility(u,Abil2_id)
call IssueImmediateOrder(u,"fanofknives")
call UnitApplyTimedLife(u,'BTLF',1.00)
if IsTerrainPathable(GetUnitX(u),GetUnitY(u),PATHING_TYPE_FLOATABILITY) then
call DestroyEffect(AddSpecialEffect(Dirt_sfx,newx,newy))
elseif not IsTerrainPathable(GetUnitX(u),GetUnitY(u),PATHING_TYPE_WALKABILITY) then
call DestroyEffect(AddSpecialEffect(Water_sfx,newx,newy))
endif
call RemoveUnit(.dum)
call SetUnitPosition(.cast,newx,newy)
call ShowUnit(.cast,true)
call SetUnitAnimation(.cast,End_anim)
if GetLocalPlayer() == GetOwningPlayer(.cast) then
call ClearSelection()
call SelectUnit(.cast,true)
endif
set .cast = null
set .dum = null
set u = null
endmethod
endstruct
//=======================================================================
private function Actions takes nothing returns nothing
call Data.create()
endfunction
//=======================================================================
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == Abil_id
endfunction
//=======================================================================
private function TrueFilt takes nothing returns boolean
return true
endfunction
//=======================================================================
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
local integer i = 0
set Timer = CreateTimer()
set Rect1 = Rect(0.00,0.00,1.00,1.00)
set Treefilt = Filter(function TreeCheck)
set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea)-64.00
set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea)-64.00
set Game_minX = GetRectMinX(bj_mapInitialPlayableArea)+64.00
set Game_minY = GetRectMinY(bj_mapInitialPlayableArea)+64.00
set Truefilt = Filter(function TrueFilt)
loop
call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,Truefilt)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(trig,Condition(function Conditions))
call TriggerAddAction(trig,function Actions)
set Treedummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),Treedummy_id,0.00,0.00,0.00)
call SetUnitPathing(Treedummy,false)
call ShowUnit(Treedummy,false)
if AllowPreload then
call KillUnit(CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),Dummy_id,0.00,0.00,0.00))
call DestroyEffect(AddSpecialEffect(Dirt_sfx,0.00,0.00))
call DestroyEffect(AddSpecialEffect(Water_sfx,0.00,0.00))
endif
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope GatlingBot /*
Gatling Bot v3.7
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Created by: Quilnez
A. Description
¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Summons an automatic drone at the target location. The drone will
attack enemy units around with hail of gun firings.
B. How to import
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
1. Import these stuffs into your map:
- (Object Editor - Unit) Missile Dummy
- (Object Editor - Unit) Gatling Bot
- (Object Editor - Abilities) Summon Gatling Bot
- (Import Manager) dummy.mdx
- Other files are rather optional, you can use yours instead
2. Import Gatling Bot trigger into your map
3. Import and configure all required external dependencies properly
4. Configure the spell properly
5. Done!
C. External Dependencies
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- CTL @ github.com/nestharus/JASS/tree/master/jass/Systems/ConstantTimerLoop32
- Missile @ hiveworkshop.com/forums/jass-resources-412/system-missile-207854/
- RapidSound @ hiveworkshop.com/threads/snippet-rapidsound.258991/
- LockBone @ hiveworkshop.com/forums/submissions-414/snippet-lockbone-259005/
D. Credits
¯¯¯¯¯¯¯¯¯¯
Author | Contribution(s)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Nestharus | CTL
BPower | Missile
Muoteck | BTNSpiderbot
Vexorian | dummy.mdx
Gottfrei | LT-1.mdx
Grey Knight | Bullet.mdx
*/
private keyword SFX
/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
______________________________
General Settings
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ */
globals
// Main spell's raw code
private constant integer SPELL_ID = 'A00Q'
// Drone unit's raw code
private constant integer DRONE_ID = 'h04F'
// Dummy unit's raw code (only useful if you don't use MissileRecycler)
private constant integer DUMMY_ID = 'e00E'
// Range to indicate that the master is moving
private constant real MOVE_RANGE_INDICATOR = 64.0
// Drone model's bone that will be rotated around
private constant string LOCKED_BONE = BONE_PART_CHEST
// This value will make sure the drone will
// face straight forward
private constant real LOCKED_BONE_ZOFFSET = 100.0
// If true, the accuracy's inconsistency will be calculated only for once
// Will be noticeable when the drone fires more than one missile at once
private constant boolean ACCURACY_MODE = false
// "move" order id
private constant integer MOVE_ORDER = 851986
// Drone's animation that going to be played
// when it attacks
private constant string ATTACK_ANIMATION = "attack"
// If true all damages by drone will be considered to come
// from it's master instead
private constant boolean CASTER_DAMAGE_SOURCE = true
// If false, the missile will only travel from drone's
// position to it's attack target, ignoring it's optimum
// attack range
private constant boolean STATIC_ATTACK_RANGE = true
// Attack & damage type of dealt damage
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_PIERCE
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
// Time for special effects to complete their animations
private constant real SFX_DECAY_TIME = 3.0
// Played when the drone attacks
private constant string FIRE_SOUND_PATH = "war3mapImported\\fire.wav"
private constant integer FIRE_SOUND_VOLUME = 200
// Missile's detail
private constant string MISSILE_MODEL_PATH = "war3mapImported\\Bullet.mdx"
private constant real MISSILE_LAUNCH_Z = 40.0
private constant real MISSILE_IMPACT_Z = 40.0
private constant real MISSILE_COLLISION_SIZE = 45.0
endglobals
private module DroneInitialization
/* ______________________________
Drone's attribute data
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Notes:
- This block will execute every time a drone
is created
- Use "level" to refer to drone's level
- Use ".unit" to refer to the drone unit */
// 1. Damage amount
// Drone's damage is increased by 6 pts every 5 levels
set .dmgTop = 16.0 + 7.0*I2R(level) + 0.2*.intel
set .dmgLow = 12.0 + 7.0*I2R(level) + 0.2*.intel
// 2. Piercing attack
// If true, drone's missiles will become piercing
// If ability's level is equal or more than 15, drone's attacks
// will become piercing
set .setPierce = level >= 2
// 3. Impact damage
// Area damage dealt when drone's missile is destroyed
if level < 3 then
set .impactDmg = 0.0
else
set .impactDmg = GetRandomReal(.dmgLow, .dmgTop)
endif
// 4. Impact damage AoE
// AoE range of impact damage
// Impact events will only fire if this AoE > 0
if level < 3 then
set .impactAoE = 0.0
else
set .impactAoE = 200.0
endif
// 5. Missile count
// Number of missile launched at once
// If ability's level is less than 10, drone fires 1 missile
// at once. Else it fires two missiles at once.
if level < 2 then
set .mslCount = 1
else
set .mslCount = 2
endif
// 6. Missile spacing
// Distance between missile. Works if only mslCount is more than 1
set .mslSpace = 30
// 7.Accuracy
// Drone's firing accuracy. Higher value means less accurate
// it would be
set .accuracy = 45.0*bj_DEGTORAD
// 8. Drone's head turn rate
// Drone's head can turn 4 degrees per tick (0.03125 s)
set .turnRate = 4.0*bj_DEGTORAD
// 9. Acquisition range
// Drone can acquire targets within 800 range (level 1-4)
// and 1000 range on higher levels
if level < 5 then
set .acqRange = 800.0
else
set .acqRange = 1000.0
endif
// 10. Attack range
// If ability's level is less than 5, drone's attack range
// is 600. Else it's 800.
if level < 3 then
set .atkRange = 600.0
else
set .atkRange = 800.0
endif
// 11. Attack rate
// Drone will attack every 0.075s
set .atkSpeed = 0.075
// 12. Follow interval
// Drone will follow its master every 0.5s
set .fllwRate = 0.5
// 13. Wander area
// How far the drone is allowed to wander around
// it's master
set .wanderArea = 300.0
// 14. Wander interval
// Drone will wander around every 4s
set .wndrRate = 4.0
// 15. Cautious (self secure)
// If true, the drone will keep some distances from it's target
set .cautious = true
/* ______________________________
Missile's data
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ */
// 1. Missile move rate
// Missiles can move 50 range away per tick
set .mslSpeed = 50.0
// 2. Missile acceleration
// May causes missiles to move faster over times
set .mslAccel = 0.0
// 3. Missile homing state
// If true, the missile will become homing
set .setHoming = false
// 4. Missile turn rate (in radians)
// How agile can the missile turn around (if setHoming=true)
set .mslTrRate = 0.0
// 5. Missile curve in radians (the absolute value must be below HP/2)
set .mslCurve = 0.0
// 6. Missile arc in radians (the absolute value must be below HP/2)
set .mslArc = 0.0
/* ______________________________
Additional Config.
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ */
// Drone has 25 additional health points every level
call SetUnitState(.unit, UNIT_STATE_LIFE, 350 + 150*level)
// Drone lasts for 30 seconds
call UnitApplyTimedLife(.unit, 'BTLF', 30)
endmodule
// p = owner of the drone, t = damage target, level = drone level
// By default drone will only attack visible alive units
private function droneTargets takes player p, unit t, integer level returns boolean
return IsUnitEnemy(t, p) and not IsUnitType(t, UNIT_TYPE_MECHANICAL)
endfunction
/* ______________________________
Event Catcher
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ */
private module EventCatcher
// You can use these parameters to manipulate things:
// - "dex" refers to the triggering missile instance ..
// .. read Missile lib's API for more detail
// - "source" refers to the source of the damage
// - "target" refers to the target of the damage
// - "level" refers to the drone's level
// Fires when a unit receive damage from missiles
static method onMissileHit takes Missile dex, unit source, unit target, integer level returns nothing
// Bonus API to create effects with detailed data
// call SFX.onPoint(modelPath, facing, scale, x, y, z, duration)
call SFX.onPoint("war3mapImported\\bullet hit blood.mdx", dex.angle*bj_RADTODEG+180, 1, dex.x, dex.y, dex.z, 3)
endmethod
// Fires when a missile impacts
static method onImpact takes Missile dex, integer level returns nothing
call SFX.onPoint("Abilities\\Weapons\\Rifle\\RifleImpact.mdl", dex.angle*bj_RADTODEG, 1, dex.x, dex.y, dex.z, 1)
endmethod
// Fires when a unit takes damage from impact
static method onImpactHit takes Missile dex, unit source, unit target, integer level returns nothing
endmethod
endmodule
/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
globals
private constant real INTERVAL = 0.031250000
endglobals
native UnitAlive takes unit id returns boolean
private keyword GatlingBot
private struct ResetDummy
unit dummy
real dur
static real MapEdgeX
static real MapEdgeY
implement CTLExpire
set .dur = .dur - INTERVAL
if .dur <= 0 then
call SetUnitX(.dummy, MapEdgeX)
call SetUnitY(.dummy, MapEdgeY)
call SetUnitScale(.dummy, 1, 1, 1)
call destroy()
set .dummy = null
endif
implement CTLEnd
static method reset takes unit whichUnit returns nothing
local thistype this = create()
set .dummy = whichUnit
set .dur = SFX_DECAY_TIME
endmethod
static method onInit takes nothing returns nothing
set MapEdgeX = GetRectMaxX(bj_mapInitialPlayableArea)
set MapEdgeY = GetRectMaxY(bj_mapInitialPlayableArea)
endmethod
endstruct
private struct SFX
static constant player PASSIVE = Player(PLAYER_NEUTRAL_PASSIVE)
static method onPoint takes string model, real angle, real scale, real x, real y, real z, real dur returns nothing
local unit u
static if LIBRARY_MissileRecycler then
set u = GetRecycledMissile(x, y, z, angle)
else
set u = CreateUnit(PASSIVE, DUMMY_ID, x, y, angle)
static if not LIBRARY_AutoFly then
if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
endif
endif
call SetUnitFlyHeight(u, z, 0)
endif
static if LIBRARY_MissileRecycler then
call RecycleMissile(u)
call ResetDummy.reset(u)
else
call UnitApplyTimedLife(u, 'BTLF', dur)
endif
call SetUnitScale(u, scale, 1, 1)
call DestroyEffect(AddSpecialEffectTarget(model, u, "origin"))
set u = null
endmethod
endstruct
private struct Bullet
real distance
Missile missile
GatlingBot index
static group Group = CreateGroup()
implement EventCatcher
static method onRemove takes Missile dex returns boolean
local thistype this = dex.data
local GatlingBot dex2 = .index
local unit u
if dex2.impactAoE > 0 then
call thistype.onImpact(dex, dex2.level)
// Deal impact damage
call GroupEnumUnitsInRange(Group, dex.x, dex.y, dex2.impactAoE, null)
loop
set u = FirstOfGroup(Group)
exitwhen u == null
call GroupRemoveUnit(Group, u)
if u == dex2.target or (droneTargets(dex2.owner, u, dex2.level) and UnitAlive(u) and IsUnitVisible(u, dex2.owner)) then
call thistype.onImpactHit(dex, dex2.unit, u, dex2.level)
call UnitDamageTarget(dex2.unit, u, dex2.impactDmg, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
endloop
endif
call ResetDummy.reset(dex.dummy)
call .destroy()
return true
endmethod
static method onPeriod takes Missile dex returns boolean
local thistype this = dex.data
local GatlingBot dex2 = .index
// If the missile doesn't collide with ground
if GetUnitFlyHeight(dex.dummy) > .01 then
set .distance = .distance+dex.speed
return (dex2.setHoming or not STATIC_ATTACK_RANGE) and .distance > dex2.atkRange
else
return true
endif
endmethod
static method onCollide takes Missile dex, unit justHit returns boolean
local thistype this = dex.data
local GatlingBot dex2 = .index
local real d
if justHit == dex2.target or (droneTargets(dex2.owner, justHit, dex2.level) and UnitAlive(justHit) and IsUnitVisible(justHit, dex2.owner)) then
set d = GetRandomReal(dex2.dmgLow, dex2.dmgTop)
call thistype.onMissileHit(dex, dex2.unit, justHit, dex2.level)
call UnitDamageTarget(dex2.unit, justHit, d, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
call dex.hitWidget(justHit)
// Instantly destroy missile if not piercing
return not dex2.setPierce
else
return false
endif
endmethod
implement MissileStruct
static method fire takes GatlingBot dex returns nothing
local thistype this
local integer i = 0
local real a = dex.angle-bj_PI/2
local real cos = Cos(a)
local real sin = Sin(a)
local real d = (dex.mslSpace*dex.mslCount)/2
local real x = GetUnitX(dex.unit)-d*cos
local real y = GetUnitY(dex.unit)-d*sin
local real droneZ = GetUnitFlyHeight(dex.unit)
local real targetX
local real targetY
local real targetZ
local real acc
local real ar
// If true, the inconsistency will only be calculated once
static if ACCURACY_MODE then
set acc = GetRandomReal(-dex.accuracy,dex.accuracy)/2
endif
set targetZ = GetUnitFlyHeight(dex.target)
if STATIC_ATTACK_RANGE then
set ar = dex.atkRange
else
set targetX = GetUnitX(dex.target)
set targetY = GetUnitY(dex.target)
set ar = SquareRoot((targetX-x)*(targetX-x)+(targetY-y)*(targetY-y))
endif
// Launch missiles
loop
exitwhen i >= dex.mslCount
set this = allocate()
set .distance = 0
static if not ACCURACY_MODE then
set acc = GetRandomReal(-dex.accuracy,dex.accuracy)/2
endif
set .index = dex
set .missile = Missile.create(x, y, droneZ+MISSILE_LAUNCH_Z, dex.angle+acc, ar, MISSILE_IMPACT_Z+targetZ)
set .missile.curve = dex.mslCurve
set .missile.source = dex.unit
set .missile.speed = dex.mslSpeed
set .missile.model = MISSILE_MODEL_PATH
set .missile.arc = dex.mslArc
set .missile.data = this
set .missile.acceleration = dex.mslAccel
set .missile.collision = MISSILE_COLLISION_SIZE
if dex.setHoming then
set .missile.target = dex.target
set .missile.turn = dex.mslTrRate
endif
call launch(.missile)
// Calculate new offset
set x = x+dex.mslSpace*cos
set y = y+dex.mslSpace*sin
set i = i+1
endloop
endmethod
endstruct
private struct GatlingBot extends array
real dmgLow
real dmgTop
real accuracy
real acqRange
real atkRange
real atkSpeed
real fllwRate
real wndrRate
real turnRate
real impactAoE
real impactDmg
real mslAccel
real mslSpace
real mslSpeed
real mslTrRate
real mslCurve
real mslArc
integer mslCount
boolean cautious
boolean setPierce
boolean setHoming
integer level
integer intel
player owner
unit unit
unit target
unit master
real angle
real masterLocX
real masterLocY
real moveDelay
real attackDelay
real wanderArea
RSound sound
static group TempGroup = CreateGroup()
static unit TempUnit
method getClosestTarget takes real x, real y returns unit
local unit fog
local real closestDist = 0
local real x2
local real y2
local real d
set TempUnit = null
call GroupEnumUnitsInRange(TempGroup, x, y, .acqRange, null)
loop
set fog = FirstOfGroup(TempGroup)
exitwhen fog == null
call GroupRemoveUnit(TempGroup, fog)
if droneTargets(.owner, fog, .level) and UnitAlive(fog) and IsUnitVisible(fog, .owner) then
set x2 = GetUnitX(fog)
set y2 = GetUnitY(fog)
set d = (x2-x)*(x2-x)+(y2-y)*(y2-y)
if TempUnit == null or d<closestDist then
set closestDist = d
set TempUnit = fog
endif
endif
endloop
return TempUnit
endmethod
implement CTL
local real a
local real d
local real r
local real droneX
local real droneY
local real targetX
local real targetY
local real masterX
local real masterY
local unit nt
local boolean b
implement CTLExpire
if UnitAlive(.unit) then
set droneX = GetUnitX(.unit)
set droneY = GetUnitY(.unit)
// If doesn't have target, attempt to obtain one
if .target == null then
set .target = getClosestTarget(droneX, droneY)
set a = GetUnitFacing(.unit)*bj_DEGTORAD
else
// If the target is still valid
if UnitAlive(.target) and IsUnitVisible(.target, .owner) then
set targetX = GetUnitX(.target)
set targetY = GetUnitY(.target)
set r = (droneX-targetX)*(droneX-targetX)+(droneY-targetY)*(droneY-targetY)
// If target is too far
if r > .acqRange*.acqRange then
set .target = null
// If target is beyond the max attack range
elseif r > .atkRange*.atkRange then
// Search for new closer target
set nt = getClosestTarget(droneX, droneY)
if nt != null then
// Switch to the new target
set .target = nt
set targetX = GetUnitX(.target)
set targetY = GetUnitY(.target)
set nt = null
endif
// If it's time to move, then allow drone to come closer to target
if .moveDelay > .fllwRate then
set .moveDelay = 0
if .master != null then
set a = Atan2(GetUnitY(.master)-targetY, GetUnitX(.master)-targetX)+GetRandomReal(-bj_PI, bj_PI)/4
else
set a = Atan2(droneY-targetY, droneX-targetX)+GetRandomReal(-bj_PI, bj_PI)/4
endif
call IssuePointOrderById(.unit, MOVE_ORDER, targetX+.atkRange*Cos(a), targetY+.atkRange*Sin(a))
endif
else
if .cautious and .moveDelay > .fllwRate then
set .moveDelay = 0
if .master != null then
set a = Atan2(GetUnitY(.master)-targetY, GetUnitX(.master)-targetX)+GetRandomReal(-bj_PI, bj_PI)/4
else
set a = Atan2(droneY-targetY, droneX-targetX)+GetRandomReal(-bj_PI, bj_PI)/4
endif
call IssuePointOrderById(.unit, MOVE_ORDER, targetX+.atkRange*Cos(a), targetY+.atkRange*Sin(a))
endif
// If ready to fire (attack)
if .attackDelay > .atkSpeed then
set .attackDelay = 0
call Bullet.fire(this)
call SetUnitAnimation(.unit, ATTACK_ANIMATION)
if .sound != 0 then
call .sound.play(droneX, droneY, MISSILE_LAUNCH_Z, FIRE_SOUND_VOLUME)
endif
else
set .attackDelay = .attackDelay+INTERVAL
endif
endif
set a = Atan2(targetY-droneY, targetX-droneX)
else
if .target != null then
set .moveDelay = .wndrRate-.fllwRate
set .target = null
endif
set a = GetUnitFacing(.unit)*bj_DEGTORAD
endif
endif
set b = false
// Update the bone rotation angle
if .turnRate > 0 and Cos(.angle-a) < Cos(.turnRate) then
if Sin(a-.angle) >= 0 then
set .angle = .angle+.turnRate
else
set .angle = .angle-.turnRate
endif
set b = true
elseif .angle != a then
set .angle = a
set b = true
endif
if b then
// If drone doesn't have target yet
if .target == null then
set d = LOCKED_BONE_ZOFFSET+GetUnitFlyHeight(.unit)
call LockBone.lockAtAngle(.unit, LOCKED_BONE, .angle, d, d)
else
set d = SquareRoot((targetX-droneX)*(targetX-droneX)+(targetY-droneY)*(targetY-droneY))
call LockBone.lockAtAngle(.unit, LOCKED_BONE, .angle, d, LOCKED_BONE_ZOFFSET+GetUnitFlyHeight(.target))
endif
endif
if UnitAlive(.master) then
set masterX = GetUnitX(.master)
set masterY = GetUnitY(.master)
// If master is moving
if (.masterLocX-masterX)*(.masterLocX-masterX)+(.masterLocY-masterY)*(.masterLocY-masterY) > MOVE_RANGE_INDICATOR*MOVE_RANGE_INDICATOR then
if .moveDelay > .fllwRate then
set .moveDelay = 0
set a = (GetUnitFacing(.master)*bj_DEGTORAD+bj_PI)+GetRandomReal(-bj_PI, bj_PI)/2
set d = GetRandomReal(0, .wanderArea)
call IssuePointOrderById(.unit, MOVE_ORDER, .masterLocX+d*Cos(a), .masterLocY+d*Sin(a))
endif
set .masterLocX = masterX
set .masterLocY = masterY
elseif .moveDelay > .wndrRate then
set .moveDelay = 0
set a = GetRandomReal(-bj_PI, bj_PI)
set d = GetRandomReal(0, .wanderArea)
call IssuePointOrderById(.unit, MOVE_ORDER, .masterLocX+d*Cos(a), .masterLocY+d*Sin(a))
endif
set .moveDelay = .moveDelay+INTERVAL
endif
else
if .sound != 0 then
call .sound.kill()
set .sound = 0
endif
call destroy()
set .unit = null
set .target = null
set .master = null
endif
implement CTLEnd
static method onCast takes nothing returns boolean
local thistype this
if GetSpellAbilityId() == SPELL_ID then
set this = create()
set .owner = GetTriggerPlayer()
set .angle = GetRandomReal(-bj_PI, bj_PI)
set .sound = RSound.create(FIRE_SOUND_PATH, true, false, 12700, 12700)
set .unit = CreateUnit(.owner, DRONE_ID, GetSpellTargetX(), GetSpellTargetY(), .angle*bj_RADTODEG)
set .level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
set .intel = GetHeroInt(GetTriggerUnit(), true)
set .target = null
set .attackDelay = 999999
set .master = GetTriggerUnit()
set .masterLocX = GetUnitX(.master)
set .masterLocY = GetUnitY(.master)
set .moveDelay = 0.0
implement DroneInitialization
endif
return false
endmethod
static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.onCast))
endmethod
endstruct
endscope
#### #### #### #### #### #### #### #### ##### # #### # # ####
# # # # # # # # # # # # # # # # ## # #
#### ## #### #### # #### ## #### # # # # # ## ####
# # # # # # # # # # # # # # # # # # #
#### #### # # #### #### # # #### # # # # #### # # ####
Frost Wrath v1.0
Created by -BerZekeR- for challenge between me and calex3
Credits to:
JetFangInferno for FreezingRing model
Me as a creator of this spell and as a creator of the icon
Special thanks to:
The Hive Workshop, for being an awesome web site and for hosting this challenge
calex3, for bringing up the challenge
Paladon, for being a judge
How to import:
Copy all custom object editor data (spells, dummies)
Copy Imported material (it's up to you if you'll use it or not)
Copy first two actions from this initialization trigger to your initialization trigger
Copy main spell trigger
Copy second spell trigger
You will need to reconect some lines (nothing hard, just click on "non functioning" action and hit ok)
Give base spell to one of your heroes
Test the spell
If some special effect doesn't apear ingame, then go back to trigger editor and search for the part
where special effect dummies are created and if there will be actions like " create 1 _ for FW Owner..."
Then click on that blank space [ _ ] and search for dummy special effect unit.
If it's still not working, then contact me on Hive:D
Have fun!
~Berz
library PointDefense requires MouseUtils, RemoveEffect, LocalZ
//=============================================================================================================================
// PointDefense v1.2
// by Pyrogasm
//
// Spawn defense drones that encircle the caster. Drones launch themselves to impact at the mouse cursor location periodically.
// Each drone can travel a maximum distance, and those closest to the target are launched first.
//
// Spell behavior is highly configurable, but by default:
// Enemies in the impact radius take damage per drone and are briefly blinded, causing them to miss most of their attacks.
//
// Dependencies:
// - MouseUtils by MyPad: https://www.hiveworkshop.com/threads/snippet-mouse-utility.310523/
// - RemoveEffect by Pyrogasm: https://www.hiveworkshop.com/threads/small-code-snippets.40758/page-45#post-3361522
// - LocalZ - any library that contains a function to get the Z height of X/Y coordinates
//=============================================================================================================================
globals
public constant integer SPELL_ID = 'Pdfs'
public constant integer DUMMY_ID = 'dmmy'
private constant real TIMER_TICK = 0.03
private constant real MAX_COLLISION = 128.00 //this value is added to GroupEnum calls before checking with IsUnitInRangeXY() so collision is properly accounted for
//set this to the max collision size a unit has in your map
private constant integer MAX_DEFENDERS = 24 //used in array member sizing
private constant real ORBIT_RADIUS = 170.
private constant real ORBIT_PERIOD = 10. //this is only used in the line below
private constant real ORBIT_ANGLEINC = 2*bj_PI/ORBIT_PERIOD*TIMER_TICK
private constant real ORBIT_DELAY = 0.51
private constant real FIRE_PERIOD = 0.24
private constant real FIRE_IMPACT_H = 0.
private constant real FIRE_MAX_DIST = 1000.
private constant real FIRE_MAX_VEL = 1500.
private constant real FIRE_START_VEL = 0.
private constant real FIRE_ACCEL = 3000.
//For all effect variables:
//1A = drone pre-launch
//1B = drone post-launch
//2A = impact fx
private constant real EFFECT_H = 150.
private constant string EFFECT_1A = "Abilities\\Weapons\\GyroCopter\\GyroCopterMissile.mdl"
private constant string EFFECT_1B = "Abilities\\Weapons\\GyroCopter\\GyroCopterMissile.mdl"
private constant string EFFECT_2A = "Objects\\Spawnmodels\\Human\\FragmentationShards\\FragBoomSpawn.mdl" // Human\\ThunderClap\\ThunderClapCaster.mdl"
private constant real E1A_SCALE = 0.5
private constant real E1A_TSCALE = 1. //timescale, aka animation speed
private constant real E1B_SCALE = 1.
private constant real E1B_TSCALE = 1.
private constant real E2A_SCALE = 0.6
private constant real E2A_TSCALE = 2.
private constant integer E1A_COL_R = 255
private constant integer E1A_COL_G = 255
private constant integer E1A_COL_B = 255
private constant integer E1B_COL_R = 255
private constant integer E1B_COL_G = 70
private constant integer E1B_COL_B = 70
private constant integer E2A_COL_R = 255
private constant integer E2A_COL_G = 0
private constant integer E2A_COL_B = 100
endglobals
globals
public keyword SpellData
private keyword defender
private SpellData array GSD
private integer COUNT = 0
private integer CURR = 0
public timer SpellTimer = CreateTimer()
public trigger CastTrigger = CreateTrigger()
endglobals
//Need this for checks in the spellBehavior module
native UnitAlive takes unit u returns boolean
public module spellBehavior
// the following variables are defined in the struct below for you to use
// they do not need to be destroyed or removed, that is handled already:
//
// unit DUMMY <-- can cast instantly and is moved to (.cx, .cy) every timer step
// add abilities to it with .prepDummy(abilCode) (level is set automatically)
// group HIT_GROUP <-- units already hit by this instance of the spell, you are free to clear this as necessary, but you must add units manually
// static group SEARCH_GROUP <-- a group you can use to search for targets via GroupEnumUnitsIn...
// integer l <-- level of the spell when it was cast
// unit c <-- caster
// player p <-- owner of caster
//
// look at the SpellData struct below for additional members if you want more information
// you may end an insance early by calling .endInstance()
// you may add your own instance or static members here:
static integer SECONDARY_ID = 'A000'
static string SECONDARY_ORDER = "thunderbolt"
static real RADIUS = 100.
static real DMG = 20.
method numDefenders takes nothing returns integer
//called on spell cast, determines the number of defenders to create
return GetRandomInt(5,15)
endmethod
static method onInitEx takes nothing returns nothing
//called on map init, so you can set up anything you need to here (most likely arrays or some global dummies)
endmethod
method onStart takes nothing returns nothing
//called when the spell is cast
call this.prepDummy(SECONDARY_ID)
endmethod
method onPeriodic takes nothing returns nothing
//called periodically while spell is running
endmethod
method onLaunch takes defender def, real tx, real ty, real th returns nothing
//called when defender def is launched
//can use members def.x, def.y, def.z for current location (def.h for height) and the tx, ty, th parameters for targeted location
endmethod
method onImpact takes defender def returns nothing
//can use members def.x, def.y, def.z for current location
local unit u
local effect e
set e = AddSpecialEffect(EFFECT_2A, def.x, def.y)
call BlzSetSpecialEffectColor(e, E2A_COL_R, E2A_COL_G, E2A_COL_B)
call BlzSetSpecialEffectScale(e, E2A_SCALE)
call BlzSetSpecialEffectTimeScale(e, E2A_TSCALE)
call DestroyEffect(e)
call GroupEnumUnitsInRange(SEARCH_GROUP, def.x, def.y, RADIUS+MAX_COLLISION, null)
loop
set u = FirstOfGroup(SEARCH_GROUP)
exitwhen u == null
call GroupRemoveUnit(SEARCH_GROUP, u)
if IsUnitInRangeXY(u, def.x, def.y, RADIUS) and /*
*/ IsUnitEnemy(u, this.p) and UnitAlive(u) and /*
*/ not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ not BlzIsUnitInvulnerable(u) then
call UnitDamageTarget(this.c, u, DMG, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS)
call IssueTargetOrder(this.DUMMY, SECONDARY_ORDER, u)
endif
endloop
set u = null
endmethod
method onEnd takes nothing returns nothing
//called when the spell ends
endmethod
endmodule
private struct defender
SpellData sd = 0
boolean f = false
boolean i = false
effect e = null
integer n = 0
integer d = 0
real x = 0.
real y = 0.
real z = 0.
real h = 0.
real v = 0
real cosA = 0.
real sinA = 0.
real dz = 0.
method move takes nothing returns nothing
if not this.f then
set this.x = this.sd.cx + this.sd.orbitR*Cos(this.sd.defAng*this.n+this.sd.defTheta)
set this.y = this.sd.cy + this.sd.orbitR*Sin(this.sd.defAng*this.n+this.sd.defTheta)
set this.z = BlzGetUnitZ(this.sd.c)+this.h
else
set this.x = this.x + this.v*this.cosA*TIMER_TICK
set this.y = this.y + this.v*this.sinA*TIMER_TICK
set this.h = this.h - this.dz
set this.z = GetLocalZ(this.x, this.y) + this.h
set this.v = this.v + FIRE_ACCEL*TIMER_TICK
if this.v > FIRE_MAX_VEL then
set this.v = FIRE_MAX_VEL
endif
set this.d = this.d - 1
endif
call BlzSetSpecialEffectPosition(this.e, this.x, this.y, this.z)
if this.d <= 0 then
call this.sd.onImpact(this)
call DestroyEffect(this.e)
set this.sd.d = this.sd.d - 1
set this.i = true
endif
endmethod
method fireAt takes real x, real y, real h returns nothing
local real a = Atan2(y-this.y, x-this.x)
local real t1
local real t2
local real qa
local real qb
local real qc
local real d
call RemoveEffect(this.e)
set this.e = AddSpecialEffect(EFFECT_1B, this.x, this.y)
call BlzSetSpecialEffectZ(this.e, this.z)
call BlzSetSpecialEffectScale(this.e, E1B_SCALE)
call BlzSetSpecialEffectTimeScale(this.e, E1B_TSCALE)
call BlzSetSpecialEffectColor(this.e, E1B_COL_R, E1B_COL_G, E1B_COL_B)
call this.sd.onLaunch(this, x, y, h)
set this.f = true
set this.cosA = Cos(a)
set this.sinA = Sin(a)
set this.v = FIRE_START_VEL
set d = SquareRoot((x-this.x)*(x-this.x) + (y-this.y)*(y-this.y))
set qa = FIRE_ACCEL/2.
set qb = FIRE_START_VEL
set qc = -1.*d
set t1 = (-1.*qb + SquareRoot(qb*qb - 4.*qa*qc))/(2.*qa)
set t2 = 0.
if this.v+FIRE_ACCEL*t1 > FIRE_MAX_VEL then
set t1 = (FIRE_MAX_VEL-this.v)/FIRE_ACCEL
set t2 = (d - (FIRE_MAX_VEL-this.v)/2.*t1)/FIRE_MAX_VEL
endif
set this.d = R2I((t1+t2)/TIMER_TICK)
set this.dz = ((this.h - h)/this.d)
endmethod
static method create takes SpellData sd, integer n returns thistype
local thistype def = thistype.allocate()
set def.sd = sd
set def.n = n
set def.f = false
set def.i = false
set def.x = sd.cx + sd.orbitR*Cos(sd.defAng*n+sd.defTheta)
set def.y = sd.cy + sd.orbitR*Sin(sd.defAng*n+sd.defTheta)
set def.h = EFFECT_H
set def.z = BlzGetUnitZ(sd.c)+def.h
set def.d = 1 //Simplest solution I guess...
set def.e = AddSpecialEffect(EFFECT_1A, def.x, def.y)
call BlzSetSpecialEffectZ(def.e, def.z)
call BlzSetSpecialEffectScale(def.e, E1A_SCALE)
call BlzSetSpecialEffectTimeScale(def.e, E1A_TSCALE)
call BlzSetSpecialEffectColor(def.e, E1A_COL_R, E1A_COL_G, E1A_COL_B)
return def
endmethod
endstruct
public struct SpellData
static group SEARCH_GROUP = CreateGroup()
group HIT_GROUP = CreateGroup()
unit DUMMY = null
unit c = null
player p = null
integer l = 0
integer d = 0
integer fire = 0
real cx = 0.
real cy = 0.
defender array defs[MAX_DEFENDERS]
integer defCount = 0
real defAng = 0.
real defTheta = 0.
integer phase = 0
real orbitA = 0.
real orbitV = 0.
real orbitR = 0.
method prepDummy takes integer abil returns nothing
call UnitAddAbility(this.DUMMY, abil)
call SetUnitAbilityLevel(this.DUMMY, abil, this.l)
endmethod
method endInstance takes nothing returns nothing
local integer i = 0
call this.onEnd()
call RemoveUnit(this.DUMMY)
call DestroyGroup(this.HIT_GROUP)
loop
call this.defs[i].destroy()
set i = i+1
exitwhen i >= this.defCount
endloop
set GSD[CURR] = GSD[COUNT-1]
set COUNT = COUNT-1
set CURR = CURR-1
if COUNT == 0 then
call PauseTimer(SpellTimer)
endif
call this.destroy()
endmethod
implement spellBehavior
method fireDefender takes real x, real y, real z returns nothing
local integer i = 0
local real dx
local real dy
local real d2
local integer closest = -1
local real d2Closest = (FIRE_MAX_DIST+ORBIT_RADIUS)*(FIRE_MAX_DIST+ORBIT_RADIUS)
loop
if not this.defs[i].f then
set dx = this.defs[i].x - x
set dy = this.defs[i].y - y
set d2 = dx*dx + dy*dy
if d2 <= d2Closest then
set closest = i
set d2Closest = d2
endif
endif
set i = i+1
exitwhen i >= this.defCount
endloop
if closest >= 0 then
call this.defs[closest].fireAt(x, y, z)
endif
endmethod
method updateDefenders takes nothing returns nothing
local integer i = 0
loop
if not this.defs[i].i then
call this.defs[i].move()
endif
set i = i+1
exitwhen i >= this.defCount
endloop
endmethod
static method periodic takes nothing returns nothing
local SpellData sd
local integer i
local real mx
local real my
local real a
set CURR = 0
loop
exitwhen CURR >= COUNT
set sd = GSD[CURR]
set sd.cx = GetUnitX(sd.c)
set sd.cy = GetUnitY(sd.c)
call SetUnitX(sd.DUMMY, sd.cx)
call SetUnitY(sd.DUMMY, sd.cy)
set sd.defTheta = sd.defTheta + ORBIT_ANGLEINC
if sd.phase == 0 then
set sd.orbitR = sd.orbitR + sd.orbitV*TIMER_TICK
set sd.orbitV = sd.orbitV - sd.orbitA*TIMER_TICK
call sd.updateDefenders()
set sd.d = sd.d-1
if sd.d <= 0 then
set sd.phase = sd.phase+1
set sd.d = sd.defCount
endif
elseif sd.phase == 1 then
call sd.updateDefenders()
call sd.onPeriodic()
set sd.fire = sd.fire-1
if sd.fire <= 0 then
set mx = UserMouse[sd.p].mouseX
set my = UserMouse[sd.p].mouseY
if (mx-sd.cx)*(mx-sd.cx) + (my-sd.cy)*(my-sd.cy) > FIRE_MAX_DIST*FIRE_MAX_DIST then
set a = Atan2(my-sd.cy, mx-sd.cx)
set mx = sd.cx + FIRE_MAX_DIST*Cos(a)
set my = sd.cy + FIRE_MAX_DIST*Sin(a)
endif
call sd.fireDefender(mx, my, FIRE_IMPACT_H)
set sd.fire = R2I(FIRE_PERIOD/TIMER_TICK)
endif
if sd.d <= 0 then
call sd.endInstance()
endif
endif
set CURR = CURR+1
endloop
endmethod
static method create takes unit c returns thistype
local thistype sd = thistype.allocate()
local integer i = 0
set sd.c = c
set sd.cx = GetUnitX(sd.c)
set sd.cy = GetUnitY(sd.c)
set sd.p = GetOwningPlayer(sd.c)
set sd.l = GetUnitAbilityLevel(sd.c, SPELL_ID)
set sd.d = R2I(ORBIT_DELAY/TIMER_TICK)
set sd.defCount = sd.numDefenders()
set sd.defAng = 2*bj_PI/sd.defCount
set sd.DUMMY = CreateUnit(sd.p, DUMMY_ID, sd.cx, sd.cy, 0.)
set sd.orbitR = 0.
set sd.orbitV = 2.*ORBIT_RADIUS/ORBIT_DELAY
set sd.orbitA = 2./ORBIT_DELAY*(sd.orbitV - ORBIT_RADIUS/ORBIT_DELAY)
loop
set sd.defs[i] = defender.create(sd, i)
set i = i+1
exitwhen i >= sd.defCount
endloop
if COUNT == 0 then
call TimerStart(SpellTimer, TIMER_TICK, true, function thistype.periodic)
endif
set GSD[COUNT] = sd
set COUNT = COUNT+1
return sd
endmethod
static method onCast takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call SpellData.create(GetTriggerUnit())
call GSD[COUNT-1].onStart()
endif
return false
endmethod
static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(CastTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(CastTrigger, Condition(function thistype.onCast))
call thistype.onInitEx()
endmethod
endstruct
endlibrary
//***************************************************************************
//* *
//* *
//* +----------------------------+ *
//* | Cluster Rockets [v1.1.0] | *
//* | by Ofel [ClsR_] | *
//* +----------------------------+ *
//* *
//* *
//***************************************************************************
//***************************************************************************
//*
//* Additional Configuration
//*
globals
boolean ClsR_PRELOAD_SPELL = true
boolean ClsR_BOUNDS_ENTIRE_MAP = true
real ClsR_FRAME_UPDATE = 1 / 32.
real ClsR_COLLISION_SCAN_RANGE = 200.00
real ClsR_PI = 3.1415926
real ClsR_DEGTORAD = 0.0174532
real ClsR_RADTODEG = 57.2957795
endglobals
//---------------------------------------------------------------------------
//*
//* Filteration
//*
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Units that can be acquired as target by the rockets.
function ClsR_AcquisitionFilter takes unit filterUnit, player casterOwner returns boolean
return IsUnitEnemy(filterUnit, casterOwner) and not IsUnitType(filterUnit, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
//---------------------------------------------------------------------------
// Units that can trigger collision with the rockets.
function ClsR_CollisionFilter takes unit filterUnit, player casterOwner returns boolean
return IsUnitEnemy(filterUnit, casterOwner)
endfunction
//---------------------------------------------------------------------------
// Units that can receive damage.
function ClsR_DamageExplosionFilter takes unit filterUnit, player casterOwner returns boolean
return not IsUnitType(filterUnit, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
//*
//* End of Configuration
//*
//***************************************************************************
//---------------------------------------------------------------------------
// Checks if unit is alive.
function ClsR_IsUnitAlive takes unit targetUnit returns boolean
return GetUnitTypeId(targetUnit) != 0 and not IsUnitType(targetUnit, UNIT_TYPE_DEAD)
endfunction
//---------------------------------------------------------------------------
// Checks if unit is channeling the spell.
function ClsR_IsUnitChanneling takes unit targetUnit returns boolean
return GetUnitCurrentOrder(targetUnit) == udg_ClsR_AbilityOrder
endfunction
//---------------------------------------------------------------------------
// Returns absolute value.
function ClsR_RAbs takes real a returns real
if (a >= 0) then
return a
else
return -a
endif
endfunction
//---------------------------------------------------------------------------
// Returns bounded x.
function ClsR_BoundX takes real x returns real
if x > udg_ClsR_mapBorder[1] then
set x = udg_ClsR_mapBorder[1]
elseif x < udg_ClsR_mapBorder[3] then
set x = udg_ClsR_mapBorder[3]
endif
return x
endfunction
//---------------------------------------------------------------------------
// Returns bounded y.
function ClsR_BoundY takes real y returns real
if y > udg_ClsR_mapBorder[2] then
set y = udg_ClsR_mapBorder[2]
elseif y < udg_ClsR_mapBorder[4] then
set y = udg_ClsR_mapBorder[4]
endif
return y
endfunction
//---------------------------------------------------------------------------
// Returns 2D distance between points - adapted from a vJASS library by
// Nestharus.
function ClsR_GetDistance2D takes real x, real y returns real
return SquareRoot(x*x+y*y)
endfunction
//---------------------------------------------------------------------------
// Returns 3D distance between points - adapted from a vJASS library by
// Nestharus.
function ClsR_GetDistance3D takes real x, real y, real z returns real
return SquareRoot(x*x+y*y+z*z)
endfunction
//---------------------------------------------------------------------------
// Returns z angle between points - adapted from a vJASS library by
// Nestharus.
function ClsR_AngleZ takes real x, real y returns real
return Atan2(y, x)
endfunction
//---------------------------------------------------------------------------
// Returns z angle between points using distance on the xy plane - adapted
// from a vJASS library by Nestharus.
function ClsR_AnglePointsZ takes real distance2D, real z returns real
return Atan2(z, distance2D)
endfunction
//---------------------------------------------------------------------------
// Returns the orientation angles in 3D space - adapted from a Wurst library
// by Wareditor.
function ClsR_GetOrientation3D takes real x, real y, real z returns nothing
local real ax
local real ay
local real px
local real py
if x == 0 and y == 0 then
if z > 0 then
set ax = -bj_PI*0.5
else
set ax = bj_PI*0.5
endif
elseif z != 0 then
set ax = ClsR_RAbs(x)
set ay = ClsR_RAbs(y)
set px = ax / (ax + ay)
set py = 1 - px
if y == 0 then
set ax = 0
else
set ax = Atan(z/y)*py
endif
if x == 0 then
set ay = 0
else
set ay = -Atan(z/x)*px
endif
endif
set udg_ClsR_OrientationYaw = ax
set udg_ClsR_OrientationPitch = ay
set udg_ClsR_OrientationRoll = Atan2(y, x)
endfunction
//---------------------------------------------------------------------------
// Returns the half of a unit collision size.
function ClsR_GetUnitCollisionRadius takes unit targetUnit returns real
return BlzGetUnitCollisionSize(targetUnit) / 2.
endfunction
//---------------------------------------------------------------------------
// Returns terrain z - adapted from a vJASS library by D.O.G.
function ClsR_GetTerrainZ takes real x, real y returns real
// Move Z location finder
call MoveLocation(udg_ClsR_ZLocator, x, y)
// Return location terrain z
return GetLocationZ(udg_ClsR_ZLocator)
endfunction
//---------------------------------------------------------------------------
// Returns (height + terrain z) of a unit alternatively.
function ClsR_GetUnitZAlt takes unit targetUnit, real x, real y returns real
local real height
if IsUnitType(targetUnit, UNIT_TYPE_FLYING) then
set height = GetUnitFlyHeight(targetUnit)
else
// Add bonus height when unit is not a flying type
set height = GetUnitFlyHeight(targetUnit) + udg_ClsR_RocketsUnitsHeightBonus
endif
// Return joined height and terrain z
return height + ClsR_GetTerrainZ(x, y)
endfunction
//---------------------------------------------------------------------------
// Returns 3D distance between a point and a unit.
function ClsR_GetDistanceToUnit3D takes real sourceX, real sourceY, real sz, unit targetUnit returns real
local real targetX = GetUnitX(targetUnit)
local real targetY = GetUnitY(targetUnit)
local real targetZ = ClsR_GetUnitZAlt(targetUnit, targetX, targetY)
return ClsR_GetDistance3D(targetX-sourceX, targetY-sourceY, targetZ-sz)
endfunction
//---------------------------------------------------------------------------
// Registers rocket target unit data.
function ClsR_RegisterTargetUnit takes integer rIndex returns nothing
local integer targetHandleID = GetHandleId(udg_ClsR_Target[rIndex])
local integer targetedCount = LoadInteger(udg_ClsR_Hashtable, targetHandleID, 0)
set udg_ClsR_lastX[rIndex] = GetUnitX(udg_ClsR_Target[rIndex])
set udg_ClsR_lastY[rIndex] = GetUnitY(udg_ClsR_Target[rIndex])
set udg_ClsR_lastZ[rIndex] = ClsR_GetUnitZAlt(udg_ClsR_Target[rIndex], udg_ClsR_lastX[rIndex], udg_ClsR_lastY[rIndex])
if targetedCount == 0 then
call SaveEffectHandle(udg_ClsR_Hashtable, targetHandleID, 0, AddSpecialEffectTarget(udg_ClsR_TargetEffectModel, udg_ClsR_Target[rIndex], udg_ClsR_TargetEffectAttachment))
endif
call SaveInteger(udg_ClsR_Hashtable, targetHandleID, 0, targetedCount + 1)
endfunction
//---------------------------------------------------------------------------
// Returns new target near a target point (Area of Effect).
function ClsR_AcquireNewTarget takes real x, real y, integer index returns unit
// Declare locals
local unit enumUnit
local integer i = 0
// Null reference
set udg_ClsR_Target[0] = null
// Enumerate all units in range
call GroupEnumUnitsInRange(udg_ClsR_tempGroup, x, y, udg_ClsR_AreaOfEffect[udg_ClsR_Level[index]], null)
loop
set enumUnit = FirstOfGroup(udg_ClsR_tempGroup)
exitwhen enumUnit == null
if enumUnit != udg_ClsR_Caster[index] and ClsR_IsUnitAlive(enumUnit) and ClsR_AcquisitionFilter(enumUnit, udg_ClsR_casterOwner[index]) then
// Random target
set i = i + 1
if GetRandomInt(1, i) == 1 then
set udg_ClsR_Target[0] = enumUnit
endif
endif
call GroupRemoveUnit(udg_ClsR_tempGroup, enumUnit)
endloop
// Return new target
return udg_ClsR_Target[0]
endfunction
//---------------------------------------------------------------------------
// Returns random terrain target inside Area of Effect.
function ClsR_AcquireTerrainTarget takes integer rIndex, integer cIndex returns nothing
local real facing = GetRandomReal(-ClsR_PI, ClsR_PI)
local real range = GetRandomReal(0, udg_ClsR_areaOfTarget[udg_ClsR_Level[cIndex]])
set udg_ClsR_lastX[rIndex] = udg_ClsR_posX[cIndex] + range * Cos(facing)
set udg_ClsR_lastY[rIndex] = udg_ClsR_posY[cIndex] + range * Sin(facing)
set udg_ClsR_lastZ[rIndex] = ClsR_GetTerrainZ(udg_ClsR_lastX[rIndex], udg_ClsR_lastY[rIndex])
endfunction
//---------------------------------------------------------------------------
// Returns new allocation index of a linked list.
function ClsR_Create takes nothing returns integer
// Declare locals
local integer newIndex = 0
// Allocate index
if udg_ClsR_Recyclable == 0 then
set udg_ClsR_MaxIndex = udg_ClsR_MaxIndex + 1
set newIndex = udg_ClsR_MaxIndex
else
set udg_ClsR_Recyclable = udg_ClsR_Recyclable - 1
set newIndex = udg_ClsR_Recycle[udg_ClsR_Recyclable]
endif
set udg_ClsR_Next[newIndex] = 0
set udg_ClsR_Next[udg_ClsR_Last] = newIndex
set udg_ClsR_Prev[newIndex] = udg_ClsR_Last
set udg_ClsR_Last = newIndex
// Return allocated index
return newIndex
endfunction
//---------------------------------------------------------------------------
// Cleans an instance by index.
function ClsR_Destroy takes integer index returns nothing
// Clean up
set udg_ClsR_Caster[index] = null
set udg_ClsR_Target[index] = null
set udg_ClsR_rocketFx[index] = null
set udg_ClsR_casterOwner[index] = null
set udg_ClsR_Stage[index] = 0
// Recycle index
if udg_ClsR_Last == index then
set udg_ClsR_Last = udg_ClsR_Prev[index]
endif
set udg_ClsR_Recycle[udg_ClsR_Recyclable] = index
set udg_ClsR_Recyclable = udg_ClsR_Recyclable + 1
set udg_ClsR_Next[udg_ClsR_Prev[index]] = udg_ClsR_Next[index]
set udg_ClsR_Prev[udg_ClsR_Next[index]] = udg_ClsR_Prev[index]
if udg_ClsR_Next[0] == 0 then
// --- When no active instances left
call PauseTimer(udg_ClsR_Timer)
endif
endfunction
//---------------------------------------------------------------------------
// Destroys rocket and damage nearby units.
function ClsR_TerminateRocket takes integer rocketIndex returns nothing
// Declare locals
local unit enumUnit
local integer targetHandleID
local integer targetedCount
// Enumerate all units near the rocket
call GroupEnumUnitsInRange(udg_ClsR_tempGroup, udg_ClsR_posX[rocketIndex], udg_ClsR_posY[rocketIndex], udg_ClsR_RocketsDamageRange[udg_ClsR_Level[rocketIndex]]+ClsR_COLLISION_SCAN_RANGE, null)
loop
set enumUnit = FirstOfGroup(udg_ClsR_tempGroup)
exitwhen enumUnit == null
if ClsR_GetDistanceToUnit3D(udg_ClsR_posX[rocketIndex], udg_ClsR_posY[rocketIndex], udg_ClsR_posZ[rocketIndex], enumUnit)-ClsR_GetUnitCollisionRadius(enumUnit) <= udg_ClsR_RocketsDamageRange[udg_ClsR_Level[rocketIndex]] then
if enumUnit != udg_ClsR_Caster[rocketIndex] and ClsR_IsUnitAlive(enumUnit) and ClsR_DamageExplosionFilter(enumUnit, udg_ClsR_casterOwner[rocketIndex]) then
// --- Exclude caster and dead units
// Make unit explode on death
call SetUnitExploded(enumUnit, udg_ClsR_MakeUnitExplode)
// Damage unit
call UnitDamageTarget(udg_ClsR_Caster[rocketIndex], enumUnit, udg_ClsR_DamagePerRocket[udg_ClsR_Level[rocketIndex]], true, false, udg_ClsR_RocketAttackType, udg_ClsR_RocketDamageType, WEAPON_TYPE_WHOKNOWS)
// Make unit normal
call SetUnitExploded(enumUnit, false)
endif
endif
call GroupRemoveUnit(udg_ClsR_tempGroup, enumUnit)
endloop
call GroupClear(udg_ClsR_tempGroup)
// Destroy rocket missile effect
call DestroyEffect(udg_ClsR_rocketFx[rocketIndex])
if ClsR_IsUnitAlive(udg_ClsR_Target[rocketIndex]) then
set targetHandleID = GetHandleId(udg_ClsR_Target[rocketIndex])
set targetedCount = LoadInteger(udg_ClsR_Hashtable, targetHandleID, 0)
if targetedCount == 1 then
call DestroyEffect(LoadEffectHandle(udg_ClsR_Hashtable, targetHandleID, 0))
call FlushChildHashtable(udg_ClsR_Hashtable, targetHandleID)
else
call SaveInteger(udg_ClsR_Hashtable, targetHandleID, 0, targetedCount - 1)
endif
endif
// End rocket instance
call ClsR_Destroy(rocketIndex)
endfunction
//---------------------------------------------------------------------------
// Creates new rocket instance.
function ClsR_LaunchRocket takes integer casterIndex returns nothing
// Declare locals
local integer rocketIndex
local real casterFacingRad = GetUnitFacing(udg_ClsR_Caster[casterIndex]) * ClsR_DEGTORAD
local real sourceX = GetUnitX(udg_ClsR_Caster[casterIndex]) + udg_ClsR_RocketsSpawnOffset * Cos(casterFacingRad) // Spawn x
local real sourceY = GetUnitY(udg_ClsR_Caster[casterIndex]) + udg_ClsR_RocketsSpawnOffset * Sin(casterFacingRad) // Spawn y
local real sourceZ = GetUnitFlyHeight(udg_ClsR_Caster[casterIndex]) + ClsR_GetTerrainZ(sourceX, sourceY) + udg_ClsR_RocketsSpawnHeightBonus // Spawn height + terrain z
local real targetX
local real targetY
local real targetZ
local real anglePointsZ
local real rocketFacing
local real rocketPitch
// Allocate new index
set rocketIndex = ClsR_Create()
// Note: Target type 0 - Instant (No target)
// Target type 1 - Unit target
// Target type 2 - Point target
if udg_ClsR_targetType[casterIndex] == 0 then
set udg_ClsR_Target[rocketIndex] = ClsR_AcquireNewTarget(udg_ClsR_posX[casterIndex], udg_ClsR_posY[casterIndex], casterIndex)
if udg_ClsR_Target[rocketIndex] != null then
set udg_ClsR_targetCollision[rocketIndex] = ClsR_GetUnitCollisionRadius(udg_ClsR_Target[rocketIndex])
call ClsR_RegisterTargetUnit(rocketIndex)
else
call ClsR_AcquireTerrainTarget(rocketIndex, casterIndex)
endif
elseif udg_ClsR_targetType[casterIndex] == 1 then
if ClsR_IsUnitAlive(udg_ClsR_Target[casterIndex]) then
set udg_ClsR_Target[rocketIndex] = udg_ClsR_Target[casterIndex]
set udg_ClsR_targetCollision[rocketIndex] = udg_ClsR_targetCollision[casterIndex]
call ClsR_RegisterTargetUnit(rocketIndex)
else
call ClsR_AcquireTerrainTarget(rocketIndex, casterIndex)
endif
elseif udg_ClsR_targetType[casterIndex] == 2 and udg_ClsR_AreaOfEffect[udg_ClsR_Level[casterIndex]] > 0 then
if udg_ClsR_PreferTargetUnit then
set udg_ClsR_Target[rocketIndex] = ClsR_AcquireNewTarget(udg_ClsR_posX[casterIndex], udg_ClsR_posY[casterIndex], casterIndex)
endif
if udg_ClsR_Target[rocketIndex] != null then
set udg_ClsR_targetCollision[rocketIndex] = ClsR_GetUnitCollisionRadius(udg_ClsR_Target[rocketIndex])
call ClsR_RegisterTargetUnit(rocketIndex)
else
call ClsR_AcquireTerrainTarget(rocketIndex, casterIndex)
endif
endif
// Copy properties to local reference variables
set targetX = udg_ClsR_lastX[rocketIndex]
set targetY = udg_ClsR_lastY[rocketIndex]
set targetZ = udg_ClsR_lastZ[rocketIndex]
// Get new 3D angles
set anglePointsZ = ClsR_AnglePointsZ(ClsR_GetDistance2D(targetX-sourceX, targetY-sourceY), targetZ-sourceZ)
set rocketFacing = GetRandomReal(casterFacingRad-udg_ClsR_RocketsMinFacingVariation, casterFacingRad+udg_ClsR_RocketsMaxFacingVariation)
set rocketPitch = GetRandomReal(anglePointsZ-udg_ClsR_RocketsMinPitchVariation, anglePointsZ+udg_ClsR_RocketsMaxPitchVariation)
set udg_ClsR_rocketFx[rocketIndex] = AddSpecialEffect(udg_ClsR_RocketsModel, sourceX, sourceY)
call BlzSetSpecialEffectOrientation(udg_ClsR_rocketFx[rocketIndex], 0, rocketPitch, rocketFacing)
call BlzSetSpecialEffectZ(udg_ClsR_rocketFx[rocketIndex], sourceZ)
call BlzSetSpecialEffectScale(udg_ClsR_rocketFx[rocketIndex], udg_ClsR_RocketsSize)
set udg_ClsR_Roll[rocketIndex] = rocketFacing
set udg_ClsR_Pitch[rocketIndex] = rocketPitch
set udg_ClsR_lastPitch[rocketIndex] = rocketPitch
set udg_ClsR_posX[rocketIndex] = sourceX
set udg_ClsR_posY[rocketIndex] = sourceY
set udg_ClsR_posZ[rocketIndex] = sourceZ
set udg_ClsR_Caster[rocketIndex] = udg_ClsR_Caster[casterIndex]
set udg_ClsR_casterOwner[rocketIndex] = udg_ClsR_casterOwner[casterIndex]
set udg_ClsR_Level[rocketIndex] = udg_ClsR_Level[casterIndex]
// Set up rocket expiration timer
set udg_ClsR_realTimer[rocketIndex] = udg_ClsR_RocketsExpirationTime
// Stage 2 - Handle rockets movement
set udg_ClsR_Stage[rocketIndex] = 2
// Update launched rockets counter
set udg_ClsR_Counter[casterIndex] = udg_ClsR_Counter[casterIndex] - 1
endfunction
//---------------------------------------------------------------------------
// Clears any death unit targeted by the rockets.
function ClsR_onUnitDeath takes nothing returns boolean
local integer targetHandleID = GetHandleId(GetTriggerUnit())
if LoadInteger(udg_ClsR_Hashtable, targetHandleID, 0) > 0 then
call DestroyEffect(LoadEffectHandle(udg_ClsR_Hashtable, targetHandleID, 0))
call FlushChildHashtable(udg_ClsR_Hashtable, targetHandleID)
endif
return false
endfunction
//---------------------------------------------------------------------------
// Loops all active instances.
function ClsR_Periodic takes nothing returns nothing
// Declare locals
local real sourceX
local real sourceY
local real sourceZ
local real targetX
local real targetY
local real targetZ
local real tempX
local real tempY
local real tempZ
local real dist
local real dist3D
local real angleZ
local real anglePointsZ
local unit enumUnit
local integer casterHandleID
local integer index = 0
// Loop all instances
loop
set index = udg_ClsR_Next[index]
exitwhen index == 0
// Stage 1: Handle spell channeling, launches x rockets each seconds
if udg_ClsR_Stage[index] == 1 then
if ClsR_IsUnitAlive(udg_ClsR_Caster[index]) then
if ClsR_IsUnitChanneling(udg_ClsR_Caster[index]) and udg_ClsR_Counter[index] != 0 then
// Update wave interval timer
set udg_ClsR_realTimer[index] = udg_ClsR_realTimer[index] - ClsR_FRAME_UPDATE
if udg_ClsR_realTimer[index] <= 0 then
// Update rocket launch interval timer
set udg_ClsR_realTimer2[index] = udg_ClsR_realTimer2[index] + ClsR_FRAME_UPDATE
if udg_ClsR_realTimer2[index] >= udg_ClsR_RocketsInterval then
// Launch rocket
call ClsR_LaunchRocket(index)
// Reset rocket launch timer
set udg_ClsR_realTimer2[index] = 0
set udg_ClsR_Counter2[index] = udg_ClsR_Counter2[index] + 1
if udg_ClsR_Counter2[index] >= udg_ClsR_RocketsPerWave then
// Reset wave timer
set udg_ClsR_realTimer[index] = udg_ClsR_DelayPerWave
// Reset rocket-per-wave counter
set udg_ClsR_Counter2[index] = 0
endif
endif
endif
else
// --- When caster finished channeling or all rockets has launched
set casterHandleID = GetHandleId(udg_ClsR_Caster[index])
// Unmark caster from 'channeling' state
call SaveBoolean(udg_ClsR_Hashtable, casterHandleID, 1, false)
if not LoadBoolean(udg_ClsR_Hashtable, casterHandleID, 0) and ClsR_IsUnitChanneling(udg_ClsR_Caster[index]) and udg_ClsR_Counter[index] == 0 then
call IssueImmediateOrderById(udg_ClsR_Caster[index], 851972)
else
call SaveBoolean(udg_ClsR_Hashtable, casterHandleID, 0, false)
endif
call ClsR_Destroy(index)
endif
else
// --- When caster died
// Unmark caster from 'channeling' state
call SaveBoolean(udg_ClsR_Hashtable, GetHandleId(udg_ClsR_Caster[index]), 1, false)
call ClsR_Destroy(index)
endif
// Stage 2: Handle rockets, determines movement
elseif udg_ClsR_Stage[index] == 2 then
if udg_ClsR_realTimer[index] > 0 then
// Update rocket expiration timer
set udg_ClsR_realTimer[index] = udg_ClsR_realTimer[index] - ClsR_FRAME_UPDATE
// Copy 3D position to local reference variables
set sourceX = udg_ClsR_posX[index]
set sourceY = udg_ClsR_posY[index]
set sourceZ = udg_ClsR_posZ[index]
// Check if there's a target unit to pursue
if udg_ClsR_Target[index] != null and ClsR_IsUnitAlive(udg_ClsR_Target[index]) then
set targetX = GetUnitX(udg_ClsR_Target[index])
set targetY = GetUnitY(udg_ClsR_Target[index])
set targetZ = ClsR_GetUnitZAlt(udg_ClsR_Target[index], targetX, targetY)
// Save target last known position
set udg_ClsR_lastX[index] = targetX
set udg_ClsR_lastY[index] = targetY
set udg_ClsR_lastZ[index] = targetZ
else
// If target has disappear, use the last known position
set targetX = udg_ClsR_lastX[index]
set targetY = udg_ClsR_lastY[index]
set targetZ = udg_ClsR_lastZ[index]
endif
// Calculate difference
set tempX = targetX - sourceX
set tempY = targetY - sourceY
set tempZ = targetZ - sourceZ
if udg_ClsR_Target[index] != null and ClsR_IsUnitAlive(udg_ClsR_Target[index]) and ClsR_GetDistance3D(tempX, tempY, tempZ)-udg_ClsR_RocketsCollisionSize <= udg_ClsR_targetCollision[index] then
// --- When target is alive and is in collision range
call ClsR_TerminateRocket(index)
else
set dist = ClsR_GetDistance2D(tempX, tempY)
set angleZ = ClsR_AngleZ(tempX, tempY)
set anglePointsZ = ClsR_AnglePointsZ(dist, tempZ)
// Get new 3D position
set targetX = sourceX + udg_ClsR_RocketsSpeedBase[udg_ClsR_Level[index]] * Cos(udg_ClsR_Roll[index]) * Cos(udg_ClsR_Pitch[index])
set targetY = sourceY + udg_ClsR_RocketsSpeedBase[udg_ClsR_Level[index]] * Sin(udg_ClsR_Roll[index]) * Cos(udg_ClsR_Pitch[index])
set targetZ = sourceZ + udg_ClsR_RocketsSpeedBase[udg_ClsR_Level[index]] * Sin(udg_ClsR_Pitch[index])
set udg_ClsR_posX[index] = targetX + udg_ClsR_RocketsTurnAcceleration[udg_ClsR_Level[index]] * Cos(angleZ) * Cos(anglePointsZ)
set udg_ClsR_posY[index] = targetY + udg_ClsR_RocketsTurnAcceleration[udg_ClsR_Level[index]] * Sin(angleZ) * Cos(anglePointsZ)
set udg_ClsR_posZ[index] = targetZ + udg_ClsR_RocketsTurnAcceleration[udg_ClsR_Level[index]] * Sin(anglePointsZ)
// Calculate difference
set tempX = udg_ClsR_posX[index] - sourceX
set tempY = udg_ClsR_posY[index] - sourceY
set tempZ = udg_ClsR_posZ[index] - sourceZ
// Get new 3D angle
call ClsR_GetOrientation3D(tempX, tempY, tempZ)
set udg_ClsR_Roll[index] = udg_ClsR_OrientationRoll
set udg_ClsR_Pitch[index] = ClsR_AnglePointsZ(ClsR_GetDistance2D(tempX, tempY), tempZ)
// Get bounded coordinates
set udg_ClsR_posX[0] = ClsR_BoundX(udg_ClsR_posX[index])
set udg_ClsR_posY[0] = ClsR_BoundX(udg_ClsR_posY[index])
// Apply new position
call BlzSetSpecialEffectPosition(udg_ClsR_rocketFx[index], udg_ClsR_posX[0], udg_ClsR_posY[0], udg_ClsR_posZ[index])
// Apply new orientation
call BlzSetSpecialEffectOrientation(udg_ClsR_rocketFx[index], udg_ClsR_OrientationYaw, udg_ClsR_lastPitch[index], udg_ClsR_Roll[index])
call BlzSetSpecialEffectPitch(udg_ClsR_rocketFx[index], udg_ClsR_OrientationPitch)
set udg_ClsR_lastPitch[index] = udg_ClsR_OrientationPitch
if BlzGetLocalSpecialEffectZ(udg_ClsR_rocketFx[index]) - ClsR_GetTerrainZ(udg_ClsR_posX[0], udg_ClsR_posY[0]) <= udg_ClsR_RocketsCrashHeight then
// --- When rocket touch the ground
call ClsR_TerminateRocket(index)
elseif not udg_ClsR_HitTargetOnly then
// Enumerate all units near the rocket
call GroupEnumUnitsInRange(udg_ClsR_tempGroup, udg_ClsR_posX[index], udg_ClsR_posY[index], udg_ClsR_RocketsCollisionSize+ClsR_COLLISION_SCAN_RANGE, null)
loop
set enumUnit = FirstOfGroup(udg_ClsR_tempGroup)
exitwhen enumUnit == null
// Check if the rocket has hit a unit
if ClsR_GetDistanceToUnit3D(udg_ClsR_posX[index], udg_ClsR_posY[index], udg_ClsR_posZ[index], enumUnit)-udg_ClsR_RocketsCollisionSize <= ClsR_GetUnitCollisionRadius(enumUnit) then
if enumUnit != udg_ClsR_Caster[index] and ClsR_IsUnitAlive(enumUnit) and ClsR_CollisionFilter(enumUnit, udg_ClsR_casterOwner[index]) then
// --- Exclude caster and dead units
// --- When the rocket collide with filtered units
call ClsR_TerminateRocket(index)
set enumUnit = null
exitwhen true
endif
endif
call GroupRemoveUnit(udg_ClsR_tempGroup, enumUnit)
endloop
call GroupClear(udg_ClsR_tempGroup)
endif
endif
else
// --- When rocket expiration timer finished
call ClsR_TerminateRocket(index)
endif
endif
endloop
endfunction
//---------------------------------------------------------------------------
// Handles any unit that is starting the effect of the spell.
function ClsR_onEffect takes nothing returns boolean
// Declare locals
local integer casterIndex
local integer casterHandleID
local unit targetUnit
local real targetX
local real targetY
if GetSpellAbilityId() == udg_ClsR_Ability then
set udg_ClsR_Caster[0] = GetTriggerUnit()
set casterIndex = ClsR_Create()
set casterHandleID = GetHandleId(udg_ClsR_Caster[0])
call SaveBoolean(udg_ClsR_Hashtable, casterHandleID, 0, false)
// Mark caster as 'channeling'
call SaveBoolean(udg_ClsR_Hashtable, casterHandleID, 1, true)
// Save index to end this instance when the caster starts another one
call SaveInteger(udg_ClsR_Hashtable, casterHandleID, 1, casterIndex)
set udg_ClsR_Caster[casterIndex] = udg_ClsR_Caster[0]
set udg_ClsR_casterOwner[casterIndex] = GetTriggerPlayer()
set udg_ClsR_Level[casterIndex] = GetUnitAbilityLevel(udg_ClsR_Caster[casterIndex], udg_ClsR_Ability)
set targetUnit = GetSpellTargetUnit()
// Note: When target type in Object Editor is set to "Instant
// (No Target)", GetSpellTargetUnit returns the caster.
if targetUnit == udg_ClsR_Caster[0] then
set udg_ClsR_targetType[casterIndex] = 0
set udg_ClsR_posX[casterIndex] = GetUnitX(udg_ClsR_Caster[0])
set udg_ClsR_posY[casterIndex] = GetUnitY(udg_ClsR_Caster[0])
elseif targetUnit != null then
set udg_ClsR_targetType[casterIndex] = 1
set udg_ClsR_Target[casterIndex] = targetUnit
set udg_ClsR_targetCollision[casterIndex] = ClsR_GetUnitCollisionRadius(targetUnit)
set udg_ClsR_posX[casterIndex] = GetUnitX(targetUnit)
set udg_ClsR_posY[casterIndex] = GetUnitY(targetUnit)
set udg_ClsR_posZ[casterIndex] = ClsR_GetUnitZAlt(targetUnit, udg_ClsR_posX[casterIndex], udg_ClsR_posY[casterIndex])
else
set udg_ClsR_targetType[casterIndex] = 2
set udg_ClsR_Target[casterIndex] = null
set udg_ClsR_posX[casterIndex] = GetSpellTargetX()
set udg_ClsR_posY[casterIndex] = GetSpellTargetY()
set udg_ClsR_posZ[casterIndex] = ClsR_GetTerrainZ(udg_ClsR_posX[casterIndex], udg_ClsR_posY[casterIndex])
endif
set targetUnit = null
// Set up wave interval timer
set udg_ClsR_realTimer[casterIndex] = udg_ClsR_FirstWaveDelay - ClsR_FRAME_UPDATE
// Set up rocket launch interval timer
set udg_ClsR_realTimer2[casterIndex] = ClsR_FRAME_UPDATE
if udg_ClsR_CountRockets then
// Set up launched rockets counter
set udg_ClsR_Counter[casterIndex] = udg_ClsR_RocketsCount[udg_ClsR_Level[casterIndex]]
else
// Give a signal not to count launched rockets
set udg_ClsR_Counter[casterIndex] = -1
endif
// Set up rocket-per-wave counter
set udg_ClsR_Counter2[casterIndex] = 0
// Stage 1 - Handle spell channeling, launches rockets
set udg_ClsR_Stage[casterIndex] = 1
if udg_ClsR_Prev[casterIndex] == 0 then
// --- When no instances ever activated
call TimerStart(udg_ClsR_Timer, ClsR_FRAME_UPDATE, true, function ClsR_Periodic)
endif
endif
return false
endfunction
//---------------------------------------------------------------------------
// Handles the spell before it is casted.
function ClsR_SpellPrecast takes integer casterHandleID returns nothing
// End previous spell channeling instance
set udg_ClsR_Counter[LoadInteger(udg_ClsR_Hashtable, casterHandleID, 1)] = 0
// Mark unit before it starts casting the spell
// Note: Since the caster will be ordered to 'stop' when launched rockets
// reaches a limit, this one will helps ignore that action when the
// caster start another instance.
call SaveBoolean(udg_ClsR_Hashtable, casterHandleID, 0, true)
endfunction
//---------------------------------------------------------------------------
// Handles any unit that is casting the spell (No target).
function ClsR_onCast takes nothing returns boolean
// Declare locals
local integer casterHandleID
if GetSpellAbilityId() == udg_ClsR_Ability then
set casterHandleID = GetHandleId(GetTriggerUnit())
// Check if the caster is already channeling the spell
if LoadBoolean(udg_ClsR_Hashtable, casterHandleID, 1) then
call ClsR_SpellPrecast(casterHandleID)
endif
endif
return false
endfunction
//---------------------------------------------------------------------------
// Handles any unit that is casting the spell (Unit or point target).
function ClsR_onOrder takes nothing returns boolean
// Declare locals
local integer casterHandleID
if GetUnitCurrentOrder(GetTriggerUnit()) == udg_ClsR_AbilityOrder then
// Note: This function is also executed when a caster learn the spell
// while channeling.
set casterHandleID = GetHandleId(GetTriggerUnit())
// Check if the caster is already channeling the spell
if LoadBoolean(udg_ClsR_Hashtable, casterHandleID, 1) then
call ClsR_SpellPrecast(casterHandleID)
endif
endif
return false
endfunction
//---------------------------------------------------------------------------
// Initializes spell properties.
function ClsR_Initialize takes nothing returns nothing
local integer i = 0
local trigger trig = CreateTrigger()
local trigger trig2 = CreateTrigger()
local rect map
if ClsR_BOUNDS_ENTIRE_MAP then
set map = GetWorldBounds()
else
set map = bj_mapInitialPlayableArea
endif
set udg_ClsR_mapBorder[1] = GetRectMaxX(map)
set udg_ClsR_mapBorder[2] = GetRectMaxY(map)
set udg_ClsR_mapBorder[3] = GetRectMinX(map)
set udg_ClsR_mapBorder[4] = GetRectMinY(map)
call RemoveRect(map)
// Convert facing and pitch angle from degree to radian
set udg_ClsR_RocketsFacingWidth = udg_ClsR_RocketsFacingWidth / 2.
set udg_ClsR_RocketsMaxFacingVariation = (udg_ClsR_RocketsMaxFacingVariation + udg_ClsR_RocketsFacingWidth) * ClsR_DEGTORAD
set udg_ClsR_RocketsMinFacingVariation = (udg_ClsR_RocketsMinFacingVariation + udg_ClsR_RocketsFacingWidth) * ClsR_DEGTORAD
set udg_ClsR_RocketsMaxPitchVariation = udg_ClsR_RocketsMaxPitchVariation * ClsR_DEGTORAD
set udg_ClsR_RocketsMinPitchVariation = udg_ClsR_RocketsMinPitchVariation * ClsR_DEGTORAD
set udg_ClsR_RocketsInterval = 1 / I2R(udg_ClsR_RocketsPerSecond)
// Initialize additional properties
loop
set i = i + 1
exitwhen i > udg_ClsR_AbilityLevels
set udg_ClsR_RocketsSpeedBase[i] = udg_ClsR_RocketsSpeedBase[i] * ClsR_FRAME_UPDATE
set udg_ClsR_RocketsTurnAcceleration[i] = udg_ClsR_RocketsTurnAcceleration[i] * ClsR_FRAME_UPDATE
set udg_ClsR_areaOfTarget[i] = (udg_ClsR_AreaOfEffect[i] - udg_ClsR_RocketsDamageRange[i]) / 2.
endloop
// Initialize terrain z finder
if udg_ClsR_ZLocator == null then
set udg_ClsR_ZLocator = Location(0, 0)
endif
// Initialize spell hashtable
if udg_ClsR_Hashtable == null then
set udg_ClsR_Hashtable = InitHashtable()
endif
// Initialize iteration timer
if udg_ClsR_Timer == null then
set udg_ClsR_Timer = CreateTimer()
endif
// Initialize dummy group
if udg_ClsR_tempGroup == null then
set udg_ClsR_tempGroup = CreateGroup()
endif
if ClsR_PRELOAD_SPELL then
// Preload files
call Preload(udg_ClsR_RocketsModel)
endif
if udg_ClsR_NoTarget then
// Register a trigger for when a unit cast the spell
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddCondition(trig, Condition(function ClsR_onCast))
else
// Register a trigger for when a unit is ordered to cast the spell
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerAddCondition(trig, Condition(function ClsR_onOrder))
endif
// Register a trigger for when a unit is dying
call TriggerRegisterAnyUnitEventBJ(trig2, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(trig2, Condition(function ClsR_onUnitDeath))
// Null references
set trig = null
set trig2 = null
endfunction
//===========================================================================
// This function automatically runs on map initialization.
function InitTrig_Cluster_Rockets takes nothing returns nothing
set gg_trg_Cluster_Rockets = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_Cluster_Rockets, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_Cluster_Rockets, Condition(function ClsR_onEffect))
endfunction
//TESH.scrollpos=74
//TESH.alwaysfold=0
//==========================================================================================
// SoulConsumption v1.02 by watermelon_1234
//******************************************************************************************
// Libraries required: (Libraries with * are optional)
// - T32 http://www.thehelper.net/forums/showthread.php/132538-Timer32
// - xe system http://www.wc3c.net/showthread.php?t=101150
// * BoundSentinel http://www.wc3c.net/showthread.php?t=102576
// * GroupUtils http://www.wc3c.net/showthread.php?t=104464
//##########################################################################################
// Importing:
// 1. Copy the ability, Soul Consumption
// 2. Implement the required libraries
// 3. Copy this trigger
// 4. Configure the spell
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Notes:
// - Right now, the spell will make missiles from units that ordinarily cannot be raised if
// the spell is used before the unit decays completely. (Example: Ghost creep)
// - If the caster moves a great distance suddenly, the missiles will teleport to the
// caster instead of moving to that point like a real missile.
//==========================================================================================
scope SoulConsumption
//==========================================================================================
// CONSTANTS
//==========================================================================================
globals
//------------------------------------------------------------------------------------------
// General spell settings
//------------------------------------------------------------------------------------------
private constant integer SPELL_ID = 'A0SN' // The raw id of the spell
//------------------------------------------------------------------------------------------
// Missile settings
//------------------------------------------------------------------------------------------
private constant string MISSILE_SFX = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl" // SFX for the missile
private constant real MISSILE_ROTATION = bj_PI // Determines how fast the missile will orbit the caster in radians
private constant real MISSILE_HEIGHT = 60. // Height of the missile
private constant real MISSILE_AREA = 50. // Area for the missile to damage enemy units
private constant real MAX_MISSILE_DUR = 2. // Max duration missiles will take to get to the caster
//------------------------------------------------------------------------------------------
// Other SFX settings
//------------------------------------------------------------------------------------------
private constant boolean PRELOAD = true // Determines if the spell preloads sfxs
private constant string DEAD_SFX = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl" // Sfx that plays on the dead units
private constant string HIT_SFX = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
private constant string HIT_ATTACH = "chest" // Attachment point for HIT_SFX
private constant string HEAL_SFX = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl" // Sfx for healing
private constant string HEAL_ATTACH = "origin" // Attachment point for HEAL_SFX
//------------------------------------------------------------------------------------------
// Damage settings
//------------------------------------------------------------------------------------------
private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WPN_TYPE = null
endglobals
//==========================================================================================
// OTHER CONFIGURATION
//==========================================================================================
// Determines the area of the spell
private constant function Area takes integer lvl returns real
return 450.+50*lvl
endfunction
// Determines which units will be consumed by the spell
private function DeadTargets takes unit target returns boolean
return IsUnitType(target, UNIT_TYPE_DEAD) and /*
*/ not IsUnitType(target, UNIT_TYPE_MECHANICAL) and /*
*/ not IsUnitType(target, UNIT_TYPE_HERO) and /*
*/ not IsUnitType(target, UNIT_TYPE_FLYING)
endfunction
// Determines which units will be damaged by the spell
private function AffectedTargets takes unit target, player owner returns boolean
return not IsUnitType(target, UNIT_TYPE_DEAD) and /*
*/ IsUnitEnemy(target, owner) and /*
*/ not IsUnitType(target, UNIT_TYPE_MECHANICAL) and /*
*/ not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
// Damage dealt to enemy units
private constant function Damage takes integer lvl returns real
return 65. + 40*lvl
endfunction
// The % of dead unit's max life that will be used to heal the caster
private constant function HealPercent takes integer lvl returns real
return .07 + .07*lvl
endfunction
//==========================================================================================
// END OF CONFIGURATION
//==========================================================================================
globals
private unit TempCast // Stores caster unit for group enumeration
private integer TempCount // Used to count how many dead units are enumerated to determine rotation direction
endglobals
private struct Data
unit cast // Caster of the spell
integer lvl // Level of the spell when it was first cast
xefx missile // Displays the missile
real heal // Stores how much to heal the caster by
real ang // Stores the angle for the missile rotation and its facing
real dist // The distance between the dead unit & caster
real count = 0 // Counts how long the missile has lasted
real expire // Used to determine when the missile should reach the caster.
integer way // Determines how the missiles will rotate, clockwise or counter-clockwise
group hit // Ensures that units won't be damaged twice by the same missile
static thistype temp // For group enumeration
// Damages units that collide with the missile
static method damageUnits takes nothing returns boolean
local unit u = GetFilterUnit()
if not IsUnitInGroup(u, temp.hit) and AffectedTargets(u, GetOwningPlayer(temp.cast)) then
call DestroyEffect(AddSpecialEffectTarget(HIT_SFX, u, HIT_ATTACH))
call UnitDamageTarget(temp.cast, u, Damage(temp.lvl), false, true, ATK_TYPE, DMG_TYPE, WPN_TYPE)
call GroupAddUnit(temp.hit, u)
endif
set u = null
return false
endmethod
// Moves the missile and damages nearby units
method periodic takes nothing returns nothing
local boolean dead = IsUnitType(.cast, UNIT_TYPE_DEAD) or GetUnitTypeId(.cast) == 0
if .count < .expire and not dead then
set .count = .count + T32_PERIOD
set .ang = .ang + .way * MISSILE_ROTATION * T32_PERIOD
set .missile.x = GetUnitX(.cast) + (.dist-.dist/.expire * .count)*Cos(.ang)
set .missile.y = GetUnitY(.cast) + (.dist-.dist/.expire * .count)*Sin(.ang)
set .missile.xyangle = .ang + bj_PI // Adds PI for correct facing
set temp = this
static if LIBRARY_GroupUtils then
call GroupEnumUnitsInArea(ENUM_GROUP, .missile.x, .missile.y, MISSILE_AREA, Filter(function thistype.damageUnits))
else
call GroupEnumUnitsInRange(bj_lastCreatedGroup, .missile.x, .missile.y, MISSILE_AREA, Filter(function thistype.damageUnits))
endif
else
// Don't heal the caster if it is already dead
if not dead then
call SetWidgetLife(.cast, GetWidgetLife(.cast) + .heal)
call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, .cast, HEAL_ATTACH))
else
call DestroyEffect(AddSpecialEffect(HEAL_SFX, .missile.x, .missile.y))
endif
call .missile.destroy()
static if LIBRARY_GroupUtils then
call ReleaseGroup(.hit)
else
call GroupClear(.hit)
endif
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
// Creates the missile from the dead unit
static method createMissile takes unit c, unit u returns nothing
local thistype this = thistype.allocate()
local real cx = GetUnitX(c)
local real cy = GetUnitY(c)
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local real dx = ux - cx
local real dy = uy - cy
set .cast = c
set .lvl = GetUnitAbilityLevel(c, SPELL_ID)
set .heal = HealPercent(.lvl)*GetUnitState(u, UNIT_STATE_MAX_LIFE)
set .ang = Atan2(dy, dx)
set .dist = SquareRoot(dx * dx + dy * dy)
set .expire = .dist/Area(.lvl) * MAX_MISSILE_DUR
set .missile = xefx.create(ux, uy, .ang + bj_PI)
set .missile.fxpath = MISSILE_SFX
set .missile.z = MISSILE_HEIGHT
// Determines whether the missile will rotate clockwise or counter-clockwise
if ModuloInteger(TempCount, 2) == 0 then
set .way = -1
else
set .way = 1
endif
call DestroyEffect(AddSpecialEffect(DEAD_SFX, ux, uy))
call RemoveUnit(u) // To remove the corpse.
static if LIBRARY_GroupUtils then
set .hit = NewGroup()
else
if .hit == null then
set .hit = CreateGroup()
endif
endif
call .startPeriodic()
endmethod
// Enumerates dead units to make missiles
static method enumDeadUnits takes nothing returns boolean
if DeadTargets(GetFilterUnit()) then
call thistype.createMissile(TempCast, GetFilterUnit())
set TempCount = TempCount + 1
endif
return false
endmethod
// Spell actions
static method spellActions takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
set TempCast = GetTriggerUnit()
set TempCount = 0
static if LIBRARY_GroupUtils then
call GroupEnumUnitsInArea(ENUM_GROUP, GetUnitX(TempCast), GetUnitY(TempCast), Area(GetUnitAbilityLevel(TempCast,SPELL_ID)), Filter(function thistype.enumDeadUnits))
else
call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(TempCast), GetUnitY(TempCast), Area(GetUnitAbilityLevel(TempCast,SPELL_ID)), Filter(function thistype.enumDeadUnits))
endif
endif
return false
endmethod
static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.spellActions))
set t = null
static if PRELOAD then
call Preload(MISSILE_SFX)
call Preload(HIT_SFX)
call Preload(HEAL_SFX)
call Preload(DEAD_SFX)
endif
endmethod
endstruct
endscope
//TESH.scrollpos=15
//TESH.alwaysfold=0
//**************************************************************************************
// Installation:
//
//
// - 1. Made on Warcraft III v1.30
// - 2. Copy 'this trigger' and 'DD Library' to your map.
// - 3. Go to Import Manager (F12) and extract icons: BTNInstinctFlash.blp and DISBTN versions
// sounds: sword_chop.wav, sword_echo.wav, sword_draw.wav, sword_dmg1.wav, sword_dmg2.wav
// - 4. Import all of this to your map and set there path to as you can see here (in my map)
// - 5. Go to Object Manager (F6) and copy:
// units: Illusion Blademaster
// ability: Instinct Flash
// When you are pasting them to your map make sure you type the following rawcodes:
// Illusion Blademaster: 'ssib'
// Instinct Flash: 'InFl'
// - 6. If you want different raw codes you must edit them below as well:
// - 7. Edit data below to your own will and enjoy!
//
//**************************************************************************************
//! zinc
library InstinctFlash requires DDLib
{
// *****************************************************************************************
// === Main Data ===
// *****************************************************************************************
// This is the raw code of the spell
// Default: InFl
constant integer ABILITY_INSTINCT_FLASH = 'A0QD';
// This is the raw code of illusion
// Default: ssib
constant integer UNIT_ILLUSION_RAWCODE = 'h000';
// *****************************************************************************************
// === Effects ===
// *****************************************************************************************
// Model file of spells motion sfx
// Default: Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl
constant string EFFECT_BLADE_ATTACHMENT = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl";
constant string EFFECT_BLADE_ATTACH_POINT = "weapon";
// Default: Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl
constant string EFFECT_WATER_IMPACT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl";
// Default: Objects\\SpawnQModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl
constant string EFFECT_GROUND_IMPACT = "Objects\\SpawnModels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl";
// Default: Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl
constant string EFFECT_UNIT_SLASH_BLEAD = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl";
// *****************************************************************************************
// === Sounds ===
// *****************************************************************************************
// Default: Custom\\Spells\\Instinct Flash\\sword_draw.wav
constant string SOUND_DRAW = "Custom\\Spells\\Instinct Flash\\sword_draw.wav";
// Default: 85.
constant real SOUND_DRAW_VOL = 85.;
// Default: Custom\\Spells\\Instinct Flash\\sword_echo.wav
constant string SOUND_ECHO = "Custom\\Spells\\Instinct Flash\\sword_echo.wav";
// Default: 45.
constant real SOUND_ECHO_VOL = 35.;
// Default: Custom\\Spells\\Instinct Flash\\sword_dmg1.wav
constant string SOUND_DMG1 = "Custom\\Spells\\Instinct Flash\\sword_dmg1.wav";
// Default: 45.
constant real SOUND_DMG1_VOL = 85.;
// Default: Custom\\Spells\\Instinct Flash\\sword_dmg2.wav
constant string SOUND_DMG2 = "Custom\\Spells\\Instinct Flash\\sword_dmg2.wav";
// Default: 85.
constant real SOUND_DMG2_VOL = 85.;
// Default: Custom\\Spells\\Instinct Flash\\sword_chop.wav
constant string SOUND_CHOP = "Custom\\Spells\\Instinct Flash\\sword_chop.wav";
// Default: 85.
constant real SOUND_CHOP_VOL = 85.;
// Default: 3500.
constant real SOUND_MAX_DISTANCE = 3500.;
// *****************************************************************************************
// === Initial Illusions ===
// *****************************************************************************************
// Max illusion creation distance (height)
// Default: 300
constant real ILLUSION_HEIGHT = 300.;
// Max illusion creation distance (width)
// Default: 550
constant real ILLUSION_WIDTH = 550.;
// This is duration of illusions in seconds
// Default: 0.6
constant real ILLUSION_DURATION = .6;
// This is the animation speed of illusions in percentage
// Default: 100*(1.4-0.15)*0.4 ( 1.4 = Animation duration, 0.4 = Illusion duration, -0.15 extra constant value )
constant real ILLUSION_ANIMATION_SPEED = 70.;
// This is animation of illusion which should be played
// Default: 9 (stand ready)
constant integer ILLUSION_ANIMATION_ID = 9; // (1.4)
// Maximum number of illusions at same time
// Default: 8
constant integer MAX_ILLUSIONS = 8;
constant integer MAX_ILLUSIONS_ARRAY = 4;
//
constant integer ILLUSION_COLOR_RED = 125;
constant integer ILLUSION_COLOR_GREEN = 125;
constant integer ILLUSION_COLOR_BLUE = 240;
constant integer ILLUSION_COLOR_ALPHA = 128;
// *****************************************************************************************
// === Caster Motion ===
// *****************************************************************************************
// This is casters end animation
// Default: 8 (attack 2)
constant integer CASTER_ATTACK_ANIMATION_ID = 8; // (1.134)
// Speed at which caster is traveling after multiple illusions are fused
// Default: 1000.
constant real CASTER_SPEED = 1000.;
// Default: 150
constant integer CASTER_COLOR_ALPHA = 150;
// Casters end animation speed in percentage
// Default: 200
constant real CASTER_ANIMATION_SPEED = 200.;
// This is the area of damage effect
// Default: 135
constant real CASTER_DAMAGE_RANGE = 135.;
// *****************************************************************************************
// === Killed Units Motion ===
// *****************************************************************************************
// Defines travel distance of units which are pushed
// Default: 330
constant real PUSH_DISTANCE = 330.;
// Defines travel height of units which are pushed
// Default: 230
constant real PUSH_HEIGHT = 230.;
// Defines start push speed
// Default: 500
constant real PUSH_SPEED = 500.;
// *****************************************************************************************
// *** (Aftereffect Illusions From Casters Movement) ***
// *****************************************************************************************
// This is casters end animation
// Default: 8 (attack 2)
constant integer AEFF_ILLUSION_ATTACK_ANIMATION_ID = 8; // (1.134)
// Duration of casters end animation
// Default: 0.66
constant real AEFF_ILLUSION_DURATION = .66;
// Casters end animation speed in percentage
// Default: 100*1.134/0.5 (1.134 = animation duration, 0.5 seconds to finish animation)
constant real AEFF_ILLUSION_ANIMATION_SPEED = 226.8;
// How much distance caster passes to produce a single aftereffect illusion
// Default: 125.
constant real AEFF_ILLUSION_DISTANCE_STEP = 125.;
real DamageAmount[];
// *****************************************************************************************
// === Level Data ===
// *****************************************************************************************
// *** Edit level data below ***
function InstinctFlash_Setup() {
// *** This is the damage per level ***
// Default: 175 / 285 / 400
DamageAmount[01] = 175.;
DamageAmount[02] = 285.;
DamageAmount[03] = 400.;
// *** This is the color of illusions ***
// Arguments: red, green, blue, alpha (from 0 to 255)
// Default: 125, 125, 240, 255
//Color = CreateColor(125, 125, 240, 255)
}
// *****************************************************************************************
// === Unit Filter ===
// *****************************************************************************************
function UnitFilter_Setup(unit u, player p) -> boolean {
return !IsUnitType(u, UNIT_TYPE_STRUCTURE) &&
!IsUnitType(u, UNIT_TYPE_FLYING) &&
!IsUnitType(u, UNIT_TYPE_MECHANICAL) &&
!BlzIsUnitInvulnerable(u) &&
!DDIsUnitWard(u) &&
!IsUnitType(u, UNIT_TYPE_DEAD) &&
IsUnitEnemy(u, p);
}
// ==============================================================================
// DO NOT EDIT BELOW IF YOU DONT KNOW JASS
// ==============================================================================
struct uknockback {
unit u;
real x, y, wz;
real dx, dy;
real a, dist;
static method create(unit u, real rad) -> thistype {
thistype this = allocate();
this.u = u;
UnitAddAbility(u, DD_ABILITY_CROWN_FORM);
x = GetWidgetX(u); y = GetWidgetY(u); wz = DDTerrZ(x, y);
a = PUSH_HEIGHT / Pw_2(PUSH_DISTANCE/2.);
dx = DD_INTERVAL * PUSH_SPEED * Cos(rad);
dy = DD_INTERVAL * PUSH_SPEED * Sin(rad);
dist = PUSH_DISTANCE;
return this;
}
method ApplyMotion() {
DDStartTim(DD_INTERVAL, true, this, function() {
thistype this = DDTimData();
x += dx; y += dy;
SetUnitX(u, x); SetUnitY(u, y);
SetUnitFlyHeight(u, wz - DDTerrZ(x, y) + PUSH_HEIGHT - a*Pw_2(dist-(PUSH_DISTANCE/2.)), 0.);
dist -= DD_INTERVAL * PUSH_SPEED;
if (dist < 0.) {
if (!IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY/*AMPHIBIOUSPATHING*/))
DestroyEffect(AddSpecialEffect(EFFECT_WATER_IMPACT, x, y));
else
DestroyEffect(AddSpecialEffect(EFFECT_GROUND_IMPACT, x, y));
u = null;
destroy();
DDQuitTim();
}
});
}
}
struct iflash {
unit u;
player p;
effect e;
real x, y;
real dx, dy;
real dist, spd;
group g, gd;
integer lvl;
real fac;
static method create(unit u, real tx, real ty, real rad) -> thistype {
thistype this = allocate();
this.u = u;
fac = bj_RADTODEG*rad;
lvl = GetUnitAbilityLevel(u, ABILITY_INSTINCT_FLASH);
p = GetOwningPlayer(u);
x = GetUnitX(u); y = GetUnitY(u);
spd = DD_INTERVAL * CASTER_SPEED;
dx = spd * Cos(rad); dy = spd * Sin(rad);
dist = SquareRoot( DDHypot(tx-x, ty-y) );
g = DDLoadGroup(); gd = DDLoadGroup();
SetUnitPathing(u, false);
SetUnitAnimationByIndex(u, CASTER_ATTACK_ANIMATION_ID);
ShowUnit(this.u, true);
PauseUnit(u, true);
e = AddSpecialEffectTarget(EFFECT_BLADE_ATTACHMENT, this.u, EFFECT_BLADE_ATTACH_POINT);
SetUnitVertexColor(u, 255, 255, 255, CASTER_COLOR_ALPHA);
SetUnitTimeScale(u, CASTER_ANIMATION_SPEED/100.);
return this;
}
method ApplyMotion() {
DDStartTim(DD_INTERVAL, true, this, function() {
thistype this = DDTimData();
unit ae_ill;
x += dx; y += dy;
dist -= spd;
SetUnitX(u, x); SetUnitY(u, y);
if (ModuloReal(dist, AEFF_ILLUSION_DISTANCE_STEP) < spd) {
ae_ill = CreateUnit(Player(15), UNIT_ILLUSION_RAWCODE, x, y, fac);
SetUnitColor(ae_ill, GetPlayerColor(p));
SetUnitVertexColor(ae_ill, ILLUSION_COLOR_RED, ILLUSION_COLOR_GREEN, ILLUSION_COLOR_BLUE, CASTER_COLOR_ALPHA);
SetUnitPathing(ae_ill, false);
SetUnitX(ae_ill, x); SetUnitY(ae_ill, y);
PauseUnit(ae_ill, true);
SetUnitAnimationByIndex(ae_ill, CASTER_ATTACK_ANIMATION_ID);
DestroyEffect(AddSpecialEffectTarget(EFFECT_BLADE_ATTACHMENT, ae_ill, EFFECT_BLADE_ATTACH_POINT));
DDFadeUnit(ae_ill, CASTER_COLOR_ALPHA, 00, AEFF_ILLUSION_DURATION);
}
DDGFilterDataSet(this);
GroupEnumUnitsInRange(g, x, y, CASTER_DAMAGE_RANGE, Filter(function() -> boolean {
thistype this = DDGFilterData();
unit f = GetFilterUnit();
if (UnitFilter_Setup(f, p) && !IsUnitInGroup(f, gd)) {
//UnitDamageTarget(u, f, DamageAmount[lvl], true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null);
DDSpellDamage(u, f, DamageAmount[lvl]);
DestroyEffect(AddSpecialEffectTarget(EFFECT_UNIT_SLASH_BLEAD, f, "origin"));
GroupAddUnit(gd, f);
if (GetRandomInt(00, 01) == 01)
DDGenericSound(SOUND_DMG1, SOUND_DMG1_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
else
DDGenericSound(SOUND_DMG2, SOUND_DMG2_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
if (IsUnitType(f, UNIT_TYPE_DEAD)) {
uknockback.create(f, Atan2(GetWidgetY(f)-GetWidgetY(u), GetWidgetX(f)-GetWidgetX(u))).ApplyMotion();
DDGenericSound(SOUND_CHOP, SOUND_CHOP_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
}
}
f = null;
return false;
}));
if (dist < 0.) {
SetUnitPathing(u, true);
PauseUnit(u, false);
SetUnitTimeScale(u, 1.);
SetUnitVertexColor(u, 255, 255, 255, 255);
DDStartTim(.6, false, New_pUnit(u), function() {
p_unit pu = DDTimData();
SetUnitAnimation(pu[00], "stand");
pu.destroy();
DDQuitTim();
});
if (GetLocalPlayer() == p)
SelectUnit(u, true);
DDRecycleGroup(g);
DDRecycleGroup(gd);
DestroyEffect(e);
e = null;
destroy();
DDQuitTim();
}
});
}
}
struct illusions {
unit il[MAX_ILLUSIONS], u;
real dist[MAX_ILLUSIONS_ARRAY];
effect e[MAX_ILLUSIONS];
real w_spd;
real dx, dy;
real dx2, dy2;
real x, y, tx, ty;
real rad;
static method create(unit u, real tx, real ty) -> thistype {
thistype this = allocate();
integer i;
playercolor pc = GetPlayerColor(GetOwningPlayer(u));
real px, py;
this.u = u;
this.tx = tx; this.ty = ty;
x = GetUnitX(u); y = GetUnitY(u);
w_spd = DD_INTERVAL * ILLUSION_HEIGHT / ILLUSION_DURATION;
rad = Atan2(ty-y, tx-x);
dx = Cos(rad); dy = Sin(rad);
for(i=01; i < MAX_ILLUSIONS_ARRAY+01; i+=01) {
px = x + (i*ILLUSION_WIDTH)/MAX_ILLUSIONS * -dy;
py = y + (i*ILLUSION_WIDTH)/MAX_ILLUSIONS * dx;
px += ( (ILLUSION_HEIGHT / Pw_2(ILLUSION_WIDTH/02)) ) * Pw_2((i-01)*ILLUSION_WIDTH/MAX_ILLUSIONS) * dx;
py += ( (ILLUSION_HEIGHT / Pw_2(ILLUSION_WIDTH/02)) ) * Pw_2((i-01)*ILLUSION_WIDTH/MAX_ILLUSIONS) * dy;
il[i-01] = CreateUnit(Player(15), UNIT_ILLUSION_RAWCODE, px, py, bj_RADTODEG*rad);
e[i-01] = AddSpecialEffectTarget(EFFECT_BLADE_ATTACHMENT, il[i-01], "weapon");
SetUnitVertexColor(il[i-01], ILLUSION_COLOR_RED, ILLUSION_COLOR_GREEN, ILLUSION_COLOR_BLUE, ILLUSION_COLOR_ALPHA);
SetUnitColor(il[i-01], pc);
SetUnitTimeScale(il[i-01], ILLUSION_ANIMATION_SPEED/100.);
SetUnitAnimationByIndex(il[i-01], ILLUSION_ANIMATION_ID);
px = x - (i*ILLUSION_WIDTH)/MAX_ILLUSIONS * -dy;
py = y - (i*ILLUSION_WIDTH)/MAX_ILLUSIONS * dx;
px += ( (ILLUSION_HEIGHT / Pw_2(ILLUSION_WIDTH/02)) ) * Pw_2((i-01)*ILLUSION_WIDTH/MAX_ILLUSIONS) * dx;
py += ( (ILLUSION_HEIGHT / Pw_2(ILLUSION_WIDTH/02)) ) * Pw_2((i-01)*ILLUSION_WIDTH/MAX_ILLUSIONS) * dy;
il[(i-01)+MAX_ILLUSIONS_ARRAY] = CreateUnit(Player(15), UNIT_ILLUSION_RAWCODE, px, py, bj_RADTODEG*rad);
e[i-01+MAX_ILLUSIONS_ARRAY] = AddSpecialEffectTarget(EFFECT_BLADE_ATTACHMENT, il[i-01+MAX_ILLUSIONS_ARRAY], "weapon");
SetUnitVertexColor(il[(i-01)+MAX_ILLUSIONS_ARRAY], ILLUSION_COLOR_RED, ILLUSION_COLOR_GREEN, ILLUSION_COLOR_BLUE, ILLUSION_COLOR_ALPHA);
SetUnitColor(il[(i-01)+MAX_ILLUSIONS_ARRAY], pc);
SetUnitTimeScale(il[(i-01)+MAX_ILLUSIONS_ARRAY], ILLUSION_ANIMATION_SPEED/100.);
SetUnitAnimationByIndex(il[(i-01)+MAX_ILLUSIONS_ARRAY], ILLUSION_ANIMATION_ID);
dist[i-01] = (i*ILLUSION_HEIGHT*2.)/MAX_ILLUSIONS;
}
return this;
}
method ApplyMotion() {
DDStartTim(DD_INTERVAL, true, this, function() {
thistype this = DDTimData();
integer i;
real px, py;
real sq;
for(i=01; i < MAX_ILLUSIONS_ARRAY+01; i+=01) {
dist[i-01] -= w_spd;
sq = SquareRoot(Pw_2(ILLUSION_WIDTH/02)*dist[i-01] / ILLUSION_HEIGHT);
px = x + dist[i-01] * dx;
py = y + dist[i-01] * dy;
px += sq * -dy; // +90 degrees
py += sq * dx;
SetUnitX(il[i-01], px); SetUnitY(il[i-01], py);
px = x + dist[i-01] * dx;
py = y + dist[i-01] * dy;
px -= sq * -dy; // -90 degrees
py -= sq * dx;
SetUnitX(il[i-01+MAX_ILLUSIONS_ARRAY], px); SetUnitY(il[i-01+MAX_ILLUSIONS_ARRAY], py);
if (dist[i-01] < 0. && il[i-01] != null) {
RemoveUnit(il[i-01]);
RemoveUnit(il[i-01+MAX_ILLUSIONS_ARRAY]);
il[i-01] = null;
il[i-01+MAX_ILLUSIONS_ARRAY] = null;
}
}
if (il[MAX_ILLUSIONS_ARRAY-01] == null) {
DDGenericSound(SOUND_ECHO, SOUND_ECHO_VOL, x, y, SOUND_MAX_DISTANCE, 1.);
iflash.create(u, tx, ty, rad).ApplyMotion();
destroy();
DDQuitTim();
}
});
}
}
// *** Main map startup function ***
function onInit() {
// *** Load locals ***
trigger t = CreateTrigger();
// *** Define users setup ***
InstinctFlash_Setup();
// *** Event / cond / act registering ***
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
illusions il;
unit u;
if (GetSpellAbilityId() != ABILITY_INSTINCT_FLASH)
return false;
u = GetTriggerUnit();
il = illusions.create(u, GetSpellTargetX(), GetSpellTargetY());
il.ApplyMotion();
ShowUnit(u, false);
//SetUnitTimeScale(u, ILLUSION_ANIMATION_SPEED/100.);
//SetUnitAnimationByIndex(u, ILLUSION_ANIMATION_ID);
DDGenericSound(SOUND_DRAW, SOUND_DRAW_VOL, GetSpellTargetX(), GetSpellTargetY(), SOUND_MAX_DISTANCE, 1.);
return false;
}));
}
}
//! endzinc
//TESH.scrollpos=369
//TESH.alwaysfold=0
//***************************************************************************
//***************************************************************************
//***************************************************************************
// S H A D O W I M A G E B L I N K
// By: Elphis (Nyuu)
// Version: 1.0
//
// Sepll Description:
// ` - Use dark power, creating an illusion and a energy
// ball shot to the selected point and after energy ball hit the target point,
// it will explodes and stuns enemies in 2/2.5/3/3.5 seconds and cause 50/100/150/200
// damage in 300 range.
// - Installation:
// - Import/copy Shadow Image Blink code to your map
// - Import/copy the custom ability and unit to your map and change the SPELL_ID, DSPELL_ID, DUMMY_BOLT and DUMMY_CASTER if needed
// - You may view the raw ID of the objects by pressing CTRL+D in the object editor
// - You may play with the configurables below
// - Credit:
// - Unleashthepower.mdx - http://www.hiveworkshop.com/forums/models-530/unleash-power-243024/?prev=search%3DPower%26d%3Dlist%26r%3D20
// - TerrainPathability - http://www.wc3c.net/showthread.php?t=103862
//
//
//***************************************************************************
//***************************************************************************
//***************************************************************************
library ShadowImageBlink uses TerrainPathability
globals
//Spell rawcode, change if needed
private constant integer SPELL_ID = 'A02K'
//
private constant integer DSPELL_ID = 'A0R7'
//Dummy unit rawcode (Bolt model) change if needed
private constant integer DUMMY_BOLT = 'e00G'
//Dummy caster rawcode, change if needed
private constant integer CASTER_DUMMY = 'e00F'
//Crow Form ability
private constant integer CROW_FORM = 'Amrf'
//Animation play when the caster casting this spell
private constant string ANIMATION = "spell"
//Effect of spell
private constant string BLINK_CAST = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
//Effect of spell when begin & end spell
private constant string BLINK_END = "war3mapImported\\Unleash the power.mdx"
//Attachment of the caster when spell end (Using BLINK_END effect)
private constant string ATTACHMENT = "chest"
//Order id of dummy caster casting the dummy ability
private constant string ORDER_ID = "thunderbolt"
//Dummy owner
private constant player DUMMY_OWNER = Player(15)
//******************************DAMAGE DATA SETTINGS******************************
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_HERO
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_DEATH
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_CLAW_HEAVY_SLICE
//********************************************************************************
//Damage radius
private constant real DAMAGE_RADIUS = 300.
//Damage base of this spell
private constant real DAMAGE_BASE = 350.
//Animation speed of the caster unit when this spell is used
private constant real ANIMATION_SPEED = 0.63
//Spell period
private constant real PERIODIC = .021250000
//Virtual caster speed separator
private constant real SPERATION_SPEED = 25.
//Max height of the ball power
private constant real MAX_HEIGHT = 1200.
/*Fade settings*/
private constant integer FADE_COUNT = 10
private constant integer FADE_FLASH = 85
private constant integer FADE_SPEED = 10
//********************************************************
//***************************Non - Configurable*****************************
/* */
/**/private constant group G = CreateGroup()/**/
/* */
/**/private integer MUI = -1 /**/
/* */
/**/private integer array StructData /**/
/* */
/**/private constant timer TIMER = CreateTimer()/**/
//**************************************************************************
/**/private unit Dummy
//**************************************************************************
endglobals
//************************************DO NOT MODIFY ANYTHING BELOW************************************
//*******************Damage setting***********************************
/**/constant function getDamage takes integer lvl returns real /**/
/**/ return DAMAGE_BASE * lvl /**/
/**/endfunction /**/
//********************************************************************
//*******************Filter Function***********************************
/**/function filterFunc takes player owner,unit filterUnit returns boolean
/**/ //Assure a better death check//
/**/return not IsUnitType(filterUnit,UNIT_TYPE_DEAD) and /**/GetUnitTypeId(filterUnit) != 0/**/ and IsUnitEnemy(filterUnit,owner)
/**/endfunction
//********************************************************************
private struct ShadowImageBlink
unit caster
unit dummy_1
unit dummy_2
unit dummy_3
integer fade = 255
integer lvl
integer fade_count = FADE_COUNT
boolean fade_done = false
boolean subtract = true
player owner
real cos
real sin
real facing
real total = -SPERATION_SPEED
real angle
real fade_seperation
real targetx
real targety
real speration_speed
real height
real dmg
static method onPeriodic takes nothing returns nothing
local integer i = 0
local thistype this
local real x
local real y
local real o
local real z
local unit f = null
loop
exitwhen i > MUI
set this = StructData[i]
if subtract then
set x = GetUnitX(dummy_1) + cos
set y = GetUnitY(dummy_1) + sin
set o = GetUnitX(dummy_2) - cos
set z = GetUnitY(dummy_2) - sin
if fade > fade_seperation then
if not fade_done then
set fade = fade - FADE_SPEED
else
set fade = 0
endif
elseif fade_done then
set fade = FADE_FLASH
if fade_count > 0 then
set fade_count = fade_count - 1
else
set subtract = false
set fade_done = false
set fade = 0
set cos = Cos(angle)
set sin = Sin(angle)
set x = targetx + total * cos
set y = targety + total * sin
call SetUnitX(dummy_1,x)
call SetUnitY(dummy_1,y)
call DestroyEffect(AddSpecialEffect(BLINK_CAST,x,y))
set x = targetx - total * cos
set y = targety - total * sin
call SetUnitX(dummy_2,x)
call SetUnitY(dummy_2,y)
call DestroyEffect(AddSpecialEffect(BLINK_CAST,x,y))
set cos = speration_speed*Cos(angle)
set sin = speration_speed*Sin(angle)
endif
else
set fade_done = true
endif
set total = total + speration_speed
endif
if not fade_done and not subtract then
set x = GetUnitX(dummy_1) - cos
set y = GetUnitY(dummy_1) - sin
set o = GetUnitX(dummy_2) + cos
set z = GetUnitY(dummy_2) + sin
if fade_count < FADE_COUNT then
set fade_count = fade_count + 1
if fade == 0 then
set fade = FADE_FLASH
else
set fade = 0
endif
if fade_count == FADE_COUNT then
set fade = 0
endif
elseif fade < 255 then
set fade = fade + FADE_SPEED
else
set StructData[i] = StructData[MUI]
set StructData[MUI] = -2
set MUI = MUI - 1
if MUI == -1 then
call PauseTimer(TIMER)
endif
call DestroyEffect(AddSpecialEffectTarget(BLINK_END,caster,ATTACHMENT))
call GroupEnumUnitsInRange(G,targetx,targety,DAMAGE_RADIUS,null)
call SetUnitAbilityLevel(Dummy,DSPELL_ID,lvl)
loop
set f = FirstOfGroup(G)
exitwhen f == null
if filterFunc(owner,f) then
call SetUnitX(Dummy,GetUnitX(f))
call SetUnitY(Dummy,GetUnitY(f))
call IssueTargetOrder(Dummy,ORDER_ID,f)
call UnitDamageTarget(caster,f,dmg,true,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
endif
call GroupRemoveUnit(G,f)
endloop
call SetUnitX(caster,targetx)
call SetUnitY(caster,targety)
call ShowUnit(caster,true)
call SetUnitTimeScale(caster,1.)
if GetLocalPlayer() == owner then
call ClearSelection()
call SelectUnit(caster,true)
endif
call RemoveUnit(dummy_1)
call RemoveUnit(dummy_2)
call RemoveUnit(dummy_3)
set owner = null
set dummy_1 = null
set dummy_2 = null
set dummy_3 = null
set caster = null
call destroy()
endif
endif
call SetUnitX(dummy_1,x)
call SetUnitY(dummy_1,y)
call SetUnitX(dummy_2,o)
call SetUnitY(dummy_2,z)
call SetUnitFlyHeight(dummy_3,GetUnitFlyHeight(dummy_3)-height,0.)
call SetUnitVertexColor(dummy_1,255,255,255,fade)
call SetUnitVertexColor(dummy_2,255,255,255,fade)
set i = i + 1
endloop
endmethod
static method onCast takes nothing returns boolean
local thistype this
local real x
local real y
local playercolor owner_color
local integer i
if GetSpellAbilityId() == SPELL_ID then
set x = GetSpellTargetX()
set y = GetSpellTargetY()
if IsTerrainWalkable(x,y) then
set this = allocate()
set MUI = MUI + 1
set StructData[MUI] = this
set caster = GetTriggerUnit()
set owner = GetTriggerPlayer()
set owner_color = GetPlayerColor(owner)
set lvl = GetUnitAbilityLevel(caster,SPELL_ID)
set dmg = getDamage(lvl)
call ShowUnit(caster,false)
set targetx = x
set targety = y
set fade_seperation = FADE_FLASH/3
set x = GetUnitX(caster)
set y = GetUnitY(caster)
set angle = 57.29583 * Atan2(targety - y, targetx - x) + 90.
set facing = angle - 90.
set angle = angle *.0174533
set speration_speed = SPERATION_SPEED/FADE_SPEED
set height = MAX_HEIGHT/(SPERATION_SPEED*FADE_SPEED/speration_speed-10.)
set cos = speration_speed * Cos(angle)
set sin = speration_speed * Sin(angle)
set i = GetUnitTypeId(caster)
set dummy_1 = CreateUnit(DUMMY_OWNER,i,x,y,facing)
if UnitAddAbility(dummy_1,'Aloc') then
call SetUnitPathing(dummy_1,false)
call SetUnitX(dummy_1,x)
call SetUnitY(dummy_1,y)
endif
set dummy_2 = CreateUnit(DUMMY_OWNER,i,x,y,facing)
if UnitAddAbility(dummy_2,'Aloc') then
call SetUnitPathing(dummy_2,false)
call SetUnitX(dummy_2,x)
call SetUnitY(dummy_2,y)
endif
set dummy_3 = CreateUnit(DUMMY_OWNER,DUMMY_BOLT,targetx,targety,facing)
if UnitAddAbility(dummy_3,CROW_FORM) then
call UnitRemoveAbility(dummy_3,CROW_FORM)
call SetUnitFlyHeight(dummy_3,MAX_HEIGHT,0.)
endif
//********************************************
call SetUnitColor(dummy_1,owner_color)
call SetUnitColor(dummy_2,owner_color)
//********************************************
call SetUnitTimeScale(caster,ANIMATION_SPEED)
call SetUnitTimeScale(dummy_1,ANIMATION_SPEED)
call SetUnitTimeScale(dummy_2,ANIMATION_SPEED)
//********************************************
call SetUnitAnimation(caster,ANIMATION)
call SetUnitAnimation(dummy_1,ANIMATION)
call SetUnitAnimation(dummy_2,ANIMATION)
//********************************************
if MUI == 0 then
call TimerStart(TIMER,PERIODIC,true,function thistype.onPeriodic)
endif
endif
endif
set owner_color = null
return false
endmethod
static method onInit takes nothing returns nothing
local integer i = 0
local trigger t = CreateTrigger()
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
set i = i + 1
endloop
call TriggerAddCondition(t,function thistype.onCast)
set Dummy = CreateUnit(DUMMY_OWNER,CASTER_DUMMY,0.,0.,0.)
endmethod
endstruct
endlibrary
//TESH.scrollpos=63
//TESH.alwaysfold=0
library TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//* > function IsTerrainDeepWater takes real x, real y returns boolean
//* > function IsTerrainShallowWater takes real x, real y returns boolean
//* > function IsTerrainLand takes real x, real y returns boolean
//* > function IsTerrainPlatform takes real x, real y returns boolean
//* > function IsTerrainWalkable takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//* [real] TerrainPathability_X
//* [real] TerrainPathability_Y
//*
globals
private constant real MAX_RANGE = 10.
private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals
globals
private item Item = null
private rect Find = null
private item array Hid
private integer HidMax = 0
public real X = 0.
public real Y = 0.
endglobals
function IsTerrainDeepWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
private function HideItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hid[HidMax] = GetEnumItem()
call SetItemVisible(Hid[HidMax], false)
set HidMax = HidMax + 1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
//Hide any items in the area to avoid conflicts with our item
call MoveRectTo(Find, x, y)
call EnumItemsInRect(Find ,null, function HideItem)
//Try to move the test item and get its coords
call SetItemPosition(Item, x, y) //Unhides the item
set X = GetItemX(Item)
set Y = GetItemY(Item)
static if LIBRARY_IsTerrainWalkable then
//This is for compatibility with the IsTerrainWalkable library
set IsTerrainWalkable_X = X
set IsTerrainWalkable_Y = Y
endif
call SetItemVisible(Item, false)//Hide it again
//Unhide any items hidden at the start
loop
exitwhen HidMax <= 0
set HidMax = HidMax - 1
call SetItemVisible(Hid[HidMax], true)
set Hid[HidMax] = null
endloop
//Return walkability
return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
private function Init takes nothing returns nothing
set Find = Rect(0., 0., 128., 128.)
set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope LightningBlink initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
//This ability is based on blink ability to ease the triggering part. To change the maximum/minimum distance, go to Object Editor, find the ablility there, and change it.
private constant integer SpellID = 'A0U3' //Raw code of the spell.
private constant real ElevAngle = bj_DEGTORAD * 50. //The elevation angle between the starting point of the parabola and its maximum height in degrees (converted to radians with the constant).
private constant real DummySize = 3. //The size of the lightning orb.
private constant real OrbSpeed = 20. //Distance traveled by the lightning orb every 0.04 seconds.
private constant real Damage = 750. //Amount of damage done when the lightning orb explodes.
private constant real DamageInc = 30. //Damage increment per level.
private constant real DamageAoE = 180. //Area affected by the lightning ball's explotion.
private constant real DamageAoEInc = 50. //Area increment per level.
private constant integer JumpTotal = 3 //Total times the lightning orb can jump.
private constant integer JumpTotInc = 1 //Increase in the amount of times the orb can jump per level.
private constant real JumpAoE = 600. //The maximum range to where the orb can jump.
private constant real JumpMinD = 300. //Minimum distance between the target point and new target for the orb to jump.
private constant string DummyPath = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl" //Effect path of the dummy (lightning orb, by default).
private constant string DeathEffect = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //Effect created when the orb explodes.
private constant attacktype AttackT = ATTACK_TYPE_NORMAL //Attack type of the spell.
private constant damagetype DamageT = DAMAGE_TYPE_NORMAL //Damage type of the spell.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = false //Wether the spell affects buildings.
private constant boolean Flying = false //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private timer OrbTimer = CreateTimer()
private integer OrbActiveTotal = 0
private integer array OrbStructID
private boolexpr MultiExpr
private boolean DoDamage
private unit TempU
private integer TempI
endglobals
private function DistBetweenPointsXY takes real X1, real Y1, real X2, real Y2 returns real
return SquareRoot((X2 - X1)*(X2 - X1) + (Y2 - Y1)*(Y2 - Y1))
endfunction
//--------Credits to moyack and Spec for this function!--------
function ParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)
endfunction
//-------------------------------------------------------------
private function RemoveUnits takes nothing returns nothing
call GroupRemoveUnit(ENUM_GROUP, GetEnumUnit())
endfunction
private function EnumFilter takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) and GetWidgetLife(Temp) > 0.405 then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
if DoDamage then
call UnitDamageTarget(TempU, Temp, Damage + DamageInc * (TempI - 1), true, false, AttackT, DamageT, WeaponT)
else
return true
endif
endif
endif
set Temp = null
return false
endfunction
private struct L_Orb
private unit Caster
private unit Target
private xefx OrbDummy
private group Affected
private integer Jumps = 0
private integer JumpTotal
private integer AbilityLevel
private real Distance = 0.
private real CenterX
private real CenterY
private real Cosine
private real Sine
private real MaxD
static method create takes unit caster, location targetloc returns L_Orb
local L_Orb o = L_Orb.allocate()
local real TargetX = GetLocationX(targetloc)
local real TargetY = GetLocationY(targetloc)
set o.Affected = NewGroup()
set o.Caster = caster
set o.CenterX = GetUnitX(o.Caster)
set o.CenterY = GetUnitY(o.Caster)
set o.OrbDummy = xefx.create(o.CenterX, o.CenterY, 0.)
set o.OrbDummy.fxpath = DummyPath
set o.OrbDummy.scale = DummySize
set o.AbilityLevel = GetUnitAbilityLevel(o.Caster, SpellID)
set o.Cosine = Atan2(TargetY - o.CenterY, TargetX - o.CenterX)
set o.Sine = Sin(o.Cosine)
set o.Cosine = Cos(o.Cosine)
set o.MaxD = DistBetweenPointsXY(o.CenterX, o.CenterY, TargetX, TargetY)
set o.JumpTotal = o.JumpTotal + (JumpTotInc * o.AbilityLevel - JumpTotInc)
if OrbActiveTotal == 0 then
call TimerStart(OrbTimer, 0.04, true, function L_Orb.Move)
endif
set OrbStructID[OrbActiveTotal] = o
set OrbActiveTotal = OrbActiveTotal + 1
return o
endmethod
private static method Move takes nothing returns nothing
local L_Orb TempStruct
local integer Count = 0
local real TempR
local real PointX
local real PointY
local unit Temp
loop
exitwhen Count >= OrbActiveTotal
set TempStruct = OrbStructID[Count]
set TempStruct.Distance = TempStruct.Distance + OrbSpeed
set PointX = TempStruct.CenterX + TempStruct.Distance * TempStruct.Cosine
set PointY = TempStruct.CenterY + TempStruct.Distance * TempStruct.Sine
set TempR = DistBetweenPointsXY(TempStruct.CenterX, TempStruct.CenterY, PointX, PointY)
set TempStruct.OrbDummy.x = PointX
set TempStruct.OrbDummy.y = PointY
set TempStruct.OrbDummy.z = ParabolaZ(Tan(ElevAngle) * TempStruct.MaxD/2., TempStruct.MaxD, TempR)
if TempR >= TempStruct.MaxD then
call TempStruct.Explode(TempStruct.Caster, PointX, PointY)
if TempStruct.Jumps >= TempStruct.JumpTotal then
call TempStruct.OrbDummy.destroy()
set OrbActiveTotal = OrbActiveTotal - 1
set OrbStructID[Count] = OrbStructID[OrbActiveTotal]
if OrbActiveTotal == 0 then
call PauseTimer(OrbTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
else
set DoDamage = false
set TempU = TempStruct.Caster
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, JumpAoE, MultiExpr)
set TempU = null
call GroupRemoveUnit(ENUM_GROUP, TempStruct.Target)
call GroupRefresh(TempStruct.Affected)
call ForGroup(TempStruct.Affected, function RemoveUnits)
loop
set Temp = FirstOfGroup(ENUM_GROUP)
if Temp == null then
call TempStruct.OrbDummy.destroy()
set OrbActiveTotal = OrbActiveTotal - 1
set OrbStructID[Count] = OrbStructID[OrbActiveTotal]
if OrbActiveTotal == 0 then
call PauseTimer(OrbTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
exitwhen true
elseif DistBetweenPointsXY(TempStruct.OrbDummy.x, TempStruct.OrbDummy.y, GetUnitX(Temp), GetUnitY(Temp)) >= JumpMinD then
call GroupAddUnit(TempStruct.Affected, Temp)
set TempStruct.CenterX = TempStruct.OrbDummy.x
set TempStruct.CenterY = TempStruct.OrbDummy.y
set TempStruct.Distance = 0.
set TempStruct.Cosine = Atan2(GetUnitY(Temp) - TempStruct.CenterY, GetUnitX(Temp) - TempStruct.CenterX)
set TempStruct.Sine = Sin(TempStruct.Cosine)
set TempStruct.Cosine = Cos(TempStruct.Cosine)
set TempStruct.MaxD = DistBetweenPointsXY(TempStruct.CenterX, TempStruct.CenterY, GetUnitX(Temp), GetUnitY(Temp))
set TempStruct.Jumps = TempStruct.Jumps + 1
exitwhen true
else
call GroupRemoveUnit(ENUM_GROUP, Temp)
endif
endloop
endif
endif
set Count = Count + 1
endloop
endmethod
private method Explode takes unit caster, real locx, real locy returns nothing
local unit Temp
local real PointX
local real PointY
local real Distance = 0
local integer Count = 0
local integer Count2 = 0
call DestroyEffect(AddSpecialEffect(DeathEffect, locx, locy))
loop
set Count = Count + 1
set Distance = Distance + 70.
loop
set Count2 = Count2 + 1
set PointX = locx + Distance * Cos(Count2 * bj_PI/4.)
set PointY = locy + Distance * Sin(Count2 * bj_PI/4.)
call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
exitwhen Count2 == 8
endloop
set Count2 = 0
exitwhen Count == 3
endloop
set DoDamage = true
set TempU = this.Caster
set TempI = this.AbilityLevel
call GroupEnumUnitsInRange(ENUM_GROUP, locx, locy, DamageAoE + (DamageAoEInc * this.AbilityLevel - DamageAoEInc), MultiExpr)
set TempU = null
endmethod
private method onDestroy takes nothing returns nothing
set .Caster = null
set .Target = null
call ReleaseGroup(.Affected)
endmethod
endstruct
private function ConditionsActions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
call L_Orb.create(GetTriggerUnit(), GetSpellTargetLoc())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger L_OrbTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(L_OrbTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(L_OrbTrig, Condition(function ConditionsActions))
call XE_PreloadAbility(SpellID)
call Preload(DeathEffect)
set MultiExpr = Condition(function EnumFilter)
set L_OrbTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Thunderstorm initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A0U2' //Raw code of the spell.
private constant real SpellDamage = 850. //Damage dealt by each lightning bolt.
private constant real DamageInc = 300. //Damage increased per level of the spell.
private constant real MaxRad = 650. //Maximum range at which lightning bolts spawn.
private constant real MaxRadInc = 50. //Increase of maximum radius per level.
private constant real DmgRadius = 200. //Radius affected by each lightning bolt (damage and knockback area).
private constant real DmgRadInc = 0. //Radius increase per level of the spell.
//=============================== Knockback section =================================
//Credits to Berb for his knockback library!
private constant boolean DoKnockback = true //Wether to knockback units hit by lightning bolts or not (if false, values in knockback section don't have any effect).
private constant real KnockDist = 150. //The distance units are knockbacked.
private constant real KnDistInc = 50. //Knockback distance increased per level.
private constant real KnockTime = .7 //Time it takes to complete the knockback on each unit.
private constant string KnockEffect1 = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl" //Individual effect spawn on knockback start.
private constant string KnockEffectP = "" //Effect spawned every 8 (or whatever the frequency is) executions of the periodic function, which runs every 0.04 seconds.
private constant integer EffectFrequency = 8 //Modifies the frequency in which the periodic effect spawns.
//===================================================================================
private constant integer MinBolts = 8 //Minimum number of lightning bolts spawned.
private constant integer MinBoltsInc = 2 //Minimum number of lightning bolts increased per level.
private constant integer MaxBolts = 15 //Maximum number of lightning bolts spawned.
private constant integer MaxBoltsInc = 3 //Maximum number of lightning bolts increased per level.
private constant real SpawnInterval = .1 //Time amount between the creation of each lightning bolt in seconds.
private constant real Pause = 1. //Time amount between the warning effects placement and the actual spawning of lightning bolts in seconds.
private constant string PreEffect = "Abilities\\Spells\\NightElf\\TrueshotAura\\TrueshotAura.mdl" //"Warning" effect placed before the lightning hits.
private constant string Effect1 = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl" //Effect spawned when lightning bolt hits (this is the actual lightning bolt).
private constant string Effect2 = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl" //Extra effect spawned when lightning bolt hits.
private constant attacktype AtkType = ATTACK_TYPE_NORMAL //Attack type of the spell's damage.
private constant damagetype DmgType = DAMAGE_TYPE_NORMAL //Damage type of the spell's damage.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = true //Wether the spell affects buildings.
private constant boolean Flying = true //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private boolexpr DamageExpr
private unit TempU
private integer TempI
private real TempR1
private real TempR2
endglobals
private function DamageTargets takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if GetWidgetLife(Temp) > .405 and IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
call UnitDamageTarget(TempU, Temp, SpellDamage + DamageInc * TempI, true, false, AtkType, DmgType, WeaponT)
if DoKnockback and (IsUnitType(Temp, UNIT_TYPE_STRUCTURE)) == false then
call Knockback.create(Temp, Atan2(GetUnitY(Temp) - TempR2, GetUnitX(Temp) - TempR1), KnockDist, KnockTime, KnockEffect1, KnockEffectP, EffectFrequency)
endif
endif
endif
set Temp = null
return false
endfunction
private function Actions takes nothing returns nothing
local unit Caster = GetTriggerUnit()
local xefx array TempE
local integer AbiLevel = GetUnitAbilityLevel(Caster, SpellID)
local integer LightningNumber = GetRandomInt(MinBolts + MinBoltsInc * (AbiLevel - 1), MaxBolts + MaxBoltsInc * (AbiLevel - 1))
local integer Count = 0
local real TempA
local real TempD
local real PointX
local real PointY
loop
set Count = Count + 1
set TempA = GetRandomReal(0., 2. * bj_PI)
set TempD = SquareRoot(GetRandomReal(0., 1.)) * (MaxRad + MaxRadInc * (AbiLevel - 1))
set PointX = GetUnitX(Caster) + TempD * Cos(TempA)
set PointY = GetUnitY(Caster) + TempD * Sin(TempA)
set TempE[Count] = xefx.create(PointX, PointY, 0.)
set TempE[Count].fxpath = PreEffect
exitwhen Count == LightningNumber
if SpawnInterval != 0. then
call TriggerSleepAction(SpawnInterval) //I know what you're thinking - "Ughh, waits? What the @#$! is wrong with you?" Hey... at least it's not GUI, so it's still MUI.
endif
endloop
set Count = 0
call TriggerSleepAction(Pause)
loop
set Count = Count + 1
set PointX = TempE[Count].x
set PointY = TempE[Count].y
call DestroyEffect(AddSpecialEffect(Effect1, PointX, PointY))
call DestroyEffect(AddSpecialEffect(Effect2, PointX, PointY))
set TempU = Caster
set TempI = AbiLevel - 1
static if DoKnockback then
set TempR1 = PointX
set TempR2 = PointY
endif
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, DmgRadius + DmgRadInc * (AbiLevel - 1), DamageExpr)
set TempU = null
call TempE[Count].destroy()
exitwhen Count == LightningNumber
if SpawnInterval != 0. then
call TriggerSleepAction(SpawnInterval)
endif
endloop
set Caster = null
endfunction
private function Conditions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
return true
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger StormTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(StormTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(StormTrig, function Actions)
call TriggerAddCondition(StormTrig, Condition(function Conditions))
call XE_PreloadAbility(SpellID)
call Preload(KnockEffect1)
call Preload(KnockEffectP)
call Preload(PreEffect)
call Preload(Effect1)
call Preload(Effect2)
set DamageExpr = Condition(function DamageTargets)
set StormTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Knockback
globals
//******** Library made by Berb with slight modifications by Patio_111 ********
//* Configuration
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯
//*
private constant real LOOP_REF = 0.04
//*
//*
//*****************************************************************************
endglobals
struct Knockback
readonly unit subject = null
readonly real velocity
readonly real rotation
private real rotationCos
private real rotationSin
readonly real friction
private static real checkItemX = GetRectMaxX(bj_mapInitialPlayableArea)
private static real checkItemY = GetRectMaxY(bj_mapInitialPlayableArea)
private static item checkItem
private static timer priv_stackLoop = CreateTimer()
private static constant real priv_stackLoopRef = LOOP_REF
private real priv_time
private boolean priv_stopped = false
private integer counter = 0
private string effectPeriodic
private integer effectFrequency
private integer priv_index
private static thistype array priv_stack
private static integer priv_stackN = 0
private method onDestroy takes nothing returns nothing
set thistype.priv_stackN = thistype.priv_stackN-1
set thistype.priv_stack[.priv_index] = thistype.priv_stack[thistype.priv_stackN]
set thistype.priv_stack[.priv_index].priv_index = .priv_index
if (.priv_stackN == 0) then
call PauseTimer(thistype.priv_stackLoop)
endif
endmethod
private static method onRef takes nothing returns nothing
local integer i = .priv_stackN-1
local thistype kb
local real x
local real y
local real iX
local real iY
loop
exitwhen(i < 0)
set kb = .priv_stack[i]
if (kb != 0) then
set x = GetWidgetX(kb.subject) + kb.velocity * kb.rotationCos
set y = GetWidgetY(kb.subject) + kb.velocity * kb.rotationSin
//use an item to check the pathability of the new coordinate and
//move the unit if the point is available.
call SetItemPosition(checkItem, x, y)
set iX = GetWidgetX(checkItem)
set iY = GetWidgetY(checkItem)
if ((iX-x)*(iX-x) + (iY-y)*(iY-y) <= 32) then
call SetUnitX(kb.subject, x)
call SetUnitY(kb.subject, y)
set kb.counter = kb.counter + 1
if kb.counter >= kb.effectFrequency then
call DestroyEffect(AddSpecialEffect(kb.effectPeriodic, x, y))
endif
endif
set kb.velocity = kb.velocity + kb.friction
set kb.priv_time = kb.priv_time - .priv_stackLoopRef
if (kb.priv_time <= 0.00) or kb.priv_stopped then
call PauseUnit(kb.subject, false)
call kb.destroy()
endif
endif
set i = i - 1
endloop
call SetItemPosition(checkItem, checkItemX, checkItemY)
call SetItemVisible(checkItem, false)
endmethod
static method create takes unit u, real angle, real distance, real time, string effect1, string periodiceffect, integer effectfreq returns thistype
local thistype kb = .allocate()
set kb.subject = u
set kb.velocity = 2*distance/time
set kb.rotation = angle
set kb.rotationCos = Cos(angle)
set kb.rotationSin = Sin(angle)
set kb.friction = -kb.velocity/time
set kb.priv_index = .priv_stackN
set kb.priv_time = time
set kb.velocity = kb.velocity * .priv_stackLoopRef
set kb.friction = kb.friction * .priv_stackLoopRef * .priv_stackLoopRef
set kb.effectPeriodic = periodiceffect
set kb.effectFrequency = effectfreq
call DestroyEffect(AddSpecialEffectTarget(effect1, kb.subject, "origin"))
if (.priv_stackN == 0) then
call TimerStart(.priv_stackLoop, .priv_stackLoopRef, true, function thistype.onRef)
endif
set .priv_stack[.priv_stackN] = kb
set .priv_stackN = .priv_stackN + 1
call PauseUnit(kb.subject, true)
return kb
endmethod
static method onInit takes nothing returns nothing
//the item will be have maintained invisibility as to not interfere with
//in-game actions, such as a unit picking the item up.
set checkItem=CreateItem('afac', checkItemX, checkItemY)
call SetItemVisible(checkItem, false)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ShadowCall initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A02O' //Raw code of the spell.
private constant integer SummonID = 'nsoc' //Raw code of the unit to be summoned by the spell.
private constant integer SummonQty = 2 //Amount of units summoned at level 1.
private constant integer QtyIncrease = 1 //Amount of units summoned increased per level.
private constant real Radius = 600. //Radius around the hero of the circular area in which units can be spawned.
private constant real Collision = 50. //The distance the missile has to be at from the target point in order to explode.
private constant real MissileSpeed = 400. //Distance traveled per second by the missiles.
private constant real SpawnInterval = .1 //Amount of time between the spawning of each missile.
private constant real MissileHeight = 64. //Height at which missiles fly.
private constant string MissileModel = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl" //Model used for the missiles.
private constant string FinalEffect = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl" //Effect created once the missile explodes.
//------------------------------------------------SETUP END------------------------------------------------
private timer MissileTimer = CreateTimer()
private integer MActiveTotal = 0
private integer array MStructID
endglobals
private struct Missile extends xecollider
private player Owner
static method create takes real x, real y, real dir, player owner returns Missile
local Missile m = Missile.allocate(x, y, dir)
set m.Owner = owner
return m
endmethod
private method loopControl takes nothing returns nothing
if SquareRoot((.x - .targetPointX)*(.x - .targetPointX) + (.y - .targetPointY)*(.y - .targetPointY)) <= Collision then
call .terminate()
endif
endmethod
private method onDestroy takes nothing returns nothing
call CreateUnit(.Owner, SummonID, .x, .y, GetRandomReal(0., 360.))
call DestroyEffect(AddSpecialEffect(FinalEffect, .x, .y))
endmethod
endstruct
private function Actions takes nothing returns nothing
local unit Caster = GetTriggerUnit()
local Missile TempStruct
local integer AbiLevel = GetUnitAbilityLevel(Caster, SpellID)
local integer Count = 0
local real TempA
local real TempD
local real PointX
local real PointY
loop
set TempA = GetRandomReal(0., 2. * bj_PI)
set TempD = SquareRoot(GetRandomReal(0., 1.)) * Radius
set PointX = GetUnitX(Caster) + TempD * Cos(TempA)
set PointY = GetUnitY(Caster) + TempD * Sin(TempA)
set TempStruct = Missile.create(GetUnitX(Caster), GetUnitY(Caster), TempA, GetOwningPlayer(Caster))
set TempStruct.fxpath = MissileModel
set TempStruct.z = MissileHeight
set TempStruct.speed = MissileSpeed
set TempStruct.angleSpeed = bj_PI
call TempStruct.setTargetPoint(PointX, PointY)
exitwhen Count + 1 == SummonQty + QtyIncrease * (AbiLevel - 1)
set Count = Count + 1
if SpawnInterval != 0. then
call TriggerSleepAction(SpawnInterval)
endif
endloop
endfunction
private function Conditions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
return true
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger ShadowCallTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(ShadowCallTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(ShadowCallTrig, Condition(function Conditions))
call TriggerAddAction(ShadowCallTrig, function Actions)
call XE_PreloadAbility(SpellID)
call Preload(MissileModel)
call Preload(FinalEffect)
set ShadowCallTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
// ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
// Û Û
// Û Û
// ßßßßßÛ Ûßßßßß
// Û Û
// Û Û ÜÜÜÜÜÜ ÜÜÜÜÜÜÜ
// Û Û Û Û Û Û
// Û Û Û Ü Û Û Ûßßßßßß
// Û Û Û ÛÛ Û Û Û
// Û Û Û Û Û Û Û Û ÜÜÜ
// Û Û Û Û Û Û Û Û Û Û
// Û Û Û ßßß Û Û ÛÜÜÛ Û
// Û Û Û Û Û Û
// ßßßß ßßßßßßßß ßßßßßßß
// ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
// ³ Clan TDG @ Azeroth ¸ ³
// ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
// Screen Solution: 1280x1024 Visit www.jx3.net/TDG!
// ++++++++++++++++++++++++++++++++++++++++++ INFO ++++++++++++++++++++++++++++++++++++++++
// Type........................................ : .....................................Random
// Language.................................... : ......................................vJass
// Coder....................................... : ......................................Hanky
// MUI......................................... : ........................................Yes
// ++++++++++++++++++++++++++++++++++++++++++ CREDITS ++++++++++++++++++++++++++++++++++++++++
// Thanks for the JassGenNewPack: .............pipedream
// .............xttocs
// .............Pitzermike
// .............Vexorian
// .............MindWorx
// .............Scio
// .............Starcraftfreak
// .............FyreDaug
// .............KDEWolf
// ++++++++++++++++++++++++++++++++++++++++++ NOTES ++++++++++++++++++++++++++++++++++++++++
// Greetz fly out to: NgO . BuranX . JonNny . Darkt3mpl3r . WaRadius . Fireeye
// TDG . WC3C . HIVE . Toadcop . xxdingo93xx . Dynasti . TheBlooddancer
// Paladon
//================================================================================================
//Loop - Costum Motion
//================================================================================================
//A very useful system for looping structs. If you want to know more about this small system
//then check out our website. ("www.ngo.clan.su")
//! textmacro CostumMotion takes type,run,end,periodic
static timer loopExe=CreateTimer()
static integer size =0
static $type$ array runStruct[maxIndex]
boolean active
boolean paused
static method loopRun takes nothing returns nothing
local integer index=0
loop
exitwhen index==$type$.size
if not $type$.runStruct[index].paused then
if $type$.runStruct[index].active then
call $type$.runStruct[index].$run$()
set index=index+1
else
call $type$.runStruct[index].$end$()
set $type$.size=$type$.size-1
set $type$.runStruct[index]=$type$.runStruct[$type$.size]
endif
else
set index=index+1
endif
endloop
if $type$.size==0 then
call PauseTimer($type$.loopExe)
endif
endmethod
static method addMotion takes $type$ data returns integer
if $type$.size==0 then
call TimerStart($type$.loopExe,$periodic$,true,function $type$.loopRun)
endif
set $type$.runStruct[$type$.size] =data
set $type$.runStruct[$type$.size].active=true
set $type$.runStruct[$type$.size].paused=false
set $type$.size =$type$.size+1
return $type$.size
endmethod
//! endtextmacro
//================================================================================================
//Hashtable Index System
//================================================================================================
library HIS
globals
//Config Part
private constant integer size=8190
private constant integer pos =0
//Hashtable Var
private hashtable tempcache=InitHashtable()
//Important SystemVars
private integer array inUse[size]
private integer array last [size]
private integer indexsize=0
private integer lastsize =0
endglobals
//System Code
function AddHandleIndex takes handle h returns nothing
local integer id=GetHandleId(h)
local integer qi
if HaveSavedInteger(tempcache,id,pos) then
set qi=LoadInteger(tempcache,id,pos)
set inUse[qi]=inUse[qi]+1
elseif lastsize>0 then
set lastsize =lastsize-1
set inUse[last[lastsize]]=1
call SaveInteger(tempcache,id,pos,last[lastsize])
else
set inUse[indexsize]=1
call SaveInteger(tempcache,id,pos,indexsize)
set indexsize=indexsize+1
endif
endfunction
function GetHandleIndex takes handle h returns integer
debug if not HaveSavedInteger(tempcache,GetHandleId(h),pos) then
debug call BJDebugMsg("Error: No index attached to handle [#100]")
debug endif
return LoadInteger(tempcache,GetHandleId(h),pos)
endfunction
function ClearHandleIndex takes handle h returns nothing
local integer id=GetHandleId(h)
local integer qi=LoadInteger(tempcache,id,pos)
debug if HaveSavedInteger(tempcache,id,pos) then
set inUse[qi]=inUse[qi]-1
if inUse[qi]==0 then
set last[lastsize]=qi
set lastsize =lastsize+1
call FlushChildHashtable(tempcache,id)
endif
debug else
debug call BJDebugMsg("Error: No index attached to handle [#101]")
debug endif
endfunction
endlibrary
//================================================================================================
//Functions - Standart Functions
//================================================================================================
library MainFunctions initializer init
globals
constant real deg2rad =0.017453 //Set the degree to radians value
constant real rad2deg =57.2957795//Set the radians to degree value
constant integer maxIndex =8190 //Set the maximal index number
constant real PreX =700. //Preload X
constant real PreY =700. //Preload Y
public group loopG =null
public item getWalkable =null
public location loc =null
public boolexpr filter =null
constant integer invulnerable_id ='Avul'
constant integer purge_buff_id ='Bprg'
public constant integer getWalkable_id ='sehr'
public constant integer flyHack ='Amrf'
endglobals
function GetProVal takes real value returns real
if value<0. then
return value*-1
endif
return value
endfunction
//Little helpful functions
function ClearTextMessagesForPlayer takes player p returns nothing
if (GetLocalPlayer()==p) then
call ClearTextMessages()
endif
endfunction
function UnitAddFly takes unit u returns nothing
call UnitAddAbility(u,flyHack)
call UnitRemoveAbility(u,flyHack)
endfunction
function A2PXY takes real x,real y,real xt,real yt returns real
return ModuloReal(rad2deg*Atan2(yt-y,xt-x),360.)
endfunction
function D2PXY takes real x,real y,real xt,real yt returns real
local real dx=xt-x
local real dy=yt-y
return SquareRoot(dx*dx+dy*dy)
endfunction
function IsPointWalkable takes real x,real y returns boolean
call SetItemPosition(getWalkable,x,y)
call SetItemVisible(getWalkable,false)
return GetItemX(getWalkable)==x and GetItemY(getWalkable)==y
endfunction
function GetPointWalkableX takes real x,real y returns real
call SetItemPosition(getWalkable,x,y)
call SetItemVisible(getWalkable,false)
return GetItemX(getWalkable)
endfunction
function GetPointWalkableY takes real x,real y returns real
call SetItemPosition(getWalkable,x,y)
call SetItemVisible(getWalkable,false)
return GetItemY(getWalkable)
endfunction
function DebugFilter takes nothing returns boolean
return true
endfunction
function GetUnitsInRange takes real radius,real x,real y returns group
call GroupEnumUnitsInRange(loopG,x,y,radius,filter)
return loopG
endfunction
function GetClonedGroup takes group g returns group
set bj_groupAddGroupDest = loopG
call ForGroup(g, function GroupAddGroupEnum)
return loopG
endfunction
function RangedReal takes real v,real min,real max returns real
if v<min then
// return min
elseif v>max then
// return max
endif
return v
endfunction
function GetZ takes real x,real y returns real
call MoveLocation(loc,x,y)
return GetLocationZ(loc)
endfunction
//Credits to JonNny for the Parabula
function GetFlyParabula takes real maxheight , real zs , real zt , real q returns real
return (maxheight * Sin(q*bj_PI))+ q * (zt-zs)
endfunction
//Generic unit filter
function IsUnitNotImmun takes unit c,unit u returns boolean
if IsUnitEnemy(u,GetOwningPlayer(c)) then
if GetUnitState(u,UNIT_STATE_LIFE)>0. then
if GetUnitAbilityLevel(u,invulnerable_id)<=0 then
if IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)==false then
return IsUnitType(u,UNIT_TYPE_STRUCTURE)==false
endif
endif
endif
endif
return false
endfunction
function IsUnitNotBuffImmune takes unit u returns boolean
if IsUnitType(u,UNIT_TYPE_RESISTANT)==false then
if IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)==false then
if GetUnitState(u,UNIT_STATE_LIFE)>0. then
if GetUnitAbilityLevel(u,invulnerable_id)<=0 then
return GetUnitAbilityLevel(u,purge_buff_id)<=0
endif
endif
endif
endif
return false
endfunction
function TerrainDeformationRippleXY takes real duration, boolean limitNeg, real x,real y, real startRadius, real endRadius, real depth, real wavePeriod, real waveWidth returns terraindeformation
local real spaceWave
local real timeWave
local real radiusRatio
if (endRadius <= 0 or waveWidth <= 0 or wavePeriod <= 0) then
return null
endif
set timeWave = 2.0 * duration / wavePeriod
set spaceWave = 2.0 * endRadius / waveWidth
set radiusRatio = startRadius / endRadius
set bj_lastCreatedTerrainDeformation = TerrainDeformRipple(x,y, endRadius, depth, R2I(duration * 1000), 1, spaceWave, timeWave, radiusRatio, limitNeg)
return bj_lastCreatedTerrainDeformation
endfunction
//Clearing Handles
function AB_DestroyTrigger takes trigger trig returns nothing
if trig!=null then
call TriggerClearActions(trig)
call TriggerClearConditions(trig)
call DestroyTrigger(trig)
endif
endfunction
function AB_DestroyTimer takes timer t returns nothing
if t!=null then
call PauseTimer(t)
call DestroyTimer(t)
endif
endfunction
function AB_DestroyGroup takes group g returns nothing
if g!=null then
call GroupClear(g)
call DestroyGroup(g)
endif
endfunction
function AB_DialogDestroy takes dialog log returns nothing
if log!=null then
call DialogClear(log)
call DialogDestroy(log)
endif
endfunction
function AB_DestroyMultiboard takes multiboard lb returns nothing
if lb!=null then
call MultiboardClear(lb)
call DestroyMultiboard(lb)
endif
endfunction
//Initialization of the varibales
private function init takes nothing returns nothing
set getWalkable=CreateItem(getWalkable_id,0,0)
set loc =Location(0,0)
set loopG =CreateGroup()
set filter =Filter(function DebugFilter)
call SetItemVisible(getWalkable,false)
endfunction
endlibrary
//Count function for rects in a area
library CountDestructable requires MainFunctions
globals
private rect dat_prove=Rect(0,0,0,0)
endglobals
private function EnumCountDestructables takes nothing returns nothing
if GetDestructableLife(GetEnumDestructable())>0 then
set bj_forLoopAIndex=bj_forLoopAIndex+1
endif
endfunction
function CountDestructableInRangeOfXY takes real x,real y,real range returns integer
call SetRect(dat_prove,x-range,y-range,x+range,y+range)
set bj_forLoopAIndex=0
call EnumDestructablesInRect(dat_prove,MainFunctions_filter,function EnumCountDestructables)
return bj_forLoopAIndex
endfunction
endlibrary
//================================================================================================
//Loop - Buff Check (Debug for some spells)
//================================================================================================
//This is just made for this spellpack. If you have questions about this system just ask me.
library BuffCheck requires MainFunctions
private struct BuffCheckData
integer buffid
real time
real max
unit u
method check takes nothing returns nothing
set .time=.time+0.5
set .active=.time<=.max and GetUnitAbilityLevel(.u,.buffid)>0
endmethod
method endcheck takes nothing returns nothing
call UnitRemoveAbility(.u,.buffid)
set .u=null
call .destroy()
endmethod
//! runtextmacro CostumMotion("BuffCheckData","check","endcheck","0.5")
endstruct
function DestroyBuffAfterTime takes unit u,integer buffid,real maxtime returns nothing
local BuffCheckData bc
if GetUnitAbilityLevel(u,buffid)>0 then
call UnitRemoveAbility(u,buffid)
else
set bc=BuffCheckData.create()
set bc.u =u
set bc.buffid=buffid
set bc.time =0.
set bc.max =maxtime
call BuffCheckData.addMotion(bc)
endif
endfunction
endlibrary
//================================================================================================
//Loop - Buff Effects
//================================================================================================
//This is just made for this spellpack. If you have questions about this system just ask me.
//! textmacro BuffEffect takes abilityId,periodic,prefix
private struct $prefix$BuffData
static timer loopExe=CreateTimer()
static integer size =0
static integer array executer [maxIndex]
static $prefix$BuffData array runStruct[maxIndex]
unit victim
real duration
real time
static method BuffCheck takes nothing returns nothing
local integer index=0
local integer id
loop
exitwhen index==$prefix$BuffData.size
set id=$prefix$BuffData.executer[index]
set $prefix$BuffData.runStruct[id].time=$prefix$BuffData.runStruct[id].time+$periodic$
if $prefix$BuffData.runStruct[id].time>=$prefix$BuffData.runStruct[id].duration then
call UnitRemoveAbility($prefix$BuffData.runStruct[id].victim,$abilityId$)
call ClearHandleIndex($prefix$BuffData.runStruct[id].victim)
set $prefix$BuffData.runStruct[id].victim=null
call $prefix$BuffData.runStruct[id].destroy()
set $prefix$BuffData.size=$prefix$BuffData.size-1
set $prefix$BuffData.executer[index]=$prefix$BuffData.executer[$prefix$BuffData.size]
elseif not IsUnitNotBuffImmune($prefix$BuffData.runStruct[id].victim) then
call UnitRemoveAbility($prefix$BuffData.runStruct[id].victim,$abilityId$)
call ClearHandleIndex($prefix$BuffData.runStruct[id].victim)
set $prefix$BuffData.runStruct[id].victim=null
call $prefix$BuffData.runStruct[id].destroy()
set $prefix$BuffData.size=$prefix$BuffData.size-1
set $prefix$BuffData.executer[index]=$prefix$BuffData.executer[$prefix$BuffData.size]
else
set index=index+1
endif
endloop
if $prefix$BuffData.size==0 then
call PauseTimer($prefix$BuffData.loopExe)
endif
endmethod
endstruct
private function UnitAdd$prefix$Buff takes unit victim,real duration,integer lvl returns nothing
local integer index
if $prefix$BuffData.size==0 then
call TimerStart($prefix$BuffData.loopExe,$periodic$,true,function $prefix$BuffData.BuffCheck)
endif
if GetUnitAbilityLevel(victim,$abilityId$)>0 then
set index=GetHandleIndex(victim)
call UnitRemoveAbility(victim,$abilityId$)
else
call AddHandleIndex(victim)
set index =GetHandleIndex(victim)
set $prefix$BuffData.runStruct[index] =$prefix$BuffData.create()
set $prefix$BuffData.executer[$prefix$BuffData.size]=index
set $prefix$BuffData.size =$prefix$BuffData.size+1
endif
set $prefix$BuffData.runStruct[index].victim =victim
set $prefix$BuffData.runStruct[index].duration =duration
set $prefix$BuffData.runStruct[index].time =0.
call UnitAddAbility(victim,$abilityId$)
call SetUnitAbilityLevel(victim,$abilityId$,lvl)
endfunction
//! endtextmacro
//================================================================================================
//Loop - Motion System
//================================================================================================
//This is a easy motion system which make some basic motions.
//It's helpful if you don't want to write some stuff again and
//again.
library MotionDatabase initializer init requires MainFunctions
globals
private constant real MinimalChaseRange=10.
public unit tm //Temp Missle
public unit tc //Temp Caster
public unit tv //Temp Victim
public integer tdata //Temp Data
private rect MaxArea =null
public real periodic =0.03
endglobals
struct JumpDatabase
unit u
real distance
real maxdistance
real angle
real speed
real x
real y
real mx
real my
real array z[3]
method motion takes nothing returns nothing
local real curv
local real x
local real y
local real z
set .distance=.distance+.speed
if .distance>.maxdistance then
call SetUnitFlyHeight(.u,GetUnitDefaultFlyHeight(.u),0)
set .active=false
else
set x=.x+.mx
set y=.y+.my
set z=GetZ(x,y)
set curv=GetFlyParabula(.z[1],.z[0],.z[2],.distance/.maxdistance) + (.z[0]-z)
if RectContainsCoords(MaxArea,x,y) then
call SetUnitPosition(.u,x,y)
set .x=x
set .y=y
endif
call SetUnitFlyHeight(.u,curv,0)
endif
endmethod
method endmotion takes nothing returns nothing
set .u=null
call .destroy()
endmethod
//! runtextmacro CostumMotion("JumpDatabase","motion","endmotion","periodic")
endstruct
struct ChaseDatabase
unit u
unit target
unit attacker
real speed
real z
real Rz
string endfunc
integer data
integer loopMember
method motion takes nothing returns nothing
local real Ux =GetUnitX(.u)
local real Uy =GetUnitY(.u)
local real Tx =GetUnitX(.target)
local real Ty =GetUnitY(.target)
local real distance =D2PXY(Ux,Uy,Tx,Ty)
local real angle
if distance>MinimalChaseRange then
set angle=Atan2(Ty-Uy,Tx-Ux)
set Ux=Ux+.speed*Cos(angle)
set Uy=Uy+.speed*Sin(angle)
call SetUnitPosition(.u,Ux,Uy)
call SetUnitFlyHeight(.u,.z,.Rz)
else
set .active=false
endif
endmethod
method endmotion takes nothing returns nothing
set tv =.target
set tm =.u
set tc =.attacker
set tdata=.data
call ExecuteFunc(.endfunc)
set .u =null
set .target =null
set .attacker=null
call .destroy()
endmethod
//! runtextmacro CostumMotion("ChaseDatabase","motion","endmotion","periodic")
endstruct
struct CollisionDatabase
unit u
unit attacker
real speed
real angle
real z
real Rz
real range
real distance
real x
real y
string endfunc
integer data
integer loopMember
method motion takes nothing returns nothing
local unit a
local group g
local integer c
set .distance=.distance-.speed
if .distance>0. then
set .x=.x+.speed*Cos(.angle)
set .y=.y+.speed*Sin(.angle)
call SetUnitPosition(.u,.x,.y)
call SetUnitFlyHeight(.u,.z,.Rz)
set g=GetUnitsInRange(.range,.x,.y)
set c=0
loop
set a=FirstOfGroup(g)
exitwhen a==null
call GroupRemoveUnit(g,a)
if IsUnitNotImmun(.attacker,a) and a!=.u then
set c=c+1
endif
endloop
call GroupClear(g)
set g=null
set .active=c<=0
else
set .active=false
endif
endmethod
method endmotion takes nothing returns nothing
set tc =.attacker
set tm =.u
set tdata=.data
call ExecuteFunc(.endfunc)
set .u =null
set .attacker=null
call .destroy()
endmethod
//! runtextmacro CostumMotion("CollisionDatabase","motion","endmotion","periodic")
endstruct
function LaunchMissileAtPointEx takes unit u,real x1,real y1,real bZ,real maxZ,real x2,real y2,real endz,real speed returns nothing
local JumpDatabase JD =JumpDatabase.create()
set JD.u =u
set JD.z[0] =GetZ(x1,y1)+bZ
set JD.z[1] =maxZ
set JD.z[2] =GetZ(x2,y2)+endz
set JD.x =x1
set JD.y =y1
set JD.speed =speed
set JD.distance =0.
set JD.maxdistance =D2PXY(x1,y1,x2,y2)
set JD.angle =Atan2(y2-y1,x2-x1)
set JD.mx =speed*Cos(JD.angle)
set JD.my =speed*Sin(JD.angle)
call UnitAddFly(u)
call SetUnitX(u,x1)
call SetUnitY(u,y1)
call JumpDatabase.addMotion(JD)
endfunction
function LaunchNormalChaseMissileAtPointEx takes unit attacker,unit missile,real x1,real y1,real z,unit victim,real speed,real Zrate,string colfunc,integer data returns nothing
local ChaseDatabase CD =ChaseDatabase.create()
set CD.u =missile
set CD.z =z
set CD.attacker =attacker
set CD.target =victim
set CD.speed =speed
set CD.endfunc =colfunc
set CD.Rz =Zrate
set CD.data =data
call UnitAddFly(missile)
call SetUnitX(missile,x1)
call SetUnitY(missile,y1)
call ChaseDatabase.addMotion(CD)
endfunction
function LaunchNormalCollisionMissileAtPointEx takes unit attacker,unit missile,real x1,real y1,real z,real x2,real y2,real speed,real range,string colfunc,integer data returns nothing
local CollisionDatabase CD =CollisionDatabase.create()
set CD.u =missile
set CD.attacker =attacker
set CD.z =z
set CD.Rz =0.
set CD.x =x1
set CD.y =y1
set CD.range =range
set CD.speed =speed
set CD.endfunc =colfunc
set CD.distance =D2PXY(x1,y1,x2,y2)
set CD.angle =Atan2(y2-y1,x2-x1)
set CD.data =data
call UnitAddFly(missile)
call SetUnitX(missile,x1)
call SetUnitY(missile,y1)
call CollisionDatabase.addMotion(CD)
endfunction
private function init takes nothing returns nothing
set MaxArea=bj_mapInitialPlayableArea
endfunction
endlibrary
//================================================================================================
//Timer - Timer Recycler
//================================================================================================
//This is a standart timer recycler. It's so fast like TimerUtils and some benchmark tests
//showed that this system seems to be sometimes faster than both TimerUtil versions.
//But try it out yourself.
library TimerRecycler requires MainFunctions,HIS
globals
private constant integer pos =0
private hashtable hs =InitHashtable()
private integer lastsize =0
private timer array last[maxIndex]
endglobals
//! textmacro NextTimer takes name,type,func
function GetNextTimer$name$ takes $type$ dat returns timer
local integer index
local timer temp
if lastsize>0 then
set lastsize=lastsize-1
set temp =last[lastsize]
else
set temp =CreateTimer()
endif
call AddHandleIndex(temp)
call Save$func$(hs,GetHandleIndex(temp),pos,dat)
return temp
endfunction
//! endtextmacro
//! runtextmacro NextTimer("Int","integer","Integer")
//! runtextmacro NextTimer("Agent","agent","AgentHandle")
//! runtextmacro NextTimer("TextTag","texttag","TextTagHandle")
//! runtextmacro NextTimer("Lightning","lightning","LightningHandle")
//! textmacro GetType takes name,type,func
function GetTimerData$name$ takes timer t returns $type$
return Load$func$(hs,GetHandleIndex(t),pos)
endfunction
//! endtextmacro
//! runtextmacro GetType("Int","integer","Integer")
//! runtextmacro GetType("Unit","unit","UnitHandle")
//! runtextmacro GetType("Effect","effect","EffectHandle")
//! runtextmacro GetType("Lightning","lightning","LightningHandle")
//! runtextmacro GetType("TextTag","texttag","TextTagHandle")
function RecycleTimer takes timer t returns nothing
local integer index=GetHandleIndex(t)
call PauseTimer(t)
call ClearHandleIndex(t)
set last[lastsize]=t
set lastsize =lastsize+1
endfunction
endlibrary
//================================================================================================
//Group - Group Recycler
//================================================================================================
//A standart group recycler.
library GroupRecycler requires MainFunctions,HIS
globals
private integer max =0
private integer cmax =0
private group array rGroup
private integer array rInt[maxIndex]
endglobals
function GetNextGroup takes nothing returns group
set cmax=cmax+1
if max<cmax then
set rGroup[cmax]=CreateGroup()
set max =cmax
endif
call AddHandleIndex(rGroup[cmax])
set rInt[GetHandleIndex(rGroup[cmax])]=cmax
return rGroup[cmax]
endfunction
function RecycleGroup takes group g returns nothing
local integer index=GetHandleIndex(g)
call ClearHandleIndex(g)
set rGroup[rInt[index]] =rGroup[cmax]
set rInt[GetHandleIndex(rGroup[cmax])]=rInt[index]
set rGroup[cmax] =g
set cmax=cmax-1
endfunction
endlibrary
//================================================================================================
//Timed - Handle Destroy
//================================================================================================
//Some small useful functions.
library TimedHandleDead requires MainFunctions,TimerRecycler
function U2Death takes nothing returns nothing
local timer t = GetExpiredTimer()
call RemoveUnit(GetTimerDataUnit(t))
call RecycleTimer(t)
set t=null
endfunction
function U2Null takes unit u,real duration returns nothing
local timer t = GetNextTimerAgent(u)
call TimerStart(t,duration,false,function U2Death)
set t = null
endfunction
function E2Death takes nothing returns nothing
local timer t = GetExpiredTimer()
call DestroyEffect(GetTimerDataEffect(t))
call RecycleTimer(t)
set t=null
endfunction
function E2Null takes effect e,real duration returns nothing
local timer t = GetNextTimerAgent(e)
call TimerStart(t,duration,false,function E2Death)
set t = null
endfunction
function L2Death takes nothing returns nothing
local timer t = GetExpiredTimer()
call DestroyLightning(GetTimerDataLightning(t))
call RecycleTimer(t)
set t=null
endfunction
function L2Null takes lightning l,real duration returns nothing
local timer t = GetNextTimerLightning(l)
call TimerStart(t,duration,false,function L2Death)
set t = null
endfunction
function TT2Death takes nothing returns nothing
local timer t = GetExpiredTimer()
call DestroyTextTag(GetTimerDataTextTag(t))
call RecycleTimer(t)
set t=null
endfunction
function TT2Null takes texttag tt,real duration returns nothing
local timer t = GetNextTimerTextTag(tt)
call TimerStart(t,duration,false,function L2Death)
set t = null
endfunction
endlibrary
//================================================================================================
//Passiv - Damage Skill
//================================================================================================
//This system will be useful for skills like my Crush or Holy Shock spell. For more
//informations just ask me.
library DamageSkill initializer init requires MainFunctions,TimerRecycler
globals
private boolexpr array ExecuteFunction[maxIndex]
private unit array Attacker[maxIndex]
private unit array Attacked[maxIndex]
private trigger DamageDetect=null
private trigger EventExecute=null
private group InUseCache =null
private group RegisterCache =null
public unit attacker=null
public unit attacked=null
public real damage =0.
endglobals
function TriggerAddDamageEvent takes unit a,unit b,code func returns nothing
local integer id
if not IsUnitInGroup(a,RegisterCache) then
call TriggerRegisterUnitEvent(DamageDetect,a,EVENT_UNIT_DAMAGED)
call GroupAddUnit(RegisterCache,a)
endif
if not IsUnitInGroup(b,InUseCache) then
call AddHandleIndex(b)
set id=GetHandleIndex(b)
call GroupAddUnit(InUseCache,b)
else
set id=GetHandleIndex(b)
call DestroyBoolExpr(ExecuteFunction[id])
endif
set ExecuteFunction[id]=Filter(func)
set Attacker[id] =b
set Attacked[id] =a
endfunction
private function CheckRegister takes nothing returns boolean
return IsUnitInGroup(GetEventDamageSource(),InUseCache)
endfunction
private function GetDamage takes nothing returns nothing
local integer id =GetHandleIndex(GetEventDamageSource())
local triggercondition tc
set attacker=Attacker[id]
set attacked=Attacked[id]
set damage =GetEventDamage()
set tc=TriggerAddCondition(EventExecute,ExecuteFunction[id])
call TriggerEvaluate(EventExecute)
call TriggerRemoveCondition(EventExecute,tc)
call DestroyBoolExpr(ExecuteFunction[id])
call GroupRemoveUnit(InUseCache,Attacker[id])
call ClearHandleIndex(Attacker[id])
set ExecuteFunction[id]=null
set Attacker[id] =null
set Attacked[id] =null
set tc =null
endfunction
private function init takes nothing returns nothing
set InUseCache =CreateGroup()
set RegisterCache =CreateGroup()
set DamageDetect =CreateTrigger()
set EventExecute =CreateTrigger()
call TriggerAddAction(DamageDetect,function GetDamage)
call TriggerAddCondition(DamageDetect,Condition(function CheckRegister))
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Jade Shield by Hanky aka OrkOfMordor aka MDZ-OrkOfMordor
scope JadeShield initializer init
//===================================================================================================================
//Constants
globals
private constant integer SpellId ='A03A' //Ability Rawcode: Jade Shield
private constant integer DummyId ='e00E' //Unit Rawcode: Dummy
private constant real speed =10. //Speed of rotating missiles
private constant real distance =120. //Distance of rotating missiles to caster
private constant real range =90. //Collision Range of missiles
private constant integer amount =3 //Amount of missiles which rotate around the caster
private constant boolean knockback=true //Enable/Disable small knockback when they collide with the missiles
private constant real knockbackran=20. //Knockback distance when unit collide with missile
private constant string MissileMdl="Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl" //Model of the missiles
private constant string MissileAtt="chest" //Attach point of the MissileMdl
private constant string HitMdl ="Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl" //Model when missiles hit a unit
private constant string HitAtt ="chest" //Attach point of the HitMdl
private constant real periodic =0.03 //The periodic motion time
//Don't change this. This is the key in that your datas will be attached.
private integer array BuffData[maxIndex] //Important attached datas
endglobals
private constant function Damage takes integer lvl returns real
//Damage the victims get when they collide with the missiles
return 100.*lvl+55.
endfunction
private constant function MaxTime takes integer lvl returns real
//Maximal life time of the missiles
return 4.
endfunction
private function UnitFilter takes unit c,unit u returns boolean
//The unit filter for the units who get damage
if IsUnitEnemy(u,GetOwningPlayer(c)) then
if GetUnitState(u,UNIT_STATE_LIFE)>0. then
if IsUnitType(u,UNIT_TYPE_FLYING)==false then
if GetUnitAbilityLevel(u,invulnerable_id)<=0 then
if IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)==false then
return IsUnitType(u,UNIT_TYPE_STRUCTURE)==false
endif
endif
endif
endif
endif
return false
endfunction
//End Constants
//===================================================================================================================
//Conditions
private function Jade_Shield_Conditions takes nothing returns boolean
return GetSpellAbilityId() == SpellId
endfunction
//Actions
private struct JadeShieldDatas
unit caster=null
unit array missile[amount]
effect array gfx[amount]
real angle
real angledistance
real time
integer lvl
method motion takes nothing returns nothing
local real ux =GetUnitX(.caster)
local real uy =GetUnitY(.caster)
local real x
local real y
local real dmg =Damage(.lvl)
local group g
local unit a
local real angle =.angle
local real direc
local real ax
local real ay
local integer i =1
local integer f =0
loop
exitwhen i>amount
if .gfx[i-1]!=null then
set x=ux+distance*Cos(angle*deg2rad)
set y=uy+distance*Sin(angle*deg2rad)
call SetUnitPosition(.missile[i-1],x,y)
call SetUnitFacing(.missile[i-1],angle+90.)
set g=GetUnitsInRange(range,x,y)
call GroupRemoveUnit(g,.missile[i-1])
loop
set a=FirstOfGroup(g)
exitwhen a==null or .gfx[i-1]==null
call GroupRemoveUnit(g,a)
if UnitFilter(.caster,a) then
if knockback then
set ax =GetUnitX(a)
set ay =GetUnitY(a)
set direc=Atan2(ay-uy,ax-ux)
call SetUnitPosition(a,GetUnitX(a)+knockbackran*Cos(direc),GetUnitY(a)+knockbackran*Sin(direc))
endif
call DestroyEffect(AddSpecialEffectTarget(HitMdl,a,HitAtt))
call UnitDamageTarget(.caster,a,dmg,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
call DestroyEffect(.gfx[i-1])
call U2Null(.missile[i-1],0.5)
set .gfx[i-1]=null
endif
endloop
set g=null
else
set f=f+1
endif
set angle=angle+.angledistance
set i=i+1
endloop
set .active=.time<=MaxTime(.lvl) and f<amount and GetUnitState(.caster,UNIT_STATE_LIFE)>0.
set .time =.time+periodic
set .angle=.angle+speed
endmethod
method endmotion takes nothing returns nothing
local integer i=0
loop
exitwhen i==amount
if .gfx[i]!=null then
call DestroyEffect(.gfx[i])
call U2Null(.missile[i],0.5)
set .gfx[i] =null
set .missile[i]=null
endif
set i=i+1
endloop
call ClearHandleIndex(.caster)
set .caster =null
call .destroy()
endmethod
//! runtextmacro CostumMotion("JadeShieldDatas","motion","endmotion","periodic")
endstruct
private function Jade_Shield_Actions takes nothing returns nothing
local unit u =GetTriggerUnit()
local real angle
local real x =GetUnitX(u)
local real y =GetUnitY(u)
local integer i =0
local integer d =GetHandleIndex(u)
local JadeShieldDatas dat=BuffData[d]
if dat.active and dat.caster==u then
set dat.active=false
endif
set dat =JadeShieldDatas.create()
set dat.caster =u
set dat.angledistance=360./amount
set dat.angle =GetRandomReal(0.,360.)
set angle =dat.angle
set dat.lvl =GetUnitAbilityLevel(u,SpellId)
set dat.time =0.
loop
exitwhen i==amount
set dat.missile[i]=CreateUnit(Player(14),DummyId,x+distance*Cos(angle*deg2rad),y+distance*Sin(angle*deg2rad),angle-45)
set dat.gfx[i] =AddSpecialEffectTarget(MissileMdl,dat.missile[i],MissileAtt)
set angle=angle+dat.angledistance
set i=i+1
endloop
call AddHandleIndex(u)
set d=GetHandleIndex(u)
set BuffData[d]=dat
call JadeShieldDatas.addMotion(dat)
set u=null
endfunction
//===========================================================================
private function init takes nothing returns nothing
local integer i=0
set gg_trg_Jade_Shield = CreateTrigger( )
loop
call TriggerRegisterPlayerUnitEvent(gg_trg_Jade_Shield, Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, MainFunctions_filter)
set i=i+1
exitwhen i==bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(gg_trg_Jade_Shield, Condition( function Jade_Shield_Conditions ) )
call TriggerAddAction(gg_trg_Jade_Shield, function Jade_Shield_Actions )
//Preload
call Preload(MissileMdl)
call Preload(HitMdl)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Electrical Field made by OrkOfMordor aka Hanky aka MDZ-OrkOfMordor
scope ElectricFlield initializer init
//===================================================================================================================
//Constants
globals
private constant integer SpellId ='A03C' //Ability Rawcode: Electric Field
private constant integer BuffSpellId ='A03D' //Ability Rawcode: Electric Field - Explosion
private constant integer MaxMissiles =6 //The amount of the missiles
private constant real MaxTime =1.75 //The maximal time of the throw
private constant real MaxRange =250.00 //The maximal area where the missiles can be thrown
private constant integer DummyId ='e00E' //Unit Rawcode: Dummy
private constant string MissileMdl ="Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl" //The missile model
private constant string ExplosionMdlA ="Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
private constant string ExplosionMdlB ="Abilities\\Spells\\Other\\Silence\\SilenceAreaBirth.mdl"
private constant string MissileAttachPoint ="origin"//The attach point of MissileMdl
private constant real MissileDamageRange =120.00 //The range of the damage
endglobals
private function EF_MissileFlyHeight takes nothing returns real
//Random fly height of the missiles
return GetRandomReal(500,700)
endfunction
private constant function EF_Damage takes integer lvl returns real
//Damage of the explosion
return (75.*lvl)+150.
endfunction
private constant function EF_BuffDuration takes integer lvl returns real
//The duration of the slow buff
return 3.
endfunction
private function UnitFilter takes unit c,unit u returns boolean
//The unit filter for the units who get damage
if IsUnitEnemy(u,GetOwningPlayer(c)) then
if GetUnitState(u,UNIT_STATE_LIFE)>0. then
if IsUnitType(u,UNIT_TYPE_FLYING)==false then
if GetUnitAbilityLevel(u,invulnerable_id)<=0 then
if IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)==false then
return IsUnitType(u,UNIT_TYPE_STRUCTURE)==false
endif
endif
endif
endif
endif
return false
endfunction
//End Constants
//===================================================================================================================
//Conditions
private function Electric_Field_Conditions takes nothing returns boolean
return GetSpellAbilityId() == SpellId
endfunction
//Actions
//! runtextmacro BuffEffect("BuffSpellId","0.5","")
private struct ElectroBombs
unit caster
unit missile
effect gfx
integer lvl
real dur
real dmg
method onDestroy takes nothing returns nothing
call DestroyEffect(.gfx)
set .missile=null
set .caster =null
set .gfx =null
endmethod
endstruct
private function ElectroFlieldExpl takes nothing returns nothing
local timer t =GetExpiredTimer()
local ElectroBombs dat =GetTimerDataInt(t)
local real x =GetUnitX(dat.missile)
local real y =GetUnitY(dat.missile)
local unit a
local group g
set g=GetUnitsInRange(MissileDamageRange,GetUnitX(dat.missile),GetUnitY(dat.missile))
loop
set a=FirstOfGroup(g)
exitwhen a==null
call GroupRemoveUnit(g,a)
if UnitFilter(dat.caster,a) then
call UnitAddBuff(a,dat.dur,dat.lvl)
call UnitDamageTarget(dat.caster,a,dat.dmg,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
endif
endloop
call DestroyEffect(AddSpecialEffect(ExplosionMdlA,x,y))
call DestroyEffect(AddSpecialEffect(ExplosionMdlB,x,y))
call U2Null(dat.missile,0.5)
call dat.destroy()
call RecycleTimer(t)
set g=null
set t=null
endfunction
private function Electric_Field_Actions takes nothing returns nothing
local timer t
local real randomA =0
local real randomB =0
local real randomY =0
local real randomX =0
local integer int =1
local ElectroBombs dat
local unit u =GetTriggerUnit()
local integer lvl =GetUnitAbilityLevel(u,SpellId)
local real x =GetUnitX(u)
local real y =GetUnitY(u)
local real Rx =GetSpellTargetX()
local real Ry =GetSpellTargetY()
local real dmg =EF_Damage(lvl)
local real dur =EF_BuffDuration(lvl)
local player p =GetOwningPlayer(u)
loop
exitwhen int>MaxMissiles
set randomA =GetRandomReal(0,360)
set randomB =GetRandomReal(0,MaxRange)
set randomX =Rx+randomB*Cos(randomA*deg2rad)
set randomY =Ry+randomB*Sin(randomA*deg2rad)
set dat =ElectroBombs.create()
set dat.caster =u
set dat.missile=CreateUnit(p,DummyId,x,y,0)
set dat.lvl =lvl
set dat.dur =dur
set dat.gfx =AddSpecialEffectTarget(MissileMdl,dat.missile,MissileAttachPoint)
set dat.dmg =dmg
set t =GetNextTimerInt(dat)
call LaunchMissileAtPointEx(dat.missile,x,y,45,EF_MissileFlyHeight(),randomX,randomY,0.,D2PXY(x,y,randomX,randomY)/MaxTime*MotionDatabase_periodic)
call TimerStart(t,MaxTime,false,function ElectroFlieldExpl)
set int=int+1
endloop
set u =null
set p =null
set t =null
endfunction
//===========================================================================
private function init takes nothing returns nothing
local integer i=0
local unit upre
set gg_trg_Electric_Field = CreateTrigger( )
loop
call TriggerRegisterPlayerUnitEvent(gg_trg_Electric_Field, Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, MainFunctions_filter)
set i=i+1
exitwhen i==bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition( gg_trg_Electric_Field, Condition( function Electric_Field_Conditions ) )
call TriggerAddAction( gg_trg_Electric_Field, function Electric_Field_Actions )
//Preload Buffs
set upre=CreateUnit(Player(14),DummyId,PreX,PreY,0)
call UnitAddAbility(upre,BuffSpellId)
call RemoveUnit(upre)
set upre=null
//Preload Models
call Preload(MissileMdl)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
/////////////////////////////////////////////////////////////////////////////////////////////////
// Mind Burst 1.04b (1.24 compatible)
// Created by JonNny
//
// ° Mapmaking since 2006...
// °
// °
// °
// °°°ÛÜ
// °°X°ÛÛÛ ÛÛÛÛÛÛÛÛ ÿÜÛÛÛÛÛÛÛÛÜ
// °°XX° ÛÛÛ ßÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °XX ° ÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °XX ° ÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °°XXX°° ÛÛÛÛÛÛÜ ÛÛÛÜ ÛÛÛÛÛ ßÛÛÛÛÛ ßÛÛÛÛ ÛÛÛÛÛÛ
// °°°°°°°° ÛÛÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛÜ
// ÛÛÛÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛÛÛÜ ÛÛÛÛ
// °°°°°°°° ÛÛÛÛÛÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛÜ ÛÛÛÛÛÛÛÛ ÛÛÛÛÛÜ ÜÛÛÛÛÛÛ
// °°XXXXX° ÛÛÛÛ ÛÛÛÜ ÛÛÛÛÛ ÛÛÛÛÛÛÜ ßÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °°XXXXX °° ÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛ °°°ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ°°° ÛÛÛÛÛÛÛÛÛÛÛÛÛß
// ° XXXXX °°ÛÛÛÛÛ°°°°ÛÛÛÛÛÛÛÛ°°XX ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ XXX°°ÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛ
// °° XXXXX ÜÛÛÛÛÛÛ X ÛÛÛÛÛÛÛÛÛ XXX ÛÛÛÛÛÛÛÛÛÛÛ XXXXXX ÛÛÛÛÛÛÛ° ÛÛÛ
// °° XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX °°°°°° °°°°
// °° XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX °°°°°X°°
// °°° XXXXXXXXXX °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° XXXXXXXXXXXXXXXXX°°
// °°°°°°°°°°°°°°°° °°°°°°°° XXXXXXX °°°
// °°°°°° XX °°
// ...2009 and still moving on. °°°°°°°
// °°
//
// What do you need to import this Spell:
// -Your Ability
// -A missile dummy
// -A Spell for the Buff
// -A simple dummy to cast the Buff
//
//
//1. Create all needed things mentioned above.
//2. Copy the code into your map (also GroupUtils if it does not already exist)
//3. Adjust all globals matching to your created stuff :
// line 53 - Your missile dummys Rawcode;
// line 54 - Your Spells Rawcode
// line 55 - Your cast dummys Rawcode
// line 56 - Your Effect Spells Rawcode
// line 59 - The Order ID of the Effect Spell
//
// After that you should be done :)
// Thanks to : Hanky for helping to improve this script , Rising_Dusk for Feedback
//////////////////////////////////////Globals with important values////////////////////////////////////////
scope MindBurst initializer Init
globals
//Rawcodes
private constant integer DummyID = 'h011' //The Missile dummy ID (Rawcode)
private constant integer MindBurstSpell = 'A044' //Spell ID (Rawcode)
private constant integer CastDummyID = 'h01L' //A normal dummy to cast the effect (Rawcode)
private constant integer MindBurstEffectSpell = 'A03R' //The Spell which is casted on each hit enemy (Rawcode)
//Spell Related Constants
private constant string EffectOrderID = "rejuvination" //Order ID of the Spell(Mind Burst Buff Effect) effect whih should last after being hit
private constant real MissileSpeed = 11 //Defines how fast the missiles are
private constant real TimerPeriod = 0.0275 //How fast the execution is
private constant real MissileHeight = 80 // Defines the missiles basic height (they spin arround this) should be bigger than SpinStarRadius that they do not touch the ground
private constant real SpinStartRadius = 60 // Sets the spin radius when they are created , why moving it decreases (look below)
private constant real SpinEndRadius = 10 // Sets the spin radius the missiles got at the end
private constant real StartScale = 0.85 // While moving missiles change their scale , this is their start scale (look below)
private constant real EndScale = 0.275 // This is the missiles end scale
private constant real SpinRate = 0.275 // The rate the missiles spin
private constant real AdditionalDamageRange = 50 //The additional range for hitting a target , the real range will be the current Spinradius + This value (this case its 110 at the start and 60 at the end)
private constant real ReleaseDistance = 20.00 // The Distance the missiled being released infront of the caster
//Effects
private constant string DamageEffect = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl" // Special effect when hitting an enemy
private constant string HealEffect = "Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl" // Special effect at the caster when being heald (missiles hit a unit with mana)
private constant string MissilesDeath = "Abilities\\Spells\\Orc\\LightningBolt\\LightningBoltMissile.mdl" //Special effect whe the missiles are removed
//System Variables , Do not change
private timer t = CreateTimer()
private boolexpr filter = null
private unit Dummy = null
private group ENUM_GROUP = CreateGroup()
endglobals
///////////////////////////////////Defining most relevant Values////////////////////////////////////////////////
private constant function MissileRangePerLevel takes real level returns real
// The range the missiles reach per level
return 550 + 225 * level
endfunction
private constant function StolenManaPerLevel takes integer level returns real
// Amount of mana stolen directly and transfered to the caster
return 20.0 + 20.0*level
endfunction
private constant function DamagePerLevel takes integer level returns real
//Amount of damage dealt when a unit is hit
return 100.00 + level * 30.00
endfunction
private function DealDamageCondition takes unit enemy , unit caster returns boolean
// The condition if a unit is damaged
return IsPlayerEnemy(GetOwningPlayer(enemy),GetOwningPlayer(caster)) // The condition if a unit is damaged
endfunction
private function ManaDrainCondition takes unit a returns boolean
// Additional condition (including DealDamageCondition) if the caster can directly steal mana
return (GetUnitState(a,UNIT_STATE_MANA) > 0.0)
endfunction
private function RunCondition takes nothing returns boolean
return GetSpellAbilityId() == MindBurstSpell
// The condition if the spell is casted
endfunction
private function UnitFilter takes nothing returns boolean
local boolean ret = false
if not(IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL)) and not(IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)) then
set ret = IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endif
return ret
endfunction
///////////////////////////////////Main Struckt//Do not change//////////////////////////////////////////////
private struct Missiles
static integer Index = 0
static Missiles array Data
unit array missiles [2] // The 2 missiles
real angle
real x
real y
real a
unit caster
group DamagedUnits // group to avoid damaging a unit twice
real rangeleft
integer level
static method create takes nothing returns Missiles
local Missiles data = Missiles.allocate()
set Missiles.Data[Missiles.Index] = data
set Missiles.Index = Missiles.Index + 1
return data
endmethod
endstruct
////////////////////////////////Main script// Do not edit unless you understand it////////////////////////////////
private function callback takes nothing returns nothing
local Missiles data
local integer i = 0
local real x = 0.00
local real y = 0.00
local real z = 0
local integer a = 0
local unit b = null
local unit dummy = null
local real radius
local real scale
// looping through all structs
loop
exitwhen i >= Missiles.Index
set data = Missiles.Data[i]
set a = 0
set z = data.rangeleft / MissileRangePerLevel(data.level)
set scale = EndScale+(StartScale-EndScale)*z
set radius = SpinEndRadius+(SpinStartRadius-SpinEndRadius)*z
// Moving the missiles and setting their scale ect. depending on the left range
call SetUnitX(data.missiles[0], data.x + MissileSpeed * Cos(data.angle) + Cos(data.angle+1.5708) * Cos(data.a) * radius)
call SetUnitY(data.missiles[0], data.y + MissileSpeed * Sin(data.angle) + Sin(data.angle+1.5708) * Cos(data.a) * radius)
call SetUnitFlyHeight(data.missiles[0], MissileHeight + Cos(data.a+1.5708) * radius, 1000)
call SetUnitScale(data.missiles[0],scale,scale,scale)
call SetUnitX(data.missiles[1], data.x + MissileSpeed * Cos(data.angle) + Cos(data.angle+1.5708) * Cos(data.a+3.14159) * radius)
call SetUnitY(data.missiles[1], data.y + MissileSpeed * Sin(data.angle) + Sin(data.angle+1.5708) * Cos(data.a+3.14159) * radius)
call SetUnitFlyHeight(data.missiles[1], MissileHeight + Cos(data.a-1.5708) * radius, 1000)
call SetUnitScale(data.missiles[1],scale,scale,scale)
set data.x = data.x + MissileSpeed * Cos(data.angle)
set data.y = data.y + MissileSpeed * Sin(data.angle)
set data.a = data.a + SpinRate
set data.rangeleft = data.rangeleft - MissileSpeed
set a = 0
// looping through all units in range
call GroupEnumUnitsInRange(ENUM_GROUP, data.x, data.y,radius + AdditionalDamageRange, filter)
loop
set b = FirstOfGroup(ENUM_GROUP)
exitwhen b==null
call GroupRemoveUnit(ENUM_GROUP,b)
//checking if they are alrady damaged
if IsUnitInGroup(b,data.DamagedUnits) == false and DealDamageCondition(b,data.caster) then
//damaging + casting buff
call GroupAddUnit(data.DamagedUnits,b)
call UnitDamageTarget(data.caster,b,DamagePerLevel(data.level),true, false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
call DestroyEffect(AddSpecialEffectTarget(DamageEffect, b, "chest") )
call SetUnitX(Dummy,GetUnitX(b))
call SetUnitY(Dummy,GetUnitY(b))
call SetUnitAbilityLevel(Dummy,MindBurstEffectSpell,data.level)
call IssueTargetOrder( Dummy, EffectOrderID,b )
// Checking if unit has mana to steal it
if ManaDrainCondition(b) then
call DestroyEffect(AddSpecialEffectTarget(HealEffect, data.caster, "origin") )
if (GetUnitState(b,UNIT_STATE_MANA) > StolenManaPerLevel(data.level)) then
call SetUnitState(b, UNIT_STATE_MANA,GetUnitState(b,UNIT_STATE_MANA)-StolenManaPerLevel(data.level))
call SetUnitState(data.caster, UNIT_STATE_MANA,GetUnitState(data.caster,UNIT_STATE_MANA)+StolenManaPerLevel(data.level))
else
call SetUnitState(data.caster, UNIT_STATE_MANA,GetUnitState(data.caster,UNIT_STATE_MANA)+GetUnitState(b,UNIT_STATE_MANA))
call SetUnitState(b, UNIT_STATE_MANA,0)
endif
endif
endif
endloop
call GroupClear(ENUM_GROUP)
// Checking if the missiles reached their end point
if data.rangeleft < 0 then
call DestroyEffect(AddSpecialEffect(MissilesDeath,data.x,data.y) )
// destryoing the missiles
call RemoveUnit(data.missiles[0])
set data.missiles[0] = null
call RemoveUnit(data.missiles[1])
set data.missiles[1] = null
call DestroyGroup(data.DamagedUnits)
set data.DamagedUnits = null
set data.caster = null
call Missiles.destroy(data)
set Missiles.Data[i] = Missiles.Data[Missiles.Index - 1]
set i = i - 1
set Missiles.Index = Missiles.Index - 1
endif
set i = i + 1
endloop
set b = null
set dummy = null
endfunction
private function Actions takes nothing returns nothing
local integer i = Missiles.Index
local unit caster = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(caster,MindBurstSpell)
local real angle = Atan2(GetLocationY(GetSpellTargetLoc()) - GetUnitY(caster), GetLocationX(GetSpellTargetLoc()) - GetUnitX(caster))
local real x = GetUnitX(caster) + ReleaseDistance * Cos(angle)
local real y = GetUnitY(caster) + ReleaseDistance * Sin(angle)
// Creating the Struct
call Missiles.create()
set Missiles.Data[i].caster = caster
set Missiles.Data[i].angle = angle
set Missiles.Data[i].rangeleft = MissileRangePerLevel(level)
set Missiles.Data[i].level = level
set Missiles.Data[i].x = x
set Missiles.Data[i].y = y
set Missiles.Data[i].DamagedUnits = CreateGroup()
set Missiles.Data[i].missiles[0] = CreateUnit(GetOwningPlayer(caster), DummyID , x , y , angle * 57.296)
set Missiles.Data[i].missiles[1] = CreateUnit(GetOwningPlayer(caster), DummyID , x , y , angle * 57.296)
if Missiles.Data[i].Index == 1 then
call TimerStart(t, TimerPeriod, true, function callback)
endif
set caster = null
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger tr = CreateTrigger()
set filter = Filter(function UnitFilter)
set Dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),CastDummyID,0,0,0)
call UnitAddAbility(Dummy,MindBurstEffectSpell)
call TriggerRegisterAnyUnitEventBJ(tr,EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddCondition( tr,Condition( function RunCondition ) )
call TriggerAddAction( tr, function Actions )
//Preloading the used effect models
call Preload(DamageEffect)
call Preload(HealEffect)
call Preload(MissilesDeath)
endfunction
endscope
//TESH.scrollpos=67
//TESH.alwaysfold=0
*Spell Name: Acid Grenade [ GUI MUI ] v1.1 = Removed some stuffs and merged some.
*Spell Author/Creator: by jakeZinc.
*Spell Version: v1.1 = * Removed some unneeded variables, used local variables,
* Global variable are now reduced
* Merged the variable creator and configuration trigger.
*Spell Purpose: It can be used in modern maps, this is acid grenade that will
bring acidics in the enemy armies. It is greatly reducing their
armors and slowing them. It is AoE Spell so it will reduce some enemies at
the same time.
*Spell Idea: None
*Spell Requested: None
*Spell Features v1.1 = * Multi- Unit Instanceabilty ( MUI Spell )
* Lagless
* Leakless ( Expecting Fixes )
* Configurable
How to Import/Install:
=Preparing for import=
Go to:
File
Preferences
General
Check the box labelled ''Automatically create unknown variables while pasting trigger data''
=Speeding up import of object data-
Go to:
Window
make sure the 'brush list' is unchecked.
You can swap between my map and your map.
=Main importing=
Difficuly in Importing:
"Easy" = 1 to 3 Copy - Paste Required
"Medium" = 4 to 6 Copy - Paste Required
"Hard" = 7 above, Copy - Paste Required
Spell Importing Difficulty: 'Medium'
In order of importing: Copy --> Paste to your Map.
Object Data (OE) - Spell - Acid Grenade
Object Data (OE) - Spell - Acid Grenade ( Dummy Ability )
Object Data (OE) - Buff - Acid Grenade
Object Data (OE) - Unit - Acid Grenade ( Dummy ).
Trigger Data (TE) - Folder - Acid Grenade [ GUI ] v1.1.
Import ( F12 ) Dummy.mdl by Vexorian
* Notes:
Note #1: Make sure to Configure the " AG Configuration " trigger in the AG_Ability, AG_DummyType, AG_DummyAbility
Note #2: If the spell give you error about " X, Y, MX and MY thing " pls copy the " AG Variable Creator " trigger.
Note #3: Go to OE ( Object Editor ) --> Abilities --> Acid Grenade ( Dummy Ability ), Set the buff of the Acid
Grenade ( Dummy Ability ) to Acid Grenade Buff, you must done this because it is important.
Note #4: After you import the spell to your map then click the save button in your map then delete the
"AG Variable Creator"
Note #5: If the spell is now working without issue then go to Note #6
Note #6: ~~Enjoy the Spell!!! ( you can delete the README File after this )
=Modification=
All Modifications should be done in the Configuration Trigger, each configuration is explained and elaborated.
//==========================================================================================================================================
Pls Give credits if you use this spell in your map.
Credits: emjl3r ( For the Spell Map Template )
Credits: Vexorian ( Dummy.mdl )
Sincerely,
jakeZinc
library NewBonus
/* ----------------------- NewBonus v1.6 by Chopinski ----------------------- */
//! novjass
Since ObjectMerger is broken and we still have no means to edit
bonus values (green values) i decided to create a light weight
Bonus library that works in the same way that the original Bonus Mod
by Earth Fury did. NewBonus requires patch 1.30+.
How it works?
By using the new Object API recently introduced to warcraft, we can now
modify an ability field, so when giving an unit a bonus, we add a specific
ability to that unit, if it do not have it already, and change that ability
bonus value to the desired value. Retrieving the amount is also trivial, by
just reading that field value.
How to Import?
Importing bonus mod is really simple. Just copy the 9 abilities with the
prefix "NewBonus" from the Object Editor into your map and match their new raw
code to the bonus types in the global block below. Then create a trigger called
NewBonus, convert it to custom text and paste this code there. You done!
API:
function GetUnitBonusEx takes unit u, integer bonus_type returns integer
-> Returns the specified bonus amount for the unit
-> Example: set amount = GetUnitBonusEx(GetTriggerUnit(), BONUS_AGILITY)
function SetUnitBonusEx takes unit u, integer bonus_type, integer amount returns integer
-> Set the specified bonus type to amount for the unit
-> Example: call SetUnitBonusEx(GetTriggerUnit(), BONUS_DAMAGE, 100)
function RemoveUnitBonusEx takes unit u, integer bonus_type returns nothing
-> Removes the Specified bonus type from unit
-> Example: call RemoveUnitBonusEx(GetTriggerUnit(), BONUS_ATTACKSPEED)
function AddUnitBonusEx takes unit u, integer bonus_type, integer amount returns integer
-> Add the specified amount for the specified bonus tyte for unit
-> Example: call AddUnitBonusEx(GetTriggerUnit(), BONUS_DAMAGE, 100)
Added in version 1.5:
Upper and Lower limit to max bonus amount set to 2147483647 and -2147483648
to avoid over/under flowing the integer fields. AddUnitBonusEx()
now returns the amount of bonus that was actually setted. In case of no over/under flow,
it returns the amount passed, in case of over/under flow it returns the amount that
was allowed to be setted. It is still up to the user to call the functions without
passing integers that already over/under flown.
Credits to Earth Fury for the original Bonus idea
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
globals
//The bonus types
constant integer BONUS_DAMAGE = 1
constant integer BONUS_ARMOR = 2
constant integer BONUS_AGILITY = 3
constant integer BONUS_STRENGTH = 4
constant integer BONUS_INTELLIGENCE = 5
constant integer BONUS_HEALTH = 6
constant integer BONUS_MANA = 7
constant integer BONUS_HEALTHREGEN = 8
constant integer BONUS_MANAREGEN = 9
constant integer BONUS_ATTACKSPEED = 10
constant integer BONUS_MOVEMENTSPEED = 11
//The abilities codes for each bonus
//When pasting the abilities over to your map
//their raw code should match the bonus here
private constant integer DAMAGE_ABILITY = 'A04U'
private constant integer ARMOR_ABILITY = 'A04R'
private constant integer STATS_ABILITY = 'A04P'
private constant integer HEALTH_ABILITY = 'A04V'
private constant integer MANA_ABILITY = 'A04Z'
private constant integer HEALTHREGEN_ABILITY = 'A053'
private constant integer MANAREGEN_ABILITY = 'A054'
private constant integer ATTACKSPEED_ABILITY = 'A04T'
private constant integer MOVEMENTSPEED_ABILITY = 'A050'
//The abilities fields that are modified. For the sake of readability
private constant abilityintegerlevelfield DAMAGE_FIELD = ABILITY_ILF_ATTACK_BONUS
private constant abilityintegerlevelfield ARMOR_FIELD = ABILITY_ILF_DEFENSE_BONUS_IDEF
private constant abilityintegerlevelfield AGILITY_FIELD = ABILITY_ILF_AGILITY_BONUS
private constant abilityintegerlevelfield STRENGTH_FIELD = ABILITY_ILF_STRENGTH_BONUS_ISTR
private constant abilityintegerlevelfield INTELLIGENCE_FIELD = ABILITY_ILF_INTELLIGENCE_BONUS
private constant abilityintegerlevelfield HEALTH_FIELD = ABILITY_ILF_MAX_LIFE_GAINED
private constant abilityintegerlevelfield MANA_FIELD = ABILITY_ILF_MAX_MANA_GAINED
private constant abilityintegerlevelfield MOVEMENTSPEED_FIELD = ABILITY_ILF_MOVEMENT_SPEED_BONUS
private constant abilityreallevelfield HEALTHREGEN_FIELD = ABILITY_RLF_AMOUNT_OF_HIT_POINTS_REGENERATED
private constant abilityreallevelfield MANAREGEN_FIELD = ABILITY_RLF_AMOUNT_REGENERATED
private constant abilityreallevelfield ATTACKSPEED_FIELD = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1
endglobals
struct NewBonus
//SetUnitAbilityBonusI() and SetUnitAbilityBonusR are necessary to manage abilities that have integer fields and real fields
//but the return is normalized to reals
static method SetUnitAbilityBonusI takes unit u, integer abilCode, abilityintegerlevelfield field, integer amount returns integer
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
endif
//Increasing and Decreasing is necessary since the abilities do not get updated just by changing
//it's fields values. In the future, if Blizzard decides to patch it, it could be removed.
if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), field, 0, amount) then
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
else
//I will disable the errror message for now.
//call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Could not set the ability field")
endif
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), field, 0)
endmethod
static method SetUnitAbilityBonusR takes unit u, integer abilCode, abilityreallevelfield field, real amount returns real
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
endif
if BlzSetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), field, 0, amount) then
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
else
//I will disable the errror message for now.
//call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Could not set the ability field")
endif
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), field, 0)
endmethod
static method GetUnitBonus takes unit u, integer bonus_type returns integer
if bonus_type == BONUS_DAMAGE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, DAMAGE_ABILITY), DAMAGE_FIELD, 0)
elseif bonus_type == BONUS_ARMOR then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, ARMOR_ABILITY), ARMOR_FIELD, 0)
elseif bonus_type == BONUS_HEALTH then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, HEALTH_ABILITY), HEALTH_FIELD, 0)
elseif bonus_type == BONUS_MANA then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MANA_ABILITY), MANA_FIELD, 0)
elseif bonus_type == BONUS_AGILITY then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), AGILITY_FIELD, 0)
elseif bonus_type == BONUS_STRENGTH then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), STRENGTH_FIELD, 0)
elseif bonus_type == BONUS_INTELLIGENCE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), INTELLIGENCE_FIELD, 0)
elseif bonus_type == BONUS_MOVEMENTSPEED then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MOVEMENTSPEED_ABILITY), MOVEMENTSPEED_FIELD, 0)
elseif bonus_type == BONUS_HEALTHREGEN then
return R2I(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, HEALTHREGEN_ABILITY), HEALTHREGEN_FIELD, 0))
elseif bonus_type == BONUS_MANAREGEN then
return R2I(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, MANAREGEN_ABILITY), MANAREGEN_FIELD, 0))
elseif bonus_type == BONUS_ATTACKSPEED then
return R2I(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, ATTACKSPEED_ABILITY), ATTACKSPEED_FIELD, 0)*100)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1
endmethod
static method SetUnitBonus takes unit u, integer bonus_type, integer amount returns integer
if bonus_type == BONUS_DAMAGE then
return SetUnitAbilityBonusI(u, DAMAGE_ABILITY, DAMAGE_FIELD, amount)
elseif bonus_type == BONUS_ARMOR then
return SetUnitAbilityBonusI(u, ARMOR_ABILITY, ARMOR_FIELD, amount)
elseif bonus_type == BONUS_HEALTH then
call BlzSetUnitMaxHP(u, (BlzGetUnitMaxHP(u) + amount - GetUnitBonus(u, bonus_type)))
return SetUnitAbilityBonusI(u, HEALTH_ABILITY, HEALTH_FIELD, amount)
elseif bonus_type == BONUS_MANA then
call BlzSetUnitMaxMana(u, (BlzGetUnitMaxMana(u) + amount - GetUnitBonus(u, bonus_type)))
return SetUnitAbilityBonusI(u, MANA_ABILITY, MANA_FIELD, amount)
elseif bonus_type == BONUS_AGILITY then
return SetUnitAbilityBonusI(u, STATS_ABILITY, AGILITY_FIELD, amount)
elseif bonus_type == BONUS_STRENGTH then
return SetUnitAbilityBonusI(u, STATS_ABILITY, STRENGTH_FIELD, amount)
elseif bonus_type == BONUS_INTELLIGENCE then
return SetUnitAbilityBonusI(u, STATS_ABILITY, INTELLIGENCE_FIELD, amount)
elseif bonus_type == BONUS_MOVEMENTSPEED then
return SetUnitAbilityBonusI(u, MOVEMENTSPEED_ABILITY, MOVEMENTSPEED_FIELD, amount)
elseif bonus_type == BONUS_HEALTHREGEN then
return R2I(SetUnitAbilityBonusR(u, HEALTHREGEN_ABILITY, HEALTHREGEN_FIELD, amount))
elseif bonus_type == BONUS_MANAREGEN then
return R2I(SetUnitAbilityBonusR(u, MANAREGEN_ABILITY, MANAREGEN_FIELD, amount))
elseif bonus_type == BONUS_ATTACKSPEED then
return R2I(SetUnitAbilityBonusR(u, ATTACKSPEED_ABILITY, ATTACKSPEED_FIELD, amount*0.01)*100)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1
endmethod
static method AddUnitBonus takes unit u, integer bonus_type, integer amount returns integer
local integer current_amount = GetUnitBonus(u, bonus_type)
// Added in version 1.5 to avoid overflow/underflow of the field value
if amount > 0 and current_amount > 2147483647 - amount then //Overflow
set amount = 2147483647 - current_amount
elseif amount < 0 and current_amount < -2147483648 - amount then //Underflow
set amount = -2147483648 - current_amount
endif
call SetUnitBonus(u, bonus_type, (current_amount + amount))
return amount
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS Public API */
/* -------------------------------------------------------------------------- */
function GetUnitBonusEx takes unit u, integer bonus_type returns integer
return NewBonus.GetUnitBonus(u, bonus_type)
endfunction
function SetUnitBonusEx takes unit u, integer bonus_type, integer amount returns integer
return NewBonus.SetUnitBonus(u, bonus_type, amount)
endfunction
function RemoveUnitBonusEx takes unit u, integer bonus_type returns nothing
call NewBonus.SetUnitBonus(u, bonus_type, 0)
endfunction
function AddUnitBonusEx takes unit u, integer bonus_type, integer amount returns integer
return NewBonus.AddUnitBonus(u, bonus_type, amount)
endfunction
endlibrary
library NewBonusUtils requires NewBonus
/* ----------------------- NewBonusUtils v1.6 by Chopinski ----------------------- */
//! novjass
Utility functions that take advantage of the NewBonus library.
API:
function AddUnitBonusTimed takes unit u, integer bonus_type, integer amount, real duration returns nothing
-> Add the specified amount for the specified bonus type for unit for a duration
-> Example: call AddUnitBonusTimed(GetTriggerUnit(), BONUS_ARMOR, 13, 10.5)
function LinkBonusToBuff takes unit u, integer bonus_type, integer amount, integer buffId returns nothing
-> Links the bonus amount specified to a buff or ability. As long as the unit has the buff or
-> the ability represented by the parameter buffId the bonus is not removed.
-> Example: call LinkBonusToBuff(GetTriggerUnit(), BONUS_ARMOR, 10, 'B000')
function LinkBonusToItem takes unit u, integer bonus_type, integer amount, item i returns nothing
-> Links the bonus amount specified to an item. As long as the unit has that item the bonus is not removed.
-> Note that it will work for items with the same id, because it takes as parameter the item object.
-> Example: call LinkBonusToItem(GetManipulatingUnit(), BONUS_ARMOR, 10, GetManipulatedItem())
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
private struct NewBonusUtils extends NewBonus
static timer t = CreateTimer()
//Dynamic Indexing
static integer didx = -1
static thistype array data
unit u
item i
real ticks
integer bonus_type
integer buffId // Works for abilities too.
integer amount
integer link //0 -> Timed bonus / 1 -> Linked to a buff / 2 -> Linked to an item.
method destroy takes nothing returns nothing
if didx == -1 then
call PauseTimer(t)
endif
call AddUnitBonus(this.u, this.bonus_type, -this.amount)
set this.u = null
set this.i = null
call this.deallocate()
endmethod
static method OnPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if this.link == 0 then // Timed link
set this.ticks = this.ticks - 1
if this.ticks <= 0 then
set data[i] = data[didx]
set didx = didx - 1
set i = i - 1
call this.destroy()
endif
elseif this.link == 1 then // Buff link
if GetUnitAbilityLevel(this.u, this.buffId) == 0 then
set data[i] = data[didx]
set didx = didx - 1
set i = i - 1
call this.destroy()
endif
else // Item link
if not UnitHasItem(this.u, this.i) then
set data[i] = data[didx]
set didx = didx - 1
set i = i - 1
call this.destroy()
endif
endif
set i = i + 1
endloop
endmethod
static method TimeLink takes unit u, integer bonus_type, integer amount, real duration, integer link returns nothing
local thistype this = thistype.allocate()
set this.u = u
set this.amount = AddUnitBonus(u, bonus_type, amount)
set this.bonus_type = bonus_type
set this.ticks = duration/0.03125000
set this.link = link
set didx = didx + 1
set data[didx] = this
if didx == 0 then
call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
endif
endmethod
static method BuffLink takes unit u, integer bonus_type, integer amount, integer buffId, integer link returns nothing
local thistype this = thistype.allocate()
set this.u = u
set this.amount = AddUnitBonus(u, bonus_type, amount)
set this.bonus_type = bonus_type
set this.buffId = buffId
set this.link = link
set didx = didx + 1
set data[didx] = this
if didx == 0 then
call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
endif
endmethod
static method ItemLink takes unit u, integer bonus_type, integer amount, item i, integer link returns nothing
local thistype this = thistype.allocate()
set this.u = u
set this.i = i
set this.amount = AddUnitBonus(u, bonus_type, amount)
set this.bonus_type = bonus_type
set this.link = link
set didx = didx + 1
set data[didx] = this
if didx == 0 then
call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
endif
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS Public API */
/* -------------------------------------------------------------------------- */
function AddUnitBonusTimed takes unit u, integer bonus_type, integer amount, real duration returns nothing
call NewBonusUtils.TimeLink(u, bonus_type, amount, duration, 0)
endfunction
function LinkBonusToBuff takes unit u, integer bonus_type, integer amount, integer buffId returns nothing
call NewBonusUtils.BuffLink(u, bonus_type, amount, buffId, 1)
endfunction
function LinkBonusToItem takes unit u, integer bonus_type, integer amount, item i returns nothing
call NewBonusUtils.ItemLink(u, bonus_type, amount, i, 2)
endfunction
endlibrary
scope LinkedBonusToItems
private struct ItemPickup
static method OnPickUp takes nothing returns nothing
local unit u = GetManipulatingUnit()
local item i = GetManipulatedItem()
if GetItemTypeId(i) == 'ward' then
call LinkBonusToItem(u, BONUS_MOVEMENTSPEED, 20, i)
elseif GetItemTypeId(i) == 'I04A' then
call LinkBonusToItem(u, BONUS_MOVEMENTSPEED, 30, i)
endif
set i = null
set u = null
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.OnPickUp)
endmethod
endstruct
endscope
library CooldownReduction requires RegisterPlayerUnitEvent
/* ------------------ Cooldown Reduction v1.0 by Chopinski ------------------ */
//! novjass
Intro
This library intension in to introduce to warcraft an easy way to
manipulate abilities cooldowns based on a cooldown reduction value that
is unique for each unit.
How it Works?
When casting an ability, its "new" cooldown is calculated based on the
amount of cooldown reduction of the casting unit. the formula for
calculation is:
Cooldown = Default Cooldown x (1 - Unit Cooldown Reduction)
The system also allow negative values for CDR, resulting in increased
ability cooldown.
It does not acumulate because the abilities are registered automatically
on the first cast, saving its base cooldown (Object Editor values) and
always using this base value for calculation, so you can still edit
the ability via the editor and the system takes care of the rest.
Of course you can manually register the abilities, but why do the work
yourself?
How to Import
simply copy the CooldownReduction folder over to your map, and start
use the API functions
Requirements
CooldownReduction requires RegisterPlayerUnitEvent and a Unit Indexer.
Credits to Magtheridon96 for RegisterPlayerUnitEvent and to Bribe for
the UnitIndexer that i chose to use in the test map, but any Unit Indexer
will do the job. It also requires patch 1.30+.
JASS API
function GetUnitCooldownReduction takes unit u returns real
-> Returns the amount of cdr a unit has
-> 0.1 = 10%
function SetUnitCooldownReduction takes unit u, real value returns nothing
->Set the amount of cdr for a unit
function UnitAddCooldownReduction takes unit u, real value returns nothing
-> Add to the amount of cdr of a unit
function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
-> Add to the amount of cdr of a unit for a given period
function RegisterUnitAbility takes unit u, integer abilityId returns nothing
-> Manually register an ability into the system
// The Following functions exists to facilitate the preview of cooldown reduction.
// Usefull when combining with string modification of a spell description.
function GetUnitAbilityCooldown takes unit u, integer abilityId, integer level returns real
-> Returns the calculated cooldown for an ability
function GetUnitAbilityCooldownEx takes unit u, integer abilityId, integer level returns string
-> Returns the calculated cooldown for an ability as string
function GetUnitCooldownReductionEx takes unit u returns string
-> Returns the amount of cdr a unit has as a string factored by 100
-> example of return: 10.50 -> 0.105 internally.
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
private struct CDR
static hashtable Ability_Table = InitHashtable()
static timer t = CreateTimer()
//----------------------------------------------
static integer didx = -1
static thistype array data
//----------------------------------------------
static real array CooldownReduction
//----------------------------------------------
unit u
real ticks
real amount
method destroy takes nothing returns nothing
if didx == -1 then
call PauseTimer(t)
endif
set this.u = null
set this.ticks = 0
call this.deallocate()
endmethod
static method Get takes unit u returns real
return CooldownReduction[GetUnitUserData(u)]
endmethod
static method Set takes unit u, real value returns nothing
set CooldownReduction[GetUnitUserData(u)] = value
endmethod
static method OnPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
set this.ticks = this.ticks - 1
if this.ticks <= 0 then
call Set(this.u, Get(this.u) - this.amount)
set data[i] = data[didx]
set didx = didx - 1
set i = i - 1
call this.destroy()
endif
set i = i + 1
endloop
endmethod
static method AddTimed takes unit u, real amount, real duration returns nothing
local thistype this = thistype.allocate()
set this.u = u
set this.amount = amount
set this.ticks = duration/0.03125000
set didx = didx + 1
set data[didx] = this
call Set(u, Get(u) + amount)
if didx == 0 then
call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
endif
endmethod
static method GetDefaultCooldown takes integer abilityId, integer level returns real
return LoadReal(Ability_Table, abilityId, level)
endmethod
static method RegisterDefaultCooldown takes unit caster, integer abilityId returns nothing
local integer i = 0
local integer levels = BlzGetAbilityIntegerField(BlzGetUnitAbility(caster, abilityId), ABILITY_IF_LEVELS)
//--------------------------------------------
call SaveBoolean(Ability_Table, abilityId, 0, true)
loop
exitwhen i >= levels
call SaveReal(Ability_Table, abilityId, (i + 1), BlzGetUnitAbilityCooldown(caster, abilityId, i))
set i = i + 1
endloop
endmethod
static method GetNewCooldown takes unit u, integer abilityId, integer level returns real
local boolean registered = LoadBoolean(Ability_Table, abilityId, 0)
if not registered then
call RegisterDefaultCooldown(u, abilityId)
endif
return GetDefaultCooldown(abilityId, level)*(1 - CooldownReduction[GetUnitUserData(u)])
endmethod
static method OnCast takes nothing returns nothing
local unit caster = GetTriggerUnit()
//--------------------------------------------
local integer abil = GetSpellAbilityId()
local integer lvl = GetUnitAbilityLevel(caster, abil)
//--------------------------------------------
call BlzSetAbilityRealLevelField(BlzGetUnitAbility(caster, abil), ABILITY_RLF_COOLDOWN, lvl - 1, GetNewCooldown(caster, abil, lvl))
call IncUnitAbilityLevel(caster, abil)
call DecUnitAbilityLevel(caster, abil)
set caster = null
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.OnCast)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
function GetUnitCooldownReduction takes unit u returns real
return CDR.Get(u)
endfunction
function SetUnitCooldownReduction takes unit u, real value returns nothing
call CDR.Set(u, value)
endfunction
function UnitAddCooldownReduction takes unit u, real value returns nothing
call CDR.Set(u, CDR.Get(u) + value)
endfunction
function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
call CDR.AddTimed(u, value, duration)
endfunction
function RegisterUnitAbility takes unit u, integer abilityId returns nothing
call CDR.RegisterDefaultCooldown(u, abilityId)
endfunction
// The Following functions exists to facilitate the preview of cooldown reduction.
// Usefull when combining with string modification of a spell description.
function GetUnitAbilityCooldown takes unit u, integer abilityId, integer level returns real
local real cd = CDR.GetNewCooldown(u, abilityId, level)
if cd < 0 then
set cd = 0.00
endif
return cd
endfunction
function GetUnitAbilityCooldownEx takes unit u, integer abilityId, integer level returns string
return R2SW(GetUnitAbilityCooldown(u, abilityId, level) , 1, 2)
endfunction
function GetUnitCooldownReductionEx takes unit u returns string
return R2SW(CDR.Get(u)*100, 1, 2)
endfunction
endlibrary
scope Items
private struct Items
static method OnPickUp takes nothing returns nothing
local unit u = GetManipulatingUnit()
local item i = GetManipulatedItem()
if GetItemTypeId(i) == 'I051' then
call UnitAddCooldownReduction(u, 0.15)
endif
set i = null
set u = null
endmethod
static method OnDrop takes nothing returns nothing
local unit u = GetManipulatingUnit()
local item i = GetManipulatedItem()
if GetItemTypeId(i) == 'I051' then
call UnitAddCooldownReduction(u, -0.15)
endif
set i = null
set u = null
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.OnPickUp)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.OnDrop)
endmethod
endstruct
endscope
library MouseUtils
/*
-------------------
MouseUtils
- MyPad
1.0.2.2
-------------------
----------------------------------------------------------------------------
A simple snippet that allows one to
conveniently use the mouse natives
as they were meant to be...
-------------------
| API |
-------------------
struct UserMouse extends array
static method operator [] (player p) -> thistype
- Returns the player's id + 1
static method getCurEventType() -> integer
- Returns the custom event that got executed.
method operator player -> player
- Returns Player(this - 1)
readonly real mouseX
readonly real mouseY
- Returns the current mouse coordinates.
readonly method operator isMouseClicked -> boolean
- Determines whether any mouse key has been clicked,
and will return true on the first mouse key.
method isMouseButtonClicked(mousebuttontype mouseButton)
- Returns true if the mouse button hasn't been
released yet.
method setMousePos(real x, y) (introduced in 1.0.2.2)
- Sets the mouse position for a given player.
static method registerCode(code c, integer ev) -> triggercondition
- Lets code run upon the execution of a certain event.
- Returns a triggercondition that can be removed later.
static method unregisterCallback(triggercondition trgHndl, integer ev)
- Removes a generated triggercondition from the trigger.
functions:
GetPlayerMouseX(player p) -> real
GetPlayerMouseY(player p) -> real
- Returns the coordinates of the mouse of the player.
OnMouseEvent(code func, integer eventId) -> triggercondition
- See UserMouse.registerCode
GetMouseEventType() -> integer
- See UserMouse.getCurEventType
UnregisterMouseCallback(triggercondition t, integer eventId)
- See UserMouse.unregisterCallback
SetUserMousePos(player p, real x, real y)
- See UserMouse.setMousePos
Unique Global Constants:
IMPL_LOCK (Introduced in v.1.0.2.2)
- Enables or disables the lock option
-------------------
| Credits |
-------------------
- Pyrogasm for pointing out a comparison logic flaw
in operator isMouseClicked.
- Illidan(Evil)X for the useful enum handles that
grant more functionality to this snippet.
- TriggerHappy for the suggestion to include
associated events and callbacks to this snippet.
- Quilnez for pointing out a bug related to the
method isMouseButtonClicked not working as intended
in certain situations.
----------------------------------------------------------------------------
*/
// Arbitrary constants
globals
constant integer EVENT_MOUSE_UP = 1024
constant integer EVENT_MOUSE_DOWN = 2048
constant integer EVENT_MOUSE_MOVE = 3072
private constant boolean IMPL_LOCK = false
endglobals
private module Init
private static method onInit takes nothing returns nothing
call thistype.init()
endmethod
endmodule
struct UserMouse extends array
static if IMPL_LOCK then
// Determines the minimum interval that a mouse move event detector
// will be deactivated. (Globally-based)
// You can configure it to any amount you like.
private static constant real INTERVAL = 0.031250000
// Determines how many times a mouse move event detector can fire
// before being deactivated. (locally-based)
// You can configure this to any integer value. (Preferably positive)
private static constant integer MOUSE_COUNT_MAX = 16
// Determines the amount to be deducted from mouseEventCount
// per INTERVAL. Runs independently of resetTimer
private static constant integer MOUSE_COUNT_LOSS = 8
private static constant boolean IS_INSTANT = INTERVAL <= 0.
endif
private static integer currentEventType = 0
private static integer updateCount = 0
private static trigger stateDetector = null
static if IMPL_LOCK and not IS_INSTANT then
private static timer resetTimer = null
endif
private static trigger array evTrigger
private static integer array mouseButtonStack
static if IMPL_LOCK and not IS_INSTANT then
private integer mouseEventCount
private timer mouseEventReductor
endif
private thistype next
private thistype prev
private thistype resetNext
private thistype resetPrev
private trigger posDetector
private integer mouseClickCount
readonly real mouseX
readonly real mouseY
// Converts the enum type mousebuttontype into an integer
private static method toIndex takes mousebuttontype mouseButton returns integer
return GetHandleId(mouseButton)
endmethod
static method getCurEventType takes nothing returns integer
return currentEventType
endmethod
static method operator [] takes player p returns thistype
if thistype(GetPlayerId(p) + 1).posDetector != null then
return GetPlayerId(p) + 1
endif
return 0
endmethod
method operator player takes nothing returns player
return Player(this - 1)
endmethod
method operator isMouseClicked takes nothing returns boolean
return .mouseClickCount > 0
endmethod
method isMouseButtonClicked takes mousebuttontype mouseButton returns boolean
return UserMouse.mouseButtonStack[(this - 1)*3 + UserMouse.toIndex(mouseButton)] > 0
endmethod
method setMousePos takes integer x, integer y returns nothing
if GetLocalPlayer() == this.player then
call BlzSetMousePos(x, y)
endif
endmethod
static if IMPL_LOCK then
private static method getMouseEventReductor takes timer t returns thistype
local thistype this = thistype(0).next
loop
exitwhen this.mouseEventReductor == t or this == 0
set this = this.next
endloop
return this
endmethod
private static method onMouseUpdateListener takes nothing returns nothing
local thistype this = thistype(0).resetNext
set updateCount = 0
loop
exitwhen this == 0
set updateCount = updateCount + 1
set this.mouseEventCount = 0
call EnableTrigger(this.posDetector)
set this.resetNext.resetPrev = this.resetPrev
set this.resetPrev.resetNext = this.resetNext
set this = this.resetNext
endloop
if updateCount > 0 then
static if not IS_INSTANT then
call TimerStart(resetTimer, INTERVAL, false, function thistype.onMouseUpdateListener)
else
call onMouseUpdateListener()
endif
else
static if not IS_INSTANT then
call TimerStart(resetTimer, 0.00, false, null)
call PauseTimer(resetTimer)
endif
endif
endmethod
private static method onMouseReductListener takes nothing returns nothing
local thistype this = getMouseEventReductor(GetExpiredTimer())
if this.mouseEventCount <= 0 then
call PauseTimer(this.mouseEventReductor)
else
set this.mouseEventCount = IMaxBJ(this.mouseEventCount - MOUSE_COUNT_LOSS, 0)
call TimerStart(this.mouseEventReductor, INTERVAL, false, function thistype.onMouseReductListener)
endif
endmethod
endif
private static method onMouseUpOrDown takes nothing returns nothing
local thistype this = thistype[GetTriggerPlayer()]
local integer index = (this - 1)*3 + UserMouse.toIndex(BlzGetTriggerPlayerMouseButton())
local boolean releaseFlag = false
if GetTriggerEventId() == EVENT_PLAYER_MOUSE_DOWN then
set this.mouseClickCount = IMinBJ(this.mouseClickCount + 1, 3)
set releaseFlag = UserMouse.mouseButtonStack[index] <= 0
set UserMouse.mouseButtonStack[index] = IMinBJ(UserMouse.mouseButtonStack[index] + 1, 1)
if releaseFlag then
set currentEventType = EVENT_MOUSE_DOWN
call TriggerEvaluate(evTrigger[EVENT_MOUSE_DOWN])
endif
else
set this.mouseClickCount = IMaxBJ(this.mouseClickCount - 1, 0)
set releaseFlag = UserMouse.mouseButtonStack[index] > 0
set UserMouse.mouseButtonStack[index] = IMaxBJ(UserMouse.mouseButtonStack[index] - 1, 0)
if releaseFlag then
set currentEventType = EVENT_MOUSE_UP
call TriggerEvaluate(evTrigger[EVENT_MOUSE_UP])
endif
endif
endmethod
private static method onMouseMove takes nothing returns nothing
local thistype this = thistype[GetTriggerPlayer()]
local boolean started = false
set this.mouseX = BlzGetTriggerPlayerMouseX()
set this.mouseY = BlzGetTriggerPlayerMouseY()
static if IMPL_LOCK then
set this.mouseEventCount = this.mouseEventCount + 1
if this.mouseEventCount <= 1 then
call TimerStart(this.mouseEventReductor, INTERVAL, false, function thistype.onMouseReductListener)
endif
endif
set currentEventType = EVENT_MOUSE_MOVE
call TriggerEvaluate(evTrigger[EVENT_MOUSE_MOVE])
static if IMPL_LOCK then
if this.mouseEventCount >= thistype.MOUSE_COUNT_MAX then
call DisableTrigger(this.posDetector)
if thistype(0).resetNext == 0 then
static if not IS_INSTANT then
call TimerStart(resetTimer, INTERVAL, false, function thistype.onMouseUpdateListener)
// Mouse event reductor should be paused
else
set started = true
endif
call PauseTimer(this.mouseEventReductor)
endif
set this.resetNext = 0
set this.resetPrev = this.resetNext.resetPrev
set this.resetPrev.resetNext = this
set this.resetNext.resetPrev = this
if started then
call onMouseUpdateListener()
endif
endif
endif
endmethod
private static method init takes nothing returns nothing
local thistype this = 1
local player p = this.player
static if IMPL_LOCK and not IS_INSTANT then
set resetTimer = CreateTimer()
endif
set stateDetector = CreateTrigger()
set evTrigger[EVENT_MOUSE_UP] = CreateTrigger()
set evTrigger[EVENT_MOUSE_DOWN] = CreateTrigger()
set evTrigger[EVENT_MOUSE_MOVE] = CreateTrigger()
call TriggerAddCondition( stateDetector, Condition(function thistype.onMouseUpOrDown))
loop
exitwhen integer(this) > bj_MAX_PLAYER_SLOTS
if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
set this.posDetector = CreateTrigger()
static if IMPL_LOCK and not IS_INSTANT then
set this.mouseEventReductor = CreateTimer()
endif
call TriggerRegisterPlayerEvent( this.posDetector, p, EVENT_PLAYER_MOUSE_MOVE )
call TriggerAddCondition( this.posDetector, Condition(function thistype.onMouseMove))
call TriggerRegisterPlayerEvent( stateDetector, p, EVENT_PLAYER_MOUSE_UP )
call TriggerRegisterPlayerEvent( stateDetector, p, EVENT_PLAYER_MOUSE_DOWN )
endif
set this = this + 1
set p = this.player
endloop
endmethod
static method registerCode takes code handlerFunc, integer eventId returns triggercondition
return TriggerAddCondition(evTrigger[eventId], Condition(handlerFunc))
endmethod
static method unregisterCallback takes triggercondition whichHandler, integer eventId returns nothing
call TriggerRemoveCondition(evTrigger[eventId], whichHandler)
endmethod
implement Init
endstruct
function GetPlayerMouseX takes player p returns real
return UserMouse[p].mouseX
endfunction
function GetPlayerMouseY takes player p returns real
return UserMouse[p].mouseY
endfunction
function OnMouseEvent takes code func, integer eventId returns triggercondition
return UserMouse.registerCode(func, eventId)
endfunction
function GetMouseEventType takes nothing returns integer
return UserMouse.getCurEventType()
endfunction
function UnregisterMouseCallback takes triggercondition whichHandler, integer eventId returns nothing
call UserMouse.unregisterCallback(whichHandler, eventId)
endfunction
function SetUserMousePos takes player p, integer x, integer y returns nothing
call UserMouse[p].setMousePos(x, y)
endfunction
endlibrary
library LocalZ
//Can be substituted with any other library that does the same thing
//or a Blz native if Blizzard adds something like that
globals
private location l = Location(0.,0.)
endglobals
function GetLocalZ takes real x, real y returns real
call MoveLocation(l, x, y)
return GetLocationZ(l)
endfunction
endlibrary
library RemoveEffect
//Destroying an effect always plays its death animation and any associated sounds.
//This library fixes it in a simple way: move the effect to a location where nobody can see or hear it.
//By Pyrogasm, additional help by Bribe
//v1.1
globals
private real SAFE_X = -3900.
private real SAFE_Y = 3900.
private real SAFE_Z = -1000.
private real TIMESCALE = 10. //doesn't really matter what this is but we set it to > 0 so the animations actually finish
endglobals
function RemoveEffect takes effect e returns nothing
call BlzSetSpecialEffectAlpha(e, 255)
call BlzSetSpecialEffectZ(e, SAFE_Z)
call BlzSetSpecialEffectPosition(e, SAFE_X, SAFE_Y, SAFE_Z)
call BlzSetSpecialEffectTimeScale(e, TIMESCALE)
call DestroyEffect(e)
endfunction
endlibrary
//! zinc
/*
* PowerupSentinel
* ------------
* Placing this library in your map will automatically fix all rune/tome
* memory leaks in your map.
*
* Powerup items don't get removed automatically by the game, they instead
* just leave a small item in the map, this caused memory leaks but - worse -
* it also makes areas of your map where a lot of tomes have been used lag a lot.
*
*/
library PowerupSentinel
{
function onInit(){
trigger t = CreateTrigger();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DROP_ITEM);
TriggerAddCondition(t, function()->boolean {
if (GetWidgetLife(GetManipulatedItem())==0) {
RemoveItem(GetManipulatedItem());
}
return false;
});
}
}
//! endzinc
//TESH.scrollpos=3
//TESH.alwaysfold=0
library ValueIndexing initializer Init
//===========================================================================
// Information:
//==============
// This is a library that allows you to get unique hashvalues for any number you like.
// This also gives the option to be directly added to the struct, so you will have this syntax:
// set Data['some'] = anyValue
//
// But you could also do it with table, right?
// Well that's correct, but in many cases this is faster;
// It is proven that LoadInteger is much faster than SaveInteger.
// When you apply new data to a normal table, SaveInteger is used.
// This system only uses SaveInteger if it hasn't created a hash yet.
//
// But the bad thing is that you should remove the hashvalues after a while, since
// there is a limit of 8191 unique hashnumbers. Also this is not available as a struct,
// just as a module.
//===========================================================================
// FAQ:
// ====
//
// 1. We have a maximum of 8190 possible hashes. And there's no clearHash function.
// Won't this break after a while?
//
// Answer: No, it won't. If the number of hahes reach a critical point, all hashes
// that have been in the game for longer than HASH_DECAY_TIME seconds will
// be removed then.
//
//===========================================================================
globals
private constant real HASH_DECAY_TIME = 500.
private constant integer CLEAR_HASH_COUNT = 8190
endglobals
globals
private hashtable HashTable = InitHashtable()
private integer HashNumber = 0
private integer array HashData
private integer array HashHash
private integer array HashPlace
private real array CreationTime
private integer TempHashNumber = 0
private integer array TempHashHash
private integer array TempHashPlace
private integer array TempHashData
private real array TempCreationTime
private integer LastHashedValue = 0
private integer LastIndex = 0
private real GameTime = 0.
private timer GameTimeTimer = CreateTimer()
private constant real GAMETIME_TIMER_INTERVAL = 30.
private constant integer key = 0
endglobals
private function GetElapsedGameTime takes nothing returns real
return GameTime + TimerGetElapsed(GameTimeTimer)
endfunction
private function UpdateGameTime takes nothing returns nothing
set GameTime = GameTime + GAMETIME_TIMER_INTERVAL
endfunction
// Well, the method to do this that I use in my automatic memory leak destroyer
// is much more efficient, but I think that's not iportant in this library,
// because old Hashes are destroyed very rarely.
private function DestroyHashes takes nothing returns nothing
local real gt = GetElapsedGameTime
local Index ind
// Well, due to the nature of this system, the looking of the code sucks.
loop
exitwhen HashNumber == 0
if gt - CreationTime[HashNumber] > HASH_DECAY_TIME then
set ind = HashHash[HashNumber]
call ind.destroy()
call RemoveSavedInteger(HashTable,key,HashData[HashNumber])
else
set TempHashNumber = TempHashNumber + 1
set TempHashData[TempHashNumber] = HashData[HashNumber]
set TempHashHash[TempHashNumber] = HashHash[HashNumber]
set TempHashPlace[TempHashNumber] = HashPlace[HashNumber]
set TempCreationTime[TempHashNumber] = CreationTime[HashNumber]
endif
set HashData[HashNumber] = 0
set HashHash[HashNumber] = 0
set HashPlace[HashNumber] = 0
set CreationTime[HashNumber] = 0.
set HashNumber = HashNumber - 1
endloop
loop
exitwhen TempHashNumber == 0
set HashNumber = HashNumber + 1
set HashData[HashNumber] = TempHashData[TempHashNumber]
set HashHash[HashNumber] = TempHashHash[TempHashNumber]
set HashPlace[HashNumber] = TempHashPlace[TempHashNumber]
set TempHashData[TempHashNumber] = 0
set TempHashNumber = TempHashNumber - 1
endloop
endfunction
struct Index
static method GetHash takes integer value returns integer
local integer int = LoadInteger(HashTable,key,value)
if int == 0 then
set int = Index.create()
call SaveInteger(HashTable,key,value,int)
set HashNumber = HashNumber + 1
set HashPlace[int] = HashNumber
set HashData[HashNumber] = value
set HashHash[HashNumber] = int
set CreationTime[HashNumber] = GetElapsedGameTime()
if HashNumber >= CLEAR_HASH_COUNT then
call DestroyHashes()
endif
endif
set LastHashedValue = value
set LastIndex = int
return int
endmethod
endstruct
function ProtectHash takes integer value returns nothing
if value == LastHashedValue then
set CreationTime[HashPlace[LastIndex]] = 999999999.
else
set CreationTime[HashPlace[Index.GetHash(value)]] = 999999999.
endif
endfunction
function GetValueIndex takes integer value returns integer
return Index.GetHash(value)
endfunction
private function Init takes nothing returns nothing
call TimerStart(GameTimeTimer,GAMETIME_TIMER_INTERVAL,true,function UpdateGameTime)
endfunction
module ValueIndexing
static integer array Data
static method operator []= takes integer i, integer equals returns nothing
set .Data[Index.GetHash(i)] = equals
endmethod
static method operator [] takes integer i returns thistype
return .Data[Index.GetHash(i)]
endmethod
endmodule
endlibrary
//TESH.scrollpos=437
//TESH.alwaysfold=0
library MemoryLeakHelper initializer Init requires Table
// ==================================
// Give credits to Mr.Malte when used!
//===========================================================================
// Information:
//==============
//
// There are things called 'memory Leaks'. When you create a group or use a location
// without destroying it, you will cause lag that stays in the whole game.
// If you implement this library into your map it will automatically fix a big part
// of those memory leaks, so reduce the lag extremely.
// This should mainfully be used by GUI-users because the system is designed for them.
//
// Of course no system can work totally automatically.
// But there is only one thing you have to do in order to prevent bugs:
// If you make groups or locations that have to be filled for more than CLEAN_UP_INTERVAL seconds
// you have to save them with the code:
//
// call ProtectHandle(XXX)
//
// Where XXX is filled with your variable. Otherwise that variable gets destroyed
// automatically. You can also fill in XXX with
//
// GetLastCaughtHandle()
//
// But if you save the things in a variable, I'd recommend to directly put the
// variable into the brackets. Note: GUI variables have the prefix 'udg_' in JASS.
//
// This gives the 'Do Nothing' function in GUI a sense!
// When you call DoNothing, all data that were caught by this system
// will be destroyed in CLEAN_UP_INTERVAL seconds, ignoring how big
// the number of caught handles is. This will not work, if the system is
// already cleaning up.
//===========================================================================
// Implementation:
//===============
//
// The easiest thing is to directly implement this thing into your map, when you start making
// it, so you don't have to look over your globals and use ProtectHandle on them.
// These are the steps you have to do to clear the memory leaks:
//
// 1. Download a tool called 'JassNewGen', and unpack it somewhere. You need that
// edit to use this tool. The 'JassNewGen' is used very commonly and offers other
// nice features. You can find it at:
// http://www.wc3c.net/showthread.php?t=90999
// 2. Make a new trigger, and convert it to custom text. Insert everything
// the library contains into that trigger.
//
// 3. Download a system called 'Table' from this link:
// http://www.wc3c.net/showthread.php?t=101246
// Do the same installation stuff for 'Table' as for this system.
//
// 4. Save your map and enjoy :-)
//
// Note: Instead of doing 2 and 3 you can also copy and paste the folder 'MemoryLeakHelper'
// from the example map.
//===========================================================================
// How bad are memory leaks?
//==========================
// If you don't remove memory leaks, they suck memory:
//
// Location: 0.361 kb
// Group: 0.62 kb + 0.040 kb for each unit in the group.
// Effect: 11.631 kb
//
// Both, locations and groups are used very frequently. So when you don't fix those memory leaks,
// you will experience lag.
// When you want to see, how useful this is for your map, implement it
// and write 'call DisplayLeaks()' into a custom script that is fired when
// they game ends.
//===========================================================================
// Changelog:
//===========
// v1.00 --- first version
// v1.01 --- able to detect special effects, too now.
// v1.02 --- made the system safer and reduced the number of variables to protect greatly.
// v1.03 --- Gave a sense to 'DoNothing'* GUI function and made the Pass Data part
// more accurate, so the time until data get destroyed are much more explicit
// now.
// v1.04 --- Added the very important constant MAX_LEAK_INSTANCES
//
// *if you don't want it to be hooked, comment line 350.
//===========================================================================
// FAQ:
// ====
// 1. Why don't you hook functions like GetLocationX or the ForGroup without BJ?
//
// Answer: Well, in jass you would never destroy groups, rather have one global group
// and clear/recycle it. But GUI always creates new groups with the functions.
// So actually, jass groups don't have to be destroyed.
// And special effects are mostly instantly destroyed and locations are never used.
// So I don't want to endanger breaking jass systems, I rather make it for GUI, where it is
// really useful and neccessary
//
// 2. Why should I protect my variables instead of killing my leaks?
//
// Answer: In GUI, unitgroup effect and location variables are actually just used
// for destroying stuff. It is rare that you really want to keep the groups.
// So in fact, it is like one-hundred times less frequent that you want to keep
// your data instead of destroying it.
//
// 3. I can't use jass. How can this system be useful for me?
//
// Answer: This system works mainly automatically; You just have to use jass very rarely (and then a simple function).
// The functions you need are:
//
// ProtectVariable(udg_###)
// GetLastCaughtHandle()
//
// where ProtectVariable saves something you want to keep from getting destroyed. Just replace ### with your
// variable name. NOTE: You don't have to protect the variable, when you want to keep the data inside less than
// CLEAN_UP_INTERVAL seconds.
//
// and where GetLastCaughtHandle responses to the handle* that was used lastly.
// That can be for example the point where you just spawned a unit.
//
// * 'handle' means a specialeffect, a location or a unitgroup
//
// 4. If you give functions like 'DelayMMH', why don't you add functions like 'Disable/EnableMMH'?
//
// Answer: Well, I want to protect the user. You do not need Disable/Enable functions.
// To me the danger is too big, that you forget to activate it again or do
// something like that. DelayMMH is totally enough if you don't want this system
// to affect the code that comes next. Also that prevents, that there are
// spells like 'Trojan Horses' that get too much access to this and can
// change it's infrastructure.
//
//===========================================================================
// Functions:
//==========
// ProtectHandle : Saves a handle from getting destroyed
// ProtectVariable : Same.
// DoNothing() : Destroys all data caught by the system right now in X seconds.
// DelayMMD() : Stops the system working until the trigger ends/next wait *
//
// * This is as fast as an automatic memory leak destroyer can get. Why should
// you want to disable the system? Because it offers the possibilty to make things
// more efficient. I don't want to say, this is unefficient, because it is not.
// But this will destroy leaks like 10% slower.
//
//===========================================================================
globals
// The system fires when you do something that creates a leak.
// The data that cause leak are saved in a variable then.
// And every CLEAN_UP_INTERVAL seconds those data are destroyed.
// This shouldn't be too high, or too low.
private constant real CLEAN_UP_INTERVAL = 120.
// If this is set to true, the system will work more slowly (but you wont notice)
// and count, how much memory this system was able to save.
// This value is display by the function DisplayLeaks() then.
// WARNING: This sucks a lot of performance. I would ONLY use it when you want
// to test, if this is useful for your map. Later set it to false.
private constant boolean DISPLAY_SAVED_MEMORY = false
// The Data are only cleaned up, when that many handles were caught
private constant integer MIN_LEAK_NUMBER = 1750
// How often are data passed to the destroyer?
// Leaks stay for a random time between CLEAN_UP_INTERVAL and CLEAN_UP_INTERVAL+PASS_INTERVAL
// in the game
private constant real PASS_INTERVAL = 2.5
// Memory leaks occur pretty frequently. When a leak is caught it is saved in
// an array. But the array can't have more than MAX_LEAK_INSTANCES instances, so
// if more than MAX_LEAK_INSTANCES memory leaks occur during a destroy interval,
// the system fails.
private constant integer MAX_LEAK_INSTANCES = 60000
endglobals
globals
private HandleTable IndexData
private HandleTable IsSaved
//! textmacro MemoryLeakVars takes NAME, TYPE
private integer Caught$NAME$Leaks = 0
private $TYPE$ array $NAME$LeakData[MAX_LEAK_INSTANCES]
private integer $NAME$DestroyCount = 0
private $TYPE$ array $NAME$DestroyData[MAX_LEAK_INSTANCES]
//! endtextmacro
//! runtextmacro MemoryLeakVars("Location","location")
//! runtextmacro MemoryLeakVars("Effect","effect")
//! runtextmacro MemoryLeakVars("Group","group")
private integer DestroyedLeaks = 0
private integer CaughtLeaks = 0
private integer DestroyedLeaksUser = 0
private handle LastCaught
private timer PassTimer = CreateTimer()
private timer CleanTimer = CreateTimer()
private timer DelayTimer = CreateTimer()
private boolean IsDestroying = false
private real SavedMemory = 0.
private real LastCheckedGroupMemoryUsage = 0.
private boolean DestroyThreadRunning = false
private boolean Disabled = false
// These values were found out in a big leak test by gekko.
private constant real LOCATION_MEMORY_USAGE = 0.361
private constant real GROUP_MEMORY_USAGE = 0.62
private constant real GROUP_UNIT_MEMORY_USAGE = 0.040
private constant real EFFECT_MEMORY_USAGE = 11.631
private constant real REMOVED_EFFECT_MEMORY_USAGE = 0.066
endglobals
// ======================================
// ============= Basic Code =============
// ======================================
function GetLastCaughtHandle takes nothing returns handle
return LastCaught
endfunction
function ProtectHandle takes handle h returns nothing
set IsSaved[h] = 1
endfunction
function ProtectVariable takes handle h returns nothing
set IsSaved[h] = 1
endfunction
private function EnableMMH takes nothing returns nothing
set Disabled = false
endfunction
function DelayMMH takes nothing returns nothing
set Disabled = true
call TimerStart(DelayTimer,0.00,false,function EnableMMH)
endfunction
function DisplayLeaks takes nothing returns nothing
call ClearTextMessages()
call BJDebugMsg("======= MemoryLeakHelper =======")
call BJDebugMsg("Destroyed Leaks: "+I2S(DestroyedLeaks))
call BJDebugMsg("Destroyed Leaks by user: "+I2S(DestroyedLeaksUser))
call BJDebugMsg("Percentage System: "+R2S(I2R(DestroyedLeaks)/I2R(DestroyedLeaks+DestroyedLeaksUser)*100.)+"%")
call BJDebugMsg("Percentage User: "+R2S(I2R(DestroyedLeaksUser)/I2R(DestroyedLeaks+DestroyedLeaksUser)*100.)+"%")
call BJDebugMsg("Leaks until next destroy: "+I2S(MIN_LEAK_NUMBER-CaughtLeaks))
call BJDebugMsg(" === In Destroy Queue === ")
call BJDebugMsg(" Group Leaks: "+I2S(GroupDestroyCount))
call BJDebugMsg(" Location Leaks: "+I2S(LocationDestroyCount))
call BJDebugMsg(" Effect Leaks: "+I2S(EffectDestroyCount))
call BJDebugMsg(" === Not in Destroy Queue yet === ")
call BJDebugMsg(" Group Leaks: "+I2S(CaughtGroupLeaks))
call BJDebugMsg(" Location Leaks: "+I2S(CaughtLocationLeaks))
call BJDebugMsg(" Effect Leaks: "+I2S(CaughtEffectLeaks))
call BJDebugMsg("Time until next PassSequence: "+I2S(R2I(TimerGetRemaining(PassTimer)+0.5))+" seconds.")
call BJDebugMsg(" ")
if DISPLAY_SAVED_MEMORY then
call BJDebugMsg("All in all the MemoryLeakHelper could release "+R2S(SavedMemory)+" kb of memory.")
endif
call BJDebugMsg("================================")
endfunction
private function GroupGetMemoryUsageEnum takes nothing returns nothing
set LastCheckedGroupMemoryUsage = LastCheckedGroupMemoryUsage + GROUP_UNIT_MEMORY_USAGE
endfunction
function GroupGetMemoryUsage takes group g returns real
set LastCheckedGroupMemoryUsage = 0.
call ForGroup(g,function GroupGetMemoryUsageEnum)
return LastCheckedGroupMemoryUsage + GROUP_MEMORY_USAGE
endfunction
//! textmacro ResponseOnLeak takes NAME, VALUE
private function Catch$NAME$ takes $VALUE$ l returns nothing
set LastCaught = l
if Disabled then
return
elseif Caught$NAME$Leaks == MAX_LEAK_INSTANCES then
debug call BJDebugMsg("MemoryLeakHelper: Failed to store leak because of size limitations")
return
endif
if IndexData.exists(l) == false then
//call BJDebugMsg("Caught $NAME$")
set Caught$NAME$Leaks = Caught$NAME$Leaks + 1
set $NAME$LeakData[Caught$NAME$Leaks] = l
set IndexData[l] = Caught$NAME$Leaks
endif
endfunction
private function AddTo$NAME$DestroyQueue takes $VALUE$ l returns nothing
set $NAME$DestroyCount = $NAME$DestroyCount + 1
set $NAME$DestroyData[$NAME$DestroyCount] = l
set IndexData[l] = $NAME$DestroyCount*-1 // Put his to negative, so we know that this is used in the DestroyQueue now.
endfunction
private function Release$NAME$ takes $VALUE$ l returns nothing
local integer index
if IsDestroying == false and IndexData.exists(l) then
set index = IndexData[l]
// If this is true, the index wasn't put to a destroy queue yet.
if index > 0 then
set $NAME$LeakData[index] = $NAME$LeakData[Caught$NAME$Leaks]
set Caught$NAME$Leaks = Caught$NAME$Leaks - 1
else
set index = index * -1
set $NAME$DestroyData[index] = $NAME$DestroyData[$NAME$DestroyCount]
set $NAME$DestroyCount = $NAME$DestroyCount - 1
endif
call IndexData.flush(l)
set DestroyedLeaksUser = DestroyedLeaksUser + 1
endif
endfunction
//! endtextmacro
//! runtextmacro ResponseOnLeak("Location","location")
//! runtextmacro ResponseOnLeak("Group","group")
//! runtextmacro ResponseOnLeak("Effect","effect")
private function DestroyMemoryLeaks takes nothing returns nothing
set IsDestroying = true
//call BJDebugMsg("DESTROYING Memory Leaks")
//! textmacro DestroyLeaks takes NAME, DESTROYCALL, MEMORYUSAGE
set DestroyedLeaks = DestroyedLeaks + $NAME$DestroyCount
loop
exitwhen $NAME$DestroyCount == 0
if DISPLAY_SAVED_MEMORY then
set SavedMemory = SavedMemory + $MEMORYUSAGE$
endif
call $DESTROYCALL$($NAME$DestroyData[$NAME$DestroyCount])
call IndexData.flush($NAME$DestroyData[$NAME$DestroyCount])
set $NAME$DestroyCount = $NAME$DestroyCount - 1
endloop
//! endtextmacro
//! runtextmacro DestroyLeaks ("Group","DestroyGroup","GroupGetMemoryUsage(GroupDestroyData[GroupDestroyCount])")
//! runtextmacro DestroyLeaks ("Location","RemoveLocation","LOCATION_MEMORY_USAGE")
//! runtextmacro DestroyLeaks ("Effect","DestroyEffect","EFFECT_MEMORY_USAGE")
set IsDestroying = false
set DestroyThreadRunning = false
//call StartPassTimer.execute() // Strange. This causes bugs sometimes and the function isn't called
// This is slower, but safe.
call ExecuteFunc("StartPassTimer")
endfunction
function StartDestroyThread takes nothing returns nothing
if DestroyThreadRunning == false then
set DestroyThreadRunning = true
call TimerStart(CleanTimer,CLEAN_UP_INTERVAL,false,function DestroyMemoryLeaks)
call PauseTimer(PassTimer)
endif
endfunction
hook DoNothing StartDestroyThread
// We want that the user doesn't have to protect too many variables, but all the variables that are filled longer
// than CLEAN_UP_INTERVAL seconds. But what, when the handle thing is put into the destroy stack and the next destroy is
// in 5 seconds, because the last one was 15 seconds ago? We can simply avoid something like that by using a 2-step-system
// that goes sure, the handle is only destroyed when it passed the CLEAN_UP_INTERVAL twice.
// Having two kinds of variables is simply easier and more efficient than having another variable that refers to
// how many times the handle passed the timer; If it isn't passed/cleared in the Interval then, we can't loop
// that easily through the data and we'd have to fix gaps later; That would suck a lot of performacne.
private function PassMemoryLeaks takes nothing returns nothing
//call BJDebugMsg("PassMemoryLeaks")
//! textmacro PassLeaks takes NAME
set CaughtLeaks = CaughtLeaks + Caught$NAME$Leaks
//call BJDebugMsg("Caught $NAME$s: "+I2S(Caught$NAME$Leaks))
loop
exitwhen Caught$NAME$Leaks < 1
if IsSaved.exists($NAME$LeakData[Caught$NAME$Leaks]) == false and $NAME$LeakData[Caught$NAME$Leaks] != null then
call AddTo$NAME$DestroyQueue($NAME$LeakData[Caught$NAME$Leaks])
endif
set $NAME$LeakData[Caught$NAME$Leaks] = null
set Caught$NAME$Leaks = Caught$NAME$Leaks - 1
endloop
//! endtextmacro
//! runtextmacro PassLeaks ("Group")
//! runtextmacro PassLeaks ("Location")
//! runtextmacro PassLeaks ("Effect")
if CaughtLeaks > MIN_LEAK_NUMBER then
set CaughtLeaks = 0
//call BJDebugMsg("Caught Leaks: "+I2S(MIN_LEAK_NUMBER))
//call BJDebugMsg("Now start Destroy Timer")
set DestroyThreadRunning = true
call TimerStart(CleanTimer,CLEAN_UP_INTERVAL,false,function DestroyMemoryLeaks)
// We have to pause this timer a bit; Otherwise it would break the CLEAN_UP_INTERVAL rule.
call PauseTimer(PassTimer)
endif
endfunction
// =================================
// ============= Usage =============
// =================================
private function PP takes location source, real dist, real angle returns nothing
call CatchLocation(source)
endfunction
private function CU takes integer count, integer unitId, player p, location l, real face returns nothing
call CatchLocation(l)
endfunction
private function IPO takes unit k, string order, location l returns nothing
call CatchLocation(l)
endfunction
private function SUP takes unit who, location l returns nothing
call CatchLocation(l)
endfunction
private function SUF takes unit who, location l, real dur returns nothing
call CatchLocation(l)
endfunction
private function GUR takes real radius, location l, boolexpr filter returns nothing
call CatchLocation(l)
endfunction
private function CUF takes integer count, integer unitId, player whichPlayer, location loc, location lookAt returns nothing
call CatchLocation(loc)
call CatchLocation(lookAt)
endfunction
hook PolarProjectionBJ PP
hook CreateNUnitsAtLoc CU
hook CreateNUnitsAtLocFacingLocBJ CUF
hook IssuePointOrderLocBJ IPO
hook SetUnitPositionLoc SUP
hook SetUnitFacingToFaceLocTimed SUF
hook GetUnitsInRangeOfLocMatching GUR
hook RemoveLocation ReleaseLocation
private function FG takes group g, code callback returns nothing
call CatchGroup(g)
endfunction
hook ForGroupBJ FG // :D This should catch all GUI usages for groups.
hook GroupPickRandomUnit CatchGroup
hook CountUnitsInGroup CatchGroup
hook DestroyGroup ReleaseGroup
private function ASETU takes string bla, widget d, string blu returns nothing
// We can not catch THIS effect, but the effect that was created before.
// So we can destroy all SpecialEffects excpet one.
call CatchEffect(GetLastCreatedEffectBJ())
endfunction
private function ASE takes location where, string modelName returns nothing
call CatchLocation(where)
call CatchEffect(GetLastCreatedEffectBJ())
endfunction
hook AddSpecialEffectLocBJ ASE
hook AddSpecialEffectTargetUnitBJ ASETU
hook DestroyEffect ReleaseEffect
hook DestroyEffectBJ ReleaseEffect
// When I want to make the timer run the PassMemoryLeaks things, I have to use an .execute command which requires an extra func.
function StartPassTimer takes nothing returns nothing
//call BJDebugMsg("Restarting PassTimer")
call TimerStart(PassTimer,PASS_INTERVAL,true,function PassMemoryLeaks)
endfunction
private function Init takes nothing returns nothing
set IndexData = HandleTable.create()
set IsSaved = HandleTable.create()
call StartPassTimer()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'xeca'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xefx initializer init requires xebasic
//**************************************************
// xefx 0.7
// --------
// Recommended: ARGB (adds ARGBrecolor method)
// For your movable fx needs
//
//**************************************************
//==================================================
globals
private constant integer MAX_INSTANCES = 8190 //change accordingly.
private constant real RECYCLE_DELAY = 4.0
//recycling, in order to show the effect correctly, must wait some time before
//removing the unit.
private timer recycler
private timer NOW
endglobals
private struct recyclebin extends array
unit u
real schedule
static recyclebin end=0
static recyclebin begin=0
static method Recycle takes nothing returns nothing
call RemoveUnit(.begin.u) //this unit is private, systems shouldn't mess with it.
set .begin.u=null
set .begin=recyclebin(integer(.begin)+1)
if(.begin==.end) then
set .begin=0
set .end=0
else
call TimerStart(recycler, .begin.schedule-TimerGetElapsed(NOW), false, function recyclebin.Recycle)
endif
endmethod
endstruct
private function init takes nothing returns nothing
set recycler=CreateTimer()
set NOW=CreateTimer()
call TimerStart(NOW,43200,true,null)
endfunction
struct xefx[MAX_INSTANCES]
public integer tag=0
private unit dummy
private effect fx=null
private real zang=0.0
private integer r=255
private integer g=255
private integer b=255
private integer a=255
private integer abil=0
static method create takes real x, real y, real facing returns xefx
local xefx this=xefx.allocate()
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, facing*bj_RADTODEG)
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
return this
endmethod
method operator owner takes nothing returns player
return GetOwningPlayer(this.dummy)
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(this.dummy,p,false)
endmethod
method operator teamcolor= takes playercolor c returns nothing
call SetUnitColor(this.dummy,c)
endmethod
method operator scale= takes real value returns nothing
call SetUnitScale(this.dummy,value,value,value)
endmethod
//! textmacro XEFX_colorstuff takes colorname, colorvar
method operator $colorname$ takes nothing returns integer
return this.$colorvar$
endmethod
method operator $colorname$= takes integer value returns nothing
set this.$colorvar$=value
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
//! endtextmacro
//! runtextmacro XEFX_colorstuff("red","r")
//! runtextmacro XEFX_colorstuff("green","g")
//! runtextmacro XEFX_colorstuff("blue","b")
//! runtextmacro XEFX_colorstuff("alpha","a")
method recolor takes integer r, integer g , integer b, integer a returns nothing
set this.r=r
set this.g=g
set this.b=b
set this.a=a
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
implement optional ARGBrecolor
method operator abilityid takes nothing returns integer
return this.abil
endmethod
method operator abilityid= takes integer a returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(a!=0) then
call UnitAddAbility(this.dummy,a)
endif
set this.abil=a
endmethod
method operator abilityLevel takes nothing returns integer
return GetUnitAbilityLevel( this.dummy, this.abil)
endmethod
method operator abilityLevel= takes integer newLevel returns nothing
call SetUnitAbilityLevel(this.dummy, this.abil, newLevel)
endmethod
method flash takes string fx returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx,this.dummy,"origin"))
endmethod
method operator xyangle takes nothing returns real
return GetUnitFacing(this.dummy)*bj_DEGTORAD
endmethod
method operator xyangle= takes real value returns nothing
call SetUnitFacing(this.dummy,value*bj_RADTODEG)
endmethod
method operator zangle takes nothing returns real
return this.zang
endmethod
method operator zangle= takes real value returns nothing
local integer i=R2I(value*bj_RADTODEG+90.5)
set this.zang=value
if(i>=180) then
set i=179
elseif(i<0) then
set i=0
endif
call SetUnitAnimationByIndex(this.dummy, i )
endmethod
method operator x takes nothing returns real
return GetUnitX(this.dummy)
endmethod
method operator y takes nothing returns real
return GetUnitY(this.dummy)
endmethod
method operator z takes nothing returns real
return GetUnitFlyHeight(this.dummy)
endmethod
method operator z= takes real value returns nothing
call SetUnitFlyHeight(this.dummy,value,0)
endmethod
method operator x= takes real value returns nothing
call SetUnitX(this.dummy,value)
endmethod
method operator y= takes real value returns nothing
call SetUnitY(this.dummy,value)
endmethod
method operator fxpath= takes string newpath returns nothing
if (this.fx!=null) then
call DestroyEffect(this.fx)
endif
if (newpath=="") then
set this.fx=null
else
set this.fx=AddSpecialEffectTarget(newpath,this.dummy,"origin")
endif
endmethod
method hiddenReset takes string newfxpath, real newfacing returns nothing
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
local real z = this.z
local real za = this.zangle
local integer level = this.abilityLevel
set fxpath=null
call RemoveUnit(this.dummy)
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, newfacing*bj_RADTODEG)
if(level != 0) then
call UnitAddAbility(this.dummy, abilityid)
endif
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
set this.z = z
set zangle = za
endmethod
private method onDestroy takes nothing returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(this.fx!=null) then
call DestroyEffect(this.fx)
set this.fx=null
endif
if (recyclebin.end==MAX_INSTANCES) then
//I'd like to see this happen...
call TimerStart(recycler,0,false,function recyclebin.Recycle)
call ExplodeUnitBJ(this.dummy)
else
set recyclebin.end.u=this.dummy
set recyclebin.end.schedule=TimerGetElapsed(NOW)+RECYCLE_DELAY
set recyclebin.end= recyclebin( integer(recyclebin.end)+1)
if( recyclebin.end==1) then
call TimerStart(recycler, RECYCLE_DELAY, false, function recyclebin.Recycle)
endif
call SetUnitOwner(this.dummy,Player(15),false)
endif
set this.dummy=null
endmethod
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xepreload initializer init requires xebasic, optional TimerUtils
//******************************************************************************
// xepreload 0.8
// ---------
// Ah, the joy of preloading abilities, it is such a necessary evil...
// Notice you are not supposed to use this system in places outside map init
//
// This one does the preloading and tries to minimize the hit on loading time
// for example, it only needs one single native call per ability preloaded.
//
//******************************************************************************
//==============================================================================
globals
private unit dum=null
endglobals
private keyword DebugIdInteger2IdString
//inline friendly (when debug mode is off..)
function XE_PreloadAbility takes integer abilid returns nothing
call UnitAddAbility(dum, abilid)
static if DEBUG_MODE then
if(dum==null) then
call BJDebugMsg("XE_PreloadAbility: do not load abilities after map init or during structs' onInit")
elseif GetUnitAbilityLevel(dum, abilid) == 0 then
call BJDebugMsg("XE_PreloadAbility: Ability "+DebugIdInteger2IdString.evaluate(abilid)+" does not exist.")
endif
endif
endfunction
// ................................................................................
//================================================================================
// Convert a integer id value into a 4-letter id code.
// * Taken from cheats.j so I don't have to code it again.
// * Used only on debug so making a whole library for it seemed silly
// * Private so people don't begin using xepreload just to call this function....
// * It will not work correctly if you paste this code in the custom script section
// due to the infamous % bug. Then again, if you do that then you probably
// deserve it....
//
private function DebugIdInteger2IdString takes integer value returns string
local string charMap = ".................................!.#$%&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
local string result = ""
local integer remainingValue = value
local integer charValue
local integer byteno
set byteno = 0
loop
set charValue = ModuloInteger(remainingValue, 256)
set remainingValue = remainingValue / 256
set result = SubString(charMap, charValue, charValue + 1) + result
set byteno = byteno + 1
exitwhen byteno == 4
endloop
return result
endfunction
//--------------------------------
private function kill takes nothing returns nothing
call RemoveUnit(dum)
set dum=null
static if (LIBRARY_TimerUtils ) then
call ReleaseTimer( GetExpiredTimer() )
else
call DestroyTimer(GetExpiredTimer())
endif
endfunction
private function init takes nothing returns nothing
local timer t
set dum = CreateUnit( Player(15), XE_DUMMY_UNITID, 0,0,0)
if( dum == null) then
debug call BJDebugMsg("xePreload : XE_DUMMY_UNITID ("+DebugIdInteger2IdString.evaluate(XE_DUMMY_UNITID)+") not added correctly to the map.")
endif
static if (LIBRARY_TimerUtils) then
set t=NewTimer()
else
set t=CreateTimer()
endif
call TimerStart(t,0.0,false,function kill)
set t=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CTL /* v1.2.0.3
*************************************************************************************
*
* CTL or Constant Timer Loop provides a loop for constant merged timers of timeout .03125
*
* Similar to T32 but pauses timer when no structs have instances and removes structs
* from timer trigger when those structs have no instances.
*
* This can also create new timers after destroying a previous timer and generates less
* code in the module. It also generates no triggers so long as the module is implemented
* at the top of the struct.
*
************************************************************************************
*
* module CTL
*
* Allows creation/destruction of timers in a struct. Provides instancing of those timers.
*
* - static method create takes nothing returns thistype
* - method destroy takes nothing returns nothing
*
* CTL (optional)
* local variables, code before running any timers
* CTLExpire (not optional)
* timer code
* CTLNull (optional)
* null any locals, runs after all timers
* CTLEnd (not optional)
*
* module CT32
*
* Converts struct into a timer group. Allows the timer group to be started and stopped.
* Instancing and looping through active timers is up to the user.
*
* - static method start takes nothing returns nothing
* - static method stop takes nothing returns nothing
*
* CT32 (not optional)
* timer code
* CT32End (not optional)
*
* struct TimerGroup32 extends array
*
* Allows for the creation of timer groups. Timer instancing and looping is entirely up
* to the user.
*
* - static method create takes code func returns thistype
* - method destroy takes nothing returns nothing
* - method start takes nothing returns nothing
* - method stop takes nothing returns nothing
*
************************************************************************************/
globals
private integer tgc = 0 //timer group count
private integer array tgr //timer group recycler
private integer ic=0 //instance count
private integer tc=0 //timer count
private integer array rf //root first
private integer array n //next
private integer array p //previous
private integer array th //timer head
private integer array ns //next stack
private trigger t=CreateTrigger()
private timer m=CreateTimer()
private triggercondition array ct
private conditionfunc array rc
private boolean array e32 //enabled
private integer array i32r //ct32 recycler
private integer i32cr = 0 //ct32 count recycler
private boolean array ir32 //is recycling
private boolean array id32 //is destroying
endglobals
private function E takes nothing returns nothing
local integer i=ns[0]
set ns[0]=0
loop
exitwhen 0==i
if (0==p[i]) then
if (0==n[i]) then
call TriggerRemoveCondition(t,ct[th[i]])
set ct[th[i]]=null
set tc=tc-1
set rf[th[i]]=0
else
set rf[th[i]]=n[i]
set p[n[i]]=0
endif
else
set p[n[i]]=p[i]
set n[p[i]]=n[i]
endif
set n[i]=n[0]
set n[0]=i
set i=ns[i]
endloop
loop
exitwhen 0 == i32cr
set i32cr = i32cr - 1
set i = i32r[i32cr]
if (not e32[i]) then
call TriggerRemoveCondition(t,ct[i])
set ct[i] = null
if (id32[i]) then
set tgr[i] = tgr[0]
set tgr[0] = i
set id32[i] = false
endif
set ir32[i] = false
endif
endloop
if (0==tc) then
call PauseTimer(m)
else
call TriggerEvaluate(t)
endif
endfunction
private function CT takes integer r returns integer
local integer i
local integer f
if (0==n[0]) then
set i=ic+1
set ic=i
else
set i=n[0]
set n[0]=n[i]
endif
set th[i]=r
set ns[i]=-1
set f=rf[r]
if (0==f) then
set n[i]=0
set p[i]=0
set rf[r]=i
set ct[r]=TriggerAddCondition(t,rc[r])
//set ct[r] = null
if (0==tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc=tc+1
else
set n[i]=f
set p[i]=0
set p[f]=i
set rf[r]=i
endif
return i
endfunction
private function DT takes integer t returns nothing
debug if (0>ns[t]) then
set ns[t]=ns[0]
set ns[0]=t
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"TIMER LOOP ERROR: ATTEMPT TO DESTROY NULL TIMER")
debug endif
endfunction
private function A takes code c returns integer
local integer i = tgr[0]
if (0 == i) then
set i = tgc + 1
set tgc = i
else
set tgr[0] = tgr[i]
endif
set rc[i]=Condition(c)
return i
endfunction
private function A32 takes integer i returns nothing
if (not (e32[i] or id32[i])) then
if (ir32[i]) then
set ir32[i] = false
else
set ct[i] = TriggerAddCondition(t, rc[i])
endif
if (0 == tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc = tc + 1
set e32[i] = true
endif
endfunction
private function SR32 takes integer i returns nothing
if (e32[i]) then
if (not (ir32[i] or id32[i])) then
set i32r[i32cr] = i
set i32cr = i32cr + 1
set ir32[i] = true
endif
set e32[i] = false
set tc = tc - 1
endif
endfunction
private function DT32 takes integer i returns nothing
if (not id32[i]) then
if (not ir32[i]) then
set ir32[i] = true
set tc = tc - 1
set i32r[i32cr] = i
set i32cr = i32cr + 1
set e32[i] = false
endif
set id32[i] = true
endif
endfunction
private keyword r
private keyword e
module CTL
static integer rctl32
static method create takes nothing returns thistype
return CT(rctl32)
endmethod
method destroy takes nothing returns nothing
call DT(this)
endmethod
static method ectl32 takes nothing returns boolean
local thistype this=rf[rctl32]
endmodule
module CTLExpire
implement CTL
loop
exitwhen 0==this
endmodule
module CTLNull
set this=n[this]
endloop
endmodule
module CTLEnd
implement CTLNull
return false
endmethod
private static method onInit takes nothing returns nothing
set rctl32 = A(function thistype.ectl32)
endmethod
endmodule
module CT32
static integer rctl32
static method start takes nothing returns nothing
call A32(rctl32)
endmethod
static method stop takes nothing returns nothing
call SR32(rctl32)
endmethod
static method ectl32 takes nothing returns boolean
endmodule
module CT32End
return false
endmethod
private static method onInit takes nothing returns nothing
set rctl32 = A(function thistype.ectl32)
endmethod
endmodule
struct TimerGroup32 extends array
static method create takes code c returns thistype
return A(c)
endmethod
method destroy takes nothing returns nothing
call DT32(this)
endmethod
method start takes nothing returns nothing
call A32(this)
endmethod
method stop takes nothing returns nothing
call SR32(this)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Missile /* version 2.5.1
*************************************************************************************
*
* Creating custom projectiles in Warcraft III.
*
* Major goal:
* No unessary external requirements.
* Implements code optional.
*
* Philosophy:
* I want that feature --> Compiler writes that code into your map script.
* I don't want that --> Compiler ignores that code completly.
*
* Important:
* Take yourself 2 minutes time to setup Missile correctly.
* Otherwise I can't guarantee, that Missile works the way you want.
* Once the setup is done, you can check out some examples and Missile will be easy
* to use for everyone. I promise it.
*
* Do the setup at:
*
* 1.) Import instruction
* 2.) Global configuration
* 3.) Function configuration
*
* Credits to Dirac, emjlr3, AceHart, Bribe, Wietlol,
* Nestharus, Maghteridon96, Vexorian and Zwiebelchen.
*
*************************************************************************************
*
* */ requires /*
*
* - Missile requires nothing.
*
*************************************************************************************
*
* Optional requirements listed can reduce overall code generation,
* add safety mechanisms, decrease overhead and optimize handle management.
* For a better overview I put them into blocks.
*
* I recommend to use at least one per block in your map.
*
* a) For best debug results: ( Useful )
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*
* b) Fatal error protection: ( Case: unit out moves of world bounds )
* - WorldBounds is safer than BoundSentinel.
* - WorldBounds adds more overhead than BoundSentinel.
* - Using none of these two forces Missile to switch from SetUnitX/Y to the SetUnitPosition native.
* */ optional WorldBounds /* githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
* */ optional BoundSentinel /* wc3c.net/showthread.php?t=102576
*
* c) Handle recycling: ( Performace gain, memory management )
* - uses MissileRecylcer > Dummy > xedummy.
* */ optional MissileRecycler /* hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
* */ optional Dummy /* github.com/nestharus/JASS/blob/master/jass/Systems/Dummy/Dummy.w3x
* */ optional xedummy /* wc3c.net/showthread.php?t=101150
*
* d) Unit indexing: ( Avoid an onIndex event )
* - not required for Missile. Only if you use one already.
* */ optional UnitIndexer /* github.com/nestharus/JASS/tree/master/jass/Systems/Unit%20Indexer
*
************************************************************************************
*
* 1. Import instruction
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* • Copy Missile into to your map.
* • You need a dummy unit, using Vexorians "dummy.mdx".
* This unit must use the locust and crow form ability. ( Aloc & Amrf )
* ¯¯¯¯
*
* 2. Global configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Seven constants to setup!
*/
globals
/**
* Missiles are moved periodically. An interval of 1./32. is recommended.
* • Too short timeout intervals may cause performance issues.
* • Too large timeout intervals may look fishy.
*/
public constant real TIMER_TIMEOUT = 1./32.
/**
* Owner of all Missile dummies. Should be a neutral player in your map.
*/
public constant player NEUTRAL_PASSIVE = Player(15)
/**
* Raw code of the dummy unit. Object Editor ( F6 )
* • Must be correct, otherwise missile dummies can neither be recycled nor destroyed.
* • Units of other type ids will not be thrown into the recycler bin.
*/
public constant integer DUMMY_UNIT_ID = 'e00E'
/**
* The maximum collision size used in your map. If unsure use 197. ( Town hall collision )
* • Applies for all types of widgets.
* • A precise value can improve Missile's performance,
* since smaller values enumerate less widgtes per loop per missile.
*/
public constant real MAXIMUM_COLLISION_SIZE = 197.
/**
* Collision types for missiles. ( Documentation only )
* Missile decides internally each loop which type of collision is required.
* • Uses circular collision dectection for speed < collision. ( Good accuracy, best performance )
* • Uses rectangle collision for speed >= collision. ( Best accuracy, normal performance )
*/
public constant integer COLLISION_TYPE_CIRCLE = 0
public constant integer COLLISION_TYPE_RECTANGLE = 1
/**
* Determine when rectangle collision is required to detect nearby widgets.
* • The smaller the factor the earlier rectangle collision is used. ( by default 1. )
* • Formula: speed >= collision*Missile_COLLISION_ACCURACY_FACTOR
*/
public constant real COLLISION_ACCURACY_FACTOR = 1.
// Optional toogles:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Set booleans listed below to "true" and the compiler will write
// the feature into your map. Otherwise this code is completly ignored.
// • Yay, I want that --> "true"
// • Naah that's useless for me --> "false"
/**
* USE_COLLISION_Z_FILTER enables z axis checks for widget collision. ( Adds minimal overhead )
* Use it when you need:
* • Missiles flying over or under widgets.
* • Determine between flying and walking units.
*/
public constant boolean USE_COLLISION_Z_FILTER = true
/**
* WRITE_DELAYED_MISSILE_RECYCLING enables a delayed dummy recycling system. ( Very recommended )
* Use it if:
* • You use a dummy recycling library like MissileRecycler, Dummy or xedummy.
* • You want to properly display death animations of effects added to missiles.
*/
public constant boolean WRITE_DELAYED_MISSILE_RECYCLING = true
/**
* DELAYED_MISSILE_DEATH_ANIMATION_TIME is the delay in seconds
* Missile holds back a dummy, before recycling it.
* • The time value does not have to be precise.
* • Requires WRITE_DELAYED_MISSILE_RECYCLING = true
*/
private constant real DELAYED_MISSILE_DEATH_ANIMATION_TIME = 3.
/**
* USE_DESTRUCTABLE_FILTER and USE_ITEM_FILTER are redundant constants from previous Missile versions.
* They do nothing, but remain for backwards compatibilty.
* From Missile version 1.5 on all widget collisions are always enabled.
*/
public constant boolean USE_DESTRUCTABLE_FILTER = true
public constant boolean USE_ITEM_FILTER = true
endglobals
/*
* 3. Function configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Four functions to setup!
*/
/**
* GetUnitBodySize(unit) returns a fictional value for z - axis collision.
* You have two options:
* • One constant value shared over all units.
* • Dynamic values based on handle id, type id, unit user data, scaling or other parameters.
*/
function GetUnitBodySize takes unit whichUnit returns real
return 100.// Other example: return LoadReal(hash, GetHandleId(whichUnit), KEY_UNIT_BODY_SIZE)
endfunction
/**
* Same as GetUnitBodySize, but for destructables.
* Using occluder height is an idea of mine. Of course you can use your own values.
*/
function GetDestructableHeight takes destructable d returns real
return GetDestructableOccluderHeight(d)// Other example: return 100.
endfunction
/**
* Same as GetUnitBodySize, but for items.
* Again it's up to you to figure out a fictional item height.
*/
function GetItemHeight takes item i returns real
return 16.
endfunction
/**
* Unit indexers and missiles ( Only if you don't use a dummy recycling library )
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* It is most likely intended that projectiles don't run through a unit indexing process.
* ToogleUnitIndexer runs:
* • Directly before a dummy is created.
* • Directly after dummy unit creation.
*
* Please return the previous setup of your indexing tool ( enabled, disabled ),
* so Missile can properly reset it to the original state.
*/
private function ToogleUnitIndexer takes boolean enable returns boolean
local boolean prev = true//UnitIndexer.enabled
// set UnitIndexer.enabled = enable
return prev
endfunction
/**
* 4. API
* ¯¯¯¯¯¯
*/
//! novjass ( Disables the compiler until the next endnovjass )
// Custom type Missile for your projectile needs.
struct Missile extends array
// Constants:
// ==========
//
readonly static constant string ORIGIN = "origin"
// • Attach point name for fxs on dummies.
readonly static constant real HIT_BOX = (2./3.)
// • Fictional hit box for homing missiles.
// while 0 would be the toe and 1 the head of a unit.
// Available creators:
// ===================
//
static method createEx takes unit missileDummy, real impactX, real impactY, real impactZ returns Missile
// • Core creator method.
// • May launches any unit.
// • Units of type Missile_DUMMY_UNIT_ID get recycled in the end.
static method create takes real x, real y, real z, real angleInRadians, real distanceToTravel, real endZ returns Missile
// • Converts arguments to fit into createEx, then calls createEx.
static method createXYZ takes real x, real y, real z, real impactX, real impactY, real impactZ returns Missile
// • Converts arguments to fit into createEx, then calls createEx.
// Available destructors:
// ======================
//
return true
// • Core destructor.
// • Returning true in any of the interface methods of the MissileStruct module
// will destroy that instance instantly.
method destroy takes nothing returns nothing
// • Destroys the missile during the next timer callback.
method terminate takes nothing returns nothing
// • Destroys the missile instantly.
// Fields you can set and read directly:
// =====================================
//
unit source
unit target // For homing missiles.
real distance // Distance traveled.
player owner // Pseudo owner for faster onCollide evaluation. The proper dummy owner remains PLAYER_NEUTRAL_PASSIVE.
real speed // Vector lenght for missile movement in plane x / y. ( DOES NOT TAKE THE TIMER TIMEOUT IN ACCOUNT )
real acceleration
real damage
real turn // Set a turn rate for missiles.
integer data // For data transfer set and read data.
boolean recycle // Is automatically set to true, when a Missile reaches it's destination.
boolean wantDestroy // Set wantDestroy to true, to destroy a missile during the next timer callback.
// Neither collision nor collisionZ accept values below zero.
real collision // Collision size in plane x / y.
real collisionZ // Collision size in z - axis. ( deprecated )
// Fields you can only read:
// =========================
//
readonly boolean allocated
readonly unit dummy// The dummy unit of this missile.
// Position members for you needs.
readonly MissilePosition origin// Grants access to readonly members of MissilePosition,
readonly MissilePosition impact// which are "x", "y", "z", "angle", "distance", "slope" and the pitch angle "alpha".
// Furthermore method origin.move(x, y, z) and impact.move(x, y, z).
readonly real terrainZ
readonly real x
readonly real y
readonly real z
readonly real angle// Current angle in radians.
// Method operators for set and read:
// ==================================
//
method operator model= takes string modelFile returns nothing
method operator model takes nothing returns string
// • Adds an effect handle on a missile dummy to it's "origin".
// • You can read the file path.
// • For multiple effects access "this.dummy" in your struct.
method operator scale= takes real value returns nothing
method operator scale takes nothing returns real
// • Set and read the scaling of the dummy unit.
method operator curve= takes real value returns nothing
method operator curve takes nothing returns real
// • Enables curved movement for your missile. ( Pass in radians, NOT degrees )
// • Do not pass in PI/2.
method operator arc= takes real value returns nothing
method operator arc takes nothing returns real
// • Enables arcing movement for your missile. ( Pass in radians, NOT degrees )
// • Do not pass in PI/2.
// Methods for missile movement:
// =============================
//
method bounce takes nothing returns nothing
// • Moves the MissilePosition "origin" to the current missile coordinates.
// • Resets the distance traveled to 0.
method deflect takes real tx, real ty returns nothing
// • Deflect the missile towards tx, ty. Then calls bounce.
method deflectEx takes real tx, real ty, real tz returns nothing
// • Deflect the missile towards tx, ty, tz. Then calls bounce.
method flightTime2Speed takes real duration returns nothing
// • Converts a fly time to a vector lenght for member "speed".
// • Does not take acceleration into account. ( Disclaimer )
method setMovementSpeed takes real value returns nothing
// • Converts Warcraft III movement speed to a vector lenght for member "speed".
// Methods for missile collision: ( all are hashtable entries )
// ==============================
// By default a widget can only be hit once per missile.
//
method hitWidget takes widget w returns nothing
// • Saves a widget in the memory as hit by this instance.
method hasHitWidget takes widget w returns boolean
// • Returns true, if "w" has been hit by this instance.
method removeHitWidget takes widget w returns nothing
// • Removes a widget from this missile's memory for widget collision. ( Can hit "w" again )
method flushHitWidgets takes nothing returns nothing
// • Flushes a missile's memory for widget collision. ( All widgets can be hit again )
method enableHitAfter takes widget w, real seconds returns nothing
// • Automatically calls removeHitWidget(w) after "seconds" time. ( Can hit "w" again after given time )
// Module MissileStruct:
// =====================
//
module MissileLaunch // ( optional )
module MissileStruct
// • Enables the entire missile interface for that struct.
// Interface in structs: ( Must implement module MissileStruct )
// =====================
//
// • Write one, many or all of the static method below into your struct.
// • Missiles launched in this struct will run those methods, when their events fire.
//
// • All of those static methods must return a boolean.
// a) return true --> destroys the missile instance instantly.
// b) return false --> keep on flying.
// Available static method:
// ========================
//
static method onCollide takes Missile missile, unit hit returns boolean
// • Runs for units in collision range of a missile.
static method onDestructable takes Missile missile, destructable hit returns boolean
// • Runs for destructables in collision range of a missile.
static method onItem takes Missile missile, item hit returns boolean
// • Runs for items in collision range of a missile.
static method onTerrain takes Missile missile returns boolean
// • Runs when a missile collides with the terrain. ( Ground and cliffs )
static method onFinish takes Missile missile returns boolean
// • Runs only when a missile reaches it's destination.
// • However does not run, if a Missile is destroyed in another method.
static method onPeriod takes Missile missile returns boolean
// • Runs every Missile_TIMER_TIMEOUT seconds.
static method onRemove takes Missile missile returns boolean
// • Runs when a missile is destroyed.
// • Unlike onFinish, onRemove will runs ALWAYS when a missile is destroyed!!!
//
// For onRemove the returned boolean has a unique meaning:
// • Return true will recycle this missile delayed. ( Only if WRITE_DELAYED_MISSILE_RECYCLING = true )
// • Return false will recycle this missile right away.
static method launch takes Missile toLaunch returns nothing
// • Well ... Launches this Missile.
// • Missile "toLaunch" will behave as declared in the struct. ( static methods from above )
// Misc: ( From the global setup )
// =====
//
// Constants:
// ==========
//
public constant real TIMER_TIMEOUT
public constant player NEUTRAL_PASSIVE
public constant integer DUMMY_UNIT_ID
public constant real MAXIMUM_COLLISION_SIZE
public constant boolean USE_COLLISION_Z_FILTER
public constant boolean WRITE_DELAYED_MISSILE_RECYCLING
public constant boolean USE_DESTRUCTABLE_FILTER
public constant boolean USE_ITEM_FILTER
readonly static constant string ORIGIN
readonly static constant real HIT_BOX
// Functions:
// ==========
//
public function GetLocZ takes real x, real y returns real
function GetUnitBodySize takes unit whichUnit returns real
function GetDestructableHeight takes destructable d returns real
function GetItemHeight takes item i returns real
//========================================================================
// Missile system. Make changes carefully.
//========================================================================
//! endnovjass ( Enables the compiler )
// Hello and welcome to Missile.
// I wrote a guideline for every piece of code inside Missile, so you
// can easily understand how the it gets compiled and evaluated.
//
// Let's go!
globals
// Core constant handle variables of Missile.
private constant trigger CORE = CreateTrigger()
private constant trigger MOVE = CreateTrigger()
private constant timer TMR = CreateTimer()
private constant location LOC = Location(0., 0.)
private constant rect RECT = Rect(0., 0., 0., 0.)
private constant group GROUP = CreateGroup()
private constant hashtable HASH = InitHashtable()
// For starting and stopping the timer.
private integer active = 0
// Arrays for data structure.
private integer array instances
private Missile array missileList
private boolexpr array expression
private triggercondition array condition
private integer array remove
private boolean array destroying
private boolean array recycling
private integer array nextNode
private integer array prevNode
// Internal widget filter functions.
private boolexpr destFilter
private boolexpr itemFilter
private boolexpr unitFilter
endglobals
public function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
// For WRITE_DELAYED_MISSILE_RECYCLING = true Missile will hold back
// dummies for DELAYED_MISSILE_DEATH_ANIMATION_TIME before they are recylced. ( Code placed in a static if )
//
//! runtextmacro optional WRITE_MISSILE_RECYCLE_BIN("WRITE_DELAYED_MISSILE_RECYCLING", "DELAYED_MISSILE_DEATH_ANIMATION_TIME")
// The code of WRITE_MISSILE_POSITION_CODE boxes a missiles position and does the required trigonometry.
//
//! runtextmacro WRITE_MISSILE_POSITION_CODE()
// Missiles structure works like a linked list with the folling methods:
// allocateCollection(), allocateNode(), insertFront(node) and remove()
//
private keyword MissileStructure
struct Missile extends array
implement MissileStructure
// Constants:
// ==========
//
// Attach point name for effects created by model=.
readonly static constant string ORIGIN = "origin"
// Set a ficitional hit box in range of 0 to 1,
// while 0 is the toe and 1 the head of a unit.
readonly static constant real HIT_BOX = (2./3.)
// DEBUG_MODE only members:
// ========================
//
// Checks for double launching. Throws an error message.
debug boolean launched
// Private members:
// ================
//
// The position of a missile using curve= does not
// match the position used by Missile's trigonometry.
// Therefore we need this member two times.
// Readable x / y / z for your needs and posX / posY for cool mathematics.
private real posX
private real posY
private real dist// distance
// Readonly members:
// =================
//
// Prevents a double free case.
readonly boolean allocated
// The dummy unit.
readonly unit dummy
// Position members for your needs.
readonly MissilePosition origin// Grants access to readonly members of MissilePosition,
readonly MissilePosition impact// which are "x", "y", "z", "angle", "distance", "slope" and "alpha".
readonly real terrainZ
readonly real x
readonly real y
readonly real z
readonly real angle// Current angle
readonly real prevX
readonly real prevY
readonly real prevZ
// Collision detection type. ( Evaluated new in each loop )
readonly integer collisionType// Current collision type ( circular or rectangle )
// Members you can set:
// ====================
//
unit source
unit target // For homing missiles.
real distance // Distance traveled.
player owner // Pseudo owner for faster onCollide evaluation. The proper dummy owner is PLAYER_NEUTRAL_PASSIVE.
real speed // Vector lenght for missile movement in plane x / y.
real acceleration
real damage
integer data // For data transfer set and read data.
boolean recycle // Is set to true, when a Missile reaches it's destination.
real turn // Sets a turn rate for a missile.
real collision // Collision size in plane x / y.
// Setting collision z is deprecated since Missile v2.5.
method operator collisionZ= takes real value returns nothing
endmethod
method operator collisionZ takes nothing returns real
return collision
endmethod
// Operator overloading:
// =====================
//
// Special effect on the missile dummy. For multiple effect attaching, access unit "dummy" directly.
private effect sfx
private string path
method operator model= takes string file returns nothing
if sfx != null then
call DestroyEffect(sfx)
set sfx = null
endif
// null and ""
if StringLength(file) > 0 then
set sfx = AddSpecialEffectTarget(file, dummy, ORIGIN)
set path = file
else
set path = null
endif
endmethod
method operator model takes nothing returns string
return path
endmethod
real open
// Enables curved movement for your missile.
// Remember that the result of Tan(PI/2) is infinity.
method operator curve= takes real value returns nothing
set open = Tan(value)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)
endmethod
real height
// Enables arcing movement for your missile.
method operator arc= takes real value returns nothing
set height = Tan(value)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)
endmethod
private real scaling
method operator scale= takes real value returns nothing
call SetUnitScale(dummy, value, 0., 0.)
set scaling = value
endmethod
method operator scale takes nothing returns real
return scaling
endmethod
// Methods:
// ========
//
method bounce takes nothing returns nothing
call origin.move(x, y, z)
set dist = 0.
endmethod
method deflect takes real tx, real ty returns nothing
local real a = 2.*Atan2(ty - y, tx - x) + bj_PI - angle
call impact.move(x + (origin.distance - dist)*Cos(a), y + (origin.distance - dist)*Sin(a), impact.z)
call bounce()
endmethod
method deflectEx takes real tx, real ty, real tz returns nothing
call impact.move(impact.x, impact.y, tz)
call deflect(tx, ty)
endmethod
method flightTime2Speed takes real duration returns nothing
set speed = RMaxBJ(0.00000001, (origin.distance - dist)*Missile_TIMER_TIMEOUT/RMaxBJ(0.00000001, duration))
endmethod
method setMovementSpeed takes real value returns nothing
set speed = value*Missile_TIMER_TIMEOUT
endmethod
boolean wantDestroy// For "true" a missile is destroyed on the next timer callback. 100% safe.
method destroy takes nothing returns nothing
set wantDestroy = true
endmethod
// Instantly destroys a missile.
method terminate takes nothing returns nothing
if allocated then
call remove()
call impact.destroy()
call origin.destroy()
call DestroyEffect(sfx)
call FlushChildHashtable(HASH, this)
if GetUnitTypeId(dummy) == Missile_DUMMY_UNIT_ID then
// MissileRecycler > Dummy > xe.
static if LIBRARY_MissileRecycler then
call RecycleMissile(dummy)
elseif LIBRARY_Dummy and Dummy.create.exists then
call Dummy[dummy].destroy()
elseif LIBRARY_xedummy and xedummy.release.exists then
call xedummy.release(dummy)
else
call RemoveUnit(dummy)
endif
endif
set sfx = null
set source = null
set target = null
set dummy = null
set owner = null
endif
endmethod
// Runs in createEx.
//! textmacro MISSILE_RESET_ALL_MEMBERS
set path = null
set speed = 0.
set acceleration = 0.
set distance = 0.
set dist = 0
set dist = 0.
set height = 0.
set turn = 0.
set open = 0.
set collision = 0.
set collisionType = 0
set stackSize = 0
set scaling = 1.
set wantDestroy = false
set recycle = false
//! endtextmacro
// Launches a dummy of your choice.
static method createEx takes unit missileDummy, real impactX, real impactY, real impactZ returns thistype
local thistype this = thistype.allocateNode()
local real originX = GetUnitX(missileDummy)
local real originY = GetUnitY(missileDummy)
local real originZ = GetUnitFlyHeight(missileDummy)
//
//! runtextmacro MISSILE_RESET_ALL_MEMBERS()
//
set origin = MissilePosition.create(originX, originY, originZ)
set impact = MissilePosition.create(impactX, impactY, impactZ)
call MissilePosition.link(origin, impact)
set posX = originX
set posY = originY
set x = originX
set y = originY
set z = originZ
set angle = origin.angle
set dummy = missileDummy
call SetUnitFlyHeight(missileDummy, originZ, 0.)
call SaveUnitHandle(HASH, this, GetHandleId(missileDummy), missileDummy)
//
static if LIBRARY_ErrorMessage then
debug call ThrowWarning(GetUnitTypeId(missileDummy) == 0, "Missile", "createEx", "missileDummy", this, "Invalid missile dummy unit ( null )!")
endif
debug set launched = false
return this
endmethod
// Freaky static if ensures these libraries really don't exist.
static if not LIBRARY_MissileRecycler and not LIBRARY_Dummy and not Dummy.create.exists and not LIBRARY_xe_dummy and not xe_dummy.new.exists then
private static method newMissileUnit takes real x, real y, real z, real face returns unit
local boolean prev = ToogleUnitIndexer(false)
set bj_lastCreatedUnit = CreateUnit(Missile_NEUTRAL_PASSIVE, Missile_DUMMY_UNIT_ID , x, y, face)
call ToogleUnitIndexer(prev)
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call UnitAddAbility(bj_lastCreatedUnit, 'Amrf')
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0.)
call PauseUnit(bj_lastCreatedUnit, true)
return bj_lastCreatedUnit
endmethod
endif
// MissileRecylcer > Dummy > xe > Missile.
//! textmacro MISSILE_GET_DUMMY_FROM_LIBRARY
static if LIBRARY_MissileRecycler then
return createEx(GetRecycledMissile(x, y, z, angle*bj_RADTODEG), impactX, impactY, impactZ)
elseif LIBRARY_Dummy and Dummy.create.exists then
local Dummy dummy = Dummy.create(x, y, angle*bj_RADTODEG)
call SetUnitFlyHeight(dummy.unit, z, 0.)
return createEx(dummy.unit, impactX, impactY, impactZ)
elseif LIBRARY_xedummy and xedummy.new.exists then
set bj_lastCreatedUnit = xedummy.new(Missile_NEUTRAL_PASSIVE, x, y, angle*bj_RADTODEG)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0.)
return createEx(bj_lastCreatedUnit, impactX, impactY, impactZ)
else
return createEx(Missile.newMissileUnit(x, y, z, angle*bj_RADTODEG), impactX, impactY, impactZ)
endif
//! endtextmacro
// Wrapper to createEx.
static method create takes real x, real y, real z, real angle, real distance, real impactZ returns thistype
local real impactX = x + distance*Cos(angle)
local real impactY = y + distance*Sin(angle)
// Get the dummy unit.
//! runtextmacro MISSILE_GET_DUMMY_FROM_LIBRARY()
endmethod
// Wrapper to createEx.
static method createXYZ takes real x, real y, real z, real impactX, real impactY, real impactZ returns thistype
local real angle = Atan2(impactY - y, impactX - x)
// Get the dummy unit.
//! runtextmacro MISSILE_GET_DUMMY_FROM_LIBRARY()
endmethod
// Missile motion takes place every Missile_TIMER_TIMEOUT
// before accessing each active struct.
static Missile temp = 0
static method move takes nothing returns boolean
local integer loops = 0 // Current iteration.
local integer limit = 150 // Set iteration border per trigger evaluation to avoid hitting the operation limit.
local thistype this = thistype.temp
local MissilePosition p
local real a
local real d
local unit u
local real newX
local real newY
local real vel
local real point
local real pitch
loop
exitwhen 0 == this or loops == limit
set p = origin
// Save previous, respectively current missile position.
set prevX = x
set prevY = y
set prevZ = z
// Evaluate the collision type.
set vel = speed
set speed = vel + acceleration
if vel < collision*Missile_COLLISION_ACCURACY_FACTOR then
set collisionType = Missile_COLLISION_TYPE_CIRCLE
else
set collisionType = Missile_COLLISION_TYPE_RECTANGLE
endif
// Update missile guidance to its intended target.
set u = target
if u != null then
if 0 == GetUnitTypeId(u) then
set target = null
else
call origin.move(x, y, z)
call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + GetUnitBodySize(u)*Missile.HIT_BOX)
set dist = 0
set height = 0
set curve = 0
endif
endif
set a = p.angle
// Update the missile facing angle depending on the turn ratio.
if 0. != turn and Cos(angle - a) < Cos(turn) then
if 0. > Sin(a - angle) then
set angle = angle - turn
else
set angle = angle + turn
endif
else
set angle = a
endif
// Update the missile position on the parabola.
set d = p.distance// origin - impact distance.
set recycle = dist + vel >= d
if recycle then// Maximum distance reached.
set point = d
set distance = distance + d - dist
else
set distance = distance + vel
set point = dist + vel
endif
set dist = point
set newX = posX + vel*Cos(angle)
set newY = posY + vel*Sin(angle)
set posX = newX
set posY = newY
// Update point(x/y) if a curving trajectory is defined.
set u = dummy
if 0. != open and target == null then
set vel = 4*open*point*(d - point)/p.square
set a = angle + bj_PI/2
set newX = newX + vel*Cos(a)
set newY = newY + vel*Sin(a)
set a = angle + Atan(-((4*open)*(2*point - d))/p.square)
else
set a = angle
endif
set x = newX
set y = newY
// Update pos z if an arc or height is set.
call MoveLocation(LOC, newX, newY)
set terrainZ = GetLocationZ(LOC)
set pitch = p.alpha
if 0. == height and 0. == pitch then
set z = p.z - terrainZ
else
set z = p.z - terrainZ + p.slope*point
if 0. != height and target == null then
set z = z + (4*height*point*(d - point)/p.square)
set pitch = pitch - Atan(((4*height)*(2*point - d))/p.square)*bj_RADTODEG
endif
endif
// Update the pitch angle of the dummy unit.
if GetUnitTypeId(u) == Missile_DUMMY_UNIT_ID then
call SetUnitAnimationByIndex(u, R2I(pitch + 90.5))
endif
// Move the missile dummy via native.
call SetUnitFlyHeight(u, z, 0.)
call SetUnitFacing(u, a*bj_RADTODEG)
// WorldBounds > BoundSentinel.
static if not LIBRARY_BoundSentinel and not LIBRARY_WorldBounds then
if RectContainsCoords(bj_mapInitialPlayableArea, newX, newY) then
call SetUnitX(u, newX)
call SetUnitY(u, newY)
endif
elseif LIBRARY_WorldBounds then
if newX < WorldBounds.maxX and newX > WorldBounds.minX and newY < WorldBounds.maxY and newY > WorldBounds.minY then
call SetUnitX(u, newX)
call SetUnitY(u, newY)
endif
else
call SetUnitX(u, newX)
call SetUnitY(u, newY)
endif
set loops = loops + 1
set this = nextNode[this]
endloop
set u = null
set thistype.temp = this
return this == 0
endmethod
// Widget collision API:
// =====================
//
// Runs automatically on widget collision.
method hitWidget takes widget w returns nothing
if w != null then
call SaveWidgetHandle(HASH, this, GetHandleId(w), w)
endif
endmethod
// All widget which have been hit return true.
method hasHitWidget takes widget w returns boolean
return HaveSavedHandle(HASH, this, GetHandleId(w))
endmethod
// Removes a widget from the missile's memory of hit widgets. ( This widget can be hit again )
method removeHitWidget takes widget w returns nothing
if w != null then
call RemoveSavedHandle(HASH, this, GetHandleId(w))
endif
endmethod
// Flushes a missile's memory for collision. ( All widgets can be hit again )
method flushHitWidgets takes nothing returns nothing
call FlushChildHashtable(HASH, this)
call hitWidget(dummy)
endmethod
// Tells missile to call removeHitWidget(w) after "seconds" time.
// Does not apply to widgets, which are already hit by this missile.
readonly integer stackSize
method enableHitAfter takes widget w, real seconds returns nothing
local integer id = GetHandleId(w)
if w != null then
if not HaveSavedInteger(HASH, this, id) then
call SaveInteger(HASH, this, id, stackSize)
call SaveInteger(HASH, this, stackSize, id)
set stackSize = stackSize + 1
endif
call SaveReal(HASH, this, id, seconds)// Set time.
endif
endmethod
method updateStack takes nothing returns nothing
local integer dex = 0
local integer id
local real time
loop
exitwhen dex == stackSize
set id = LoadInteger(HASH, this, dex)
set time = LoadReal(HASH, this, id) - Missile_TIMER_TIMEOUT
if time <= 0. or not HaveSavedHandle(HASH, this, id) then
set stackSize = stackSize - 1
set id = LoadInteger(HASH, this, stackSize)
call SaveInteger(HASH, this, dex, id)
call SaveInteger(HASH, this, id, dex)
// Enables hit.
call RemoveSavedHandle(HASH, this, id)
// Remove data from stack.
call RemoveSavedReal(HASH, this, id)
call RemoveSavedInteger(HASH, this, id)
call RemoveSavedInteger(HASH, this, stackSize)
else
call SaveReal(HASH, this, id, time)
set dex = dex + 1
endif
endloop
endmethod
// Widget collision code:
// ======================
//
private static boolean circle = true
//
// 1.) Rectangle collision for fast moving missiles with small collision radius.
//
// Runs for destructables and items in a rectangle.
// Checks if widget w is in collision range of a missile.
// Is not precise in z - axis collision.
private method isWidgetInRectangle takes 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) - terrainZ
local real dx = x - prevX
local real dy = y - prevY
local real s = (dx*(wx - prevX) + dy*(wy - prevY))/(dx*dx + dy*dy)
if s < 0. then
set s = 0.
elseif s > 1 then
set s = 1.
endif
set dx = (prevX + s*dx) - wx
set dy = (prevY + s*dy) - wy
return dx*dx + dy*dy <= distance*distance and dz + wz >= z - distance and dz <= z + distance
endmethod
//
// 2.) Circular collision detection for all other missiles.
//
// Returns true for widgets in a xyz collision range.
private method isWidgetInRange takes 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) - terrainZ
// collision in plane x and y, collision in z axis.
return IsUnitInRangeXY(dummy, wx, wy, distance) and dz + wz >= z - distance and dz <= z + distance
endmethod
//
// 3.) Action functions inside the widget enumeration thread.
//
// Runs for every enumerated destructable.
// • Directly filters out already hit destructables.
// • Distance formula based on the Pythagorean theorem.
//
private static method filterDests takes nothing returns boolean
local destructable d = GetFilterDestructable()
local boolean b = false
if not HaveSavedHandle(HASH, temp, GetHandleId(d)) then
if circle then
set b = temp.isWidgetInRange(d, GetDestructableHeight(d), temp.collision)
else
set b = temp.isWidgetInRectangle(d, GetDestructableHeight(d), temp.collision)
endif
endif
set d = null
return b
endmethod
//
// Runs for every enumerated item.
// • Directly filters out already hit items.
// • Distance formula based on the Pythagorean theorem.
// • Items have a fix collision size of 16.
//
private static method filterItems takes nothing returns boolean
local item i = GetFilterItem()
local boolean b = false
if not HaveSavedHandle(HASH, temp, GetHandleId(i)) then
if circle then // Item in missile collision size or item pathing in missile range.
set b = temp.isWidgetInRange(i, GetItemHeight(i), RMaxBJ(temp.collision, 16.))
else
set b = temp.isWidgetInRectangle(i, GetItemHeight(i), RMaxBJ(temp.collision, 16.))
endif
endif
set i = null
return b
endmethod
//
// 4.) Filter function for rectangle unit collision.
//
// Runs for every enumerated units.
// • Filters out units which are not in collision range in plane x / y.
// • Inlined and therefore a bit faster than item and destructable collision.
//
private static method filterUnits takes nothing returns boolean
local thistype this = thistype.temp
local unit u = GetFilterUnit()
local real dx
local real dy
local real s
local boolean is = false
if not HaveSavedHandle(HASH, this, GetHandleId(u)) then
set dx = x - prevX
set dy = y - prevY
set s = (dx*(GetUnitX(u) - prevX) + dy*(GetUnitY(u)- prevY))/(dx*dx + dy*dy)
if s < 0. then
set s = 0.
elseif s > 1. then
set s = 1.
endif
set is = IsUnitInRangeXY(u, prevX + s*dx, prevY + s*dy, collision)
endif
set u = null
return is
endmethod
//
// 5.) Proper rect preparation.
//
// For rectangle.
private method prepareRectRectangle takes nothing returns nothing
local real x1 = prevX
local real y1 = prevY
local real x2 = x
local real y2 = y
local real d = collision + Missile_MAXIMUM_COLLISION_SIZE
// What is min, what is max ...
if x1 < x2 then
if y1 < y2 then
call SetRect(RECT, x1 - d, y1 - d, x2 + d, y2 + d)
else
call SetRect(RECT, x1 - d, y2 - d, x2 + d, y1 + d)
endif
elseif y1 < y2 then
call SetRect(RECT, x2 - d, y1 - d, x1 + d, y2 + d)
else
call SetRect(RECT, x2 - d, y2 - d, x1 + d, y1 + d)
endif
endmethod
//
// For circular.
private method prepareRectCircle takes nothing returns nothing
local real d = collision + Missile_MAXIMUM_COLLISION_SIZE
call SetRect(RECT, x - d, y - d, x + d, y + d)
endmethod
//
// 5.) API for the MissileStruct iteration.
//
method groupEnumUnitsRectangle takes nothing returns nothing
call prepareRectRectangle()
set thistype.temp = this
call GroupEnumUnitsInRect(GROUP, RECT, unitFilter)
endmethod
//
// Prepares destructable enumeration, then runs enumDests.
method checkDestCollision takes code func returns nothing
set circle = collisionType == Missile_COLLISION_TYPE_CIRCLE
if circle then
call prepareRectCircle()
else
call prepareRectRectangle()
endif
//
set thistype.temp = this
call EnumDestructablesInRect(RECT, destFilter, func)
endmethod
//
// Prepares item enumeration, then runs enumItems.
method checkItemCollision takes code func returns nothing
set circle = collisionType == Missile_COLLISION_TYPE_CIRCLE
if circle then
call prepareRectCircle()
else
call prepareRectRectangle()
endif
//
set thistype.temp = this
call EnumItemsInRect(RECT, itemFilter, func)
endmethod
static if Missile_WRITE_DELAYED_MISSILE_RECYCLING then
method nullBefore takes nothing returns nothing
set dummy = null
endmethod
endif
// Does not check for 'Aloc' and 'Amrf' as they could be customized.
private static method onInit takes nothing returns nothing
static if LIBRARY_ErrorMessage then
debug local boolean prev = ToogleUnitIndexer(false)
debug local unit dummy = CreateUnit(Missile_NEUTRAL_PASSIVE, Missile_DUMMY_UNIT_ID, 0., 0., 0.)
debug call ToogleUnitIndexer(prev)
//
debug call ThrowError((GetUnitTypeId(dummy) != Missile_DUMMY_UNIT_ID), "Missile", "DEBUG_MISSILE", "type id", 0, "Global setup for public integer DUMMY_UNIT_ID is incorrect! This map currently can't use Missile!")
debug call ThrowError((Missile_MAXIMUM_COLLISION_SIZE < 0), "Missile", "DEBUG_MISSILE", "collision", 0, "Global setup for public real MAXIMUM_COLLISION_SIZE is incorrect, below zero! This map currently can't use Missile!")
debug call RemoveUnit(dummy)
debug set dummy = null
endif
//
set unitFilter = Filter(function thistype.filterUnits)
set destFilter = Filter(function thistype.filterDests)
set itemFilter = Filter(function thistype.filterItems)
call TriggerAddCondition(MOVE, Condition(function thistype.move))
endmethod
endstruct
// You made it to the end of Missile, but we are not finished.
// Do you remember about the data structure, the delayed recycler
// and of course our interface module "MissileStruct"
//
// This comes now!
// Debug code taken from List ( full credits to Nestharus )
private module MissileStructure
private static thistype collectionCount = 0
private static thistype nodeCount = 0
static if LIBRARY_ErrorMessage then
debug private boolean isNode
debug private boolean isCollection
endif
private thistype _list
method operator list takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "list", "thistype", this, "Attempted To Read Null Node.")
debug call ThrowError(not isNode, "MissileStructure", "list", "thistype", this, "Attempted To Read Invalid Node.")
endif
return _list
endmethod
private thistype _next
method operator next takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "next", "thistype", this, "Attempted To Go Out Of Bounds.")
debug call ThrowError(not isNode, "MissileStructure", "next", "thistype", this, "Attempted To Read Invalid Node.")
endif
return _next
endmethod
private thistype _prev
method operator prev takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "prev", "thistype", this, "Attempted To Go Out Of Bounds.")
debug call ThrowError(not isNode, "MissileStructure", "prev", "thistype", this, "Attempted To Read Invalid Node.")
endif
return _prev
endmethod
private thistype _first
method operator first takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "first", "thistype", this, "Attempted To Read Null List.")
debug call ThrowError(not isCollection, "MissileStructure", "first", "thistype", this, "Attempted To Read Invalid List.")
endif
return _first
endmethod
private thistype _last
method operator last takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "last", "thistype", this, "Attempted To Read Null List.")
debug call ThrowError(not isCollection, "MissileStructure", "last", "thistype", this, "Attempted To Read Invalid List.")
endif
return _last
endmethod
static method allocateCollection takes nothing returns thistype
local thistype this = thistype(0)._first
if (0 == this) then
static if LIBRARY_ErrorMessage then
debug call ThrowError(collectionCount == 8191, "MissileStructure", "allocateCollection", "thistype", 0, "Overflow.")
endif
set this = collectionCount + 1
set collectionCount = this
else
set thistype(0)._first = _first
endif
static if LIBRARY_ErrorMessage then
debug set isCollection = true
endif
set _first = 0
return this
endmethod
static method allocateNode takes nothing returns thistype
local thistype this = thistype(0)._next
if (0 == this) then
static if LIBRARY_ErrorMessage then
debug call ThrowError(nodeCount == 8191, "MissileStructure", "allocateNode", "thistype", 0, "Overflow.")
endif
set this = nodeCount + 1
set nodeCount = this
else
set thistype(0)._next = _next
endif
set allocated = true
return this
endmethod
method insertFront takes thistype node returns thistype
// Extra static unique list for missile motion.
set nextNode[node] = 0
set prevNode[node] = prevNode[0]
set nextNode[prevNode[0]] = node
set prevNode[0] = node
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "List", "push", "thistype", this, "Attempted To Push On To Null List.")
debug call ThrowError(not isCollection, "List", "push", "thistype", this, "Attempted To Push On To Invalid List.")
debug set node.isNode = true
endif
set node._list = this
if (_first == 0) then
set _first = node
set _last = node
set node._next = 0
else
set _first._prev = node
set node._next = _first
set _first = node
endif
set node._prev = 0
return node
endmethod
method remove takes nothing returns nothing
local thistype node = this
set this = node._list
static if LIBRARY_ErrorMessage then
debug call ThrowError(node == 0, "MissileStructure", "remove", "thistype", this, "Attempted To Remove Null Node.")
debug call ThrowError(not node.isNode, "MissileStructure", "remove", "thistype", this, "Attempted To Remove Invalid Node (" + I2S(node) + ").")
debug set node.isNode = false
endif
set node._list = 0
if (0 == node._prev) then
set _first = node._next
else
set node._prev._next = node._next
endif
if (0 == node._next) then
set _last = node._prev
else
set node._next._prev = node._prev
endif
set node._next = thistype(0)._next
set thistype(0)._next = node
set node.allocated = false
// Static unique list for missile motion.
set nextNode[prevNode[node]] = nextNode[node]
set prevNode[nextNode[node]] = prevNode[node]
endmethod
endmodule
// Boolean expressions per struct:
// ===============================
//
// Condition function for the core trigger evaluation. ( Runs for all struct using module MissileStruct )
private function MissileCreateExpression takes integer structId, code func returns nothing
set expression[structId] = Condition(func)
endfunction
// Creates a collection for a struct. ( Runs for all struct using module MissileStruct )
private function MissileCreateCollection takes integer structId returns nothing
set missileList[structId] = Missile.allocateCollection()
endfunction
// Core:
// =====
//
// Fires every Missile_TIMER_TIMEOUT.
private function Fire takes nothing returns nothing
local integer i = remove[0]
set remove[0] = 0
loop
exitwhen 0 == i
if recycling[i] then
call TriggerRemoveCondition(CORE, condition[i])
set condition[i] = null
set active = active - 1
endif
set destroying[i] = false
set recycling[i] = false
set i = remove[i]
endloop
if 0 == active then
call PauseTimer(TMR)
else
// Move all launched missiles.
set Missile.temp = nextNode[0]
set i = 0
loop
exitwhen TriggerEvaluate(MOVE)
exitwhen i == 60// Moved over 8910 missiles, something buggy happened.
set i = i + 1 // This case is impossible, hence never happens. But if I'm wrong, which also never happens
endloop // then the map 100% crashes. i == 66 will prevent the game crash and Missile will start to
// to debug itself over the next iterations.
// Access all structs using module MissileStruct.
static if DEBUG_MODE and LIBRARY_ErrorMesssage then
if not TriggerEvaluate(CORE) then
call ThrowWarning(true, "Missile", "Fire", "op limit", 0, /*
*/"You just ran into a op limit!
The op limit protection of Missile is insufficient for your map.
The code of the missile module can't run in one thread and must be splitted.
If unsure make a post in the official 'The Hive Workshop' forum thread of Missile!")
endif
else
call TriggerEvaluate(CORE)
endif
endif
endfunction
// Conditionally starts the timer.
private function StartPeriodic takes integer structId returns nothing
if 0 == instances[structId] or destroying[structId] then
if destroying[structId] then
set recycling[structId] = false
else
if 0 == active then
call TimerStart(TMR, Missile_TIMER_TIMEOUT, true, function Fire)
endif
set active = active + 1
set condition[structId] = TriggerAddCondition(CORE, expression[structId])
endif
endif
set instances[structId] = instances[structId] + 1
endfunction
// Conditionally stops the timer in the next callback.
private function StopPeriodic takes integer structId returns nothing
set instances[structId] = instances[structId] - 1
if 0 == instances[structId] and condition[structId] != null then
if not destroying[structId] and not recycling[structId] then
set destroying[structId] = true
set recycling[structId] = true
set remove[structId] = remove[0]
set remove[0] = structId
endif
endif
endfunction
// Modules:
// ========
//
// You want to place module MissileLaunch at the very top of your struct.
module MissileLaunch
static method launch takes Missile missile returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(missile.launched, "thistype", "launch", "missile.launched", missile, "This missile was already launched before!")
endif
debug set missile.launched = true
//
call missileList[thistype.typeid].insertFront(missile)
call StartPeriodic(thistype.typeid)
endmethod
endmodule
module MissileTerminate
// Called from missileIterate. "P" to avoid code collision.
static method missileTerminateP takes Missile node returns nothing
if node.allocated then
static if thistype.onRemove.exists then
static if Missile_WRITE_DELAYED_MISSILE_RECYCLING and RecycleBin.recycle.exists then
if thistype.onRemove(node) and GetUnitTypeId(node.dummy) == Missile_DUMMY_UNIT_ID then
call RecycleBin.recycle(node.dummy)
call node.nullBefore()
endif
else
call thistype.onRemove(node)
endif
endif
call node.terminate()
call StopPeriodic(thistype.typeid)
endif
endmethod
endmodule
// Allows you to inject missile in certain stages of the motion process.
module MissileAction
static if DEBUG_MODE then
// Runs check during compile time.
static if thistype.onMissile.exists then
Error Message from library Missile in struct thistype !
thistype.onMissile is a reserved name for Missile, once you implemented MissileStruct.
thistype.onMissile is currently not supported by library Missile.
Please delete or re-name that method.
endif
endif
static if thistype.onItem.exists then
private static method missileActionItem takes nothing returns nothing
local item i = GetEnumItem()
local Missile this = Missile.temp
if this.allocated then
call SaveItemHandle(HASH, this, GetHandleId(i), i)
if thistype.onItem(this, i) then
call missileTerminateP(this)
endif
endif
set i = null
endmethod
endif
static if thistype.onDestructable.exists then
private static method missileActionDest takes nothing returns nothing
local destructable d = GetEnumDestructable()
local Missile this = Missile.temp
if this.allocated then
call SaveDestructableHandle(HASH, this, GetHandleId(d), d)
if thistype.onDestructable(this, d) then
call missileTerminateP(this)
endif
endif
set d = null
endmethod
endif
// Runs every Missile_TIMER_TIMEOUT for this struct.
static method missileIterateP takes nothing returns boolean
local Missile this = missileList[thistype.typeid].first
local Missile node
local real collideZ
local boolean b
local unit u
loop
exitwhen 0 == this
set node = this.next// The linked list should not lose the next node.
if this.wantDestroy then
call thistype.missileTerminateP(this)
else
if this.stackSize > 0 then
call this.updateStack()
endif
// Runs unit collision.
static if thistype.onCollide.exists then
if this.allocated and 0. < this.collision then
set b = this.collisionType == Missile_COLLISION_TYPE_RECTANGLE
if b then
call this.groupEnumUnitsRectangle()
else
call GroupEnumUnitsInRange(GROUP, this.x, this.y, this.collision + Missile_MAXIMUM_COLLISION_SIZE, null)
endif
loop
set u = FirstOfGroup(GROUP)
exitwhen u == null
call GroupRemoveUnit(GROUP, u)
if not HaveSavedHandle(HASH, this, GetHandleId(u)) then
if IsUnitInRange(u, this.dummy, this.collision) or b then
// Eventually run z collision checks.
static if Missile_USE_COLLISION_Z_FILTER then
set collideZ = Missile_GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u) - this.terrainZ
if (collideZ + GetUnitBodySize(u) >= this.z - this.collisionZ) and (collideZ <= this.z + this.collisionZ) then
// Mark as hit.
call SaveUnitHandle(HASH, this, GetHandleId(u), u)
if thistype.onCollide(this, u) then
call thistype.missileTerminateP(this)
exitwhen true
endif
endif
else
// Runs unit collision without z collision checks.
call SaveUnitHandle(HASH, this, GetHandleId(u), u)
if thistype.onCollide(this, u) then
call thistype.missileTerminateP(this)
exitwhen true
endif
endif
endif
endif
endloop
endif
endif
// Runs destructable collision.
static if thistype.onDestructable.exists then
// Check if the missile is not terminated.
if this.allocated and 0. < this.collision then
call this.checkDestCollision(function thistype.missileActionDest)
endif
endif
// Runs item collision.
static if thistype.onItem.exists then
// Check if the missile is not terminated.
if this.allocated and 0. < this.collision then
call this.checkItemCollision(function thistype.missileActionItem)
endif
endif
// Runs when the destination is reached.
if this.recycle and this.allocated then
static if thistype.onFinish.exists then
if thistype.onFinish(this) then
call thistype.missileTerminateP(this)
endif
else
call thistype.missileTerminateP(this)
endif
endif
// Runs on terrian collision.
static if thistype.onTerrain.exists then
if this.allocated and 0. > this.z and thistype.onTerrain(this) then
call missileTerminateP(this)
endif
endif
// Runs every Missile_TIMER_TIMEOUT.
static if thistype.onPeriod.exists then
if this.allocated and thistype.onPeriod(this) then
call missileTerminateP(this)
endif
endif
endif
set this = node
endloop
set u = null
static if DEBUG_MODE and LIBRARY_ErrorMessage then
return true
else
return false
endif
endmethod
endmodule
module MissileStruct
implement MissileLaunch
implement MissileTerminate
implement MissileAction
private static method onInit takes nothing returns nothing
call MissileCreateCollection(thistype.typeid)
call MissileCreateExpression(thistype.typeid, function thistype.missileIterateP)
endmethod
endmodule
// Missile position:
// =================
//
// Simple trigonometry.
//! textmacro WRITE_MISSILE_POSITION_CODE
struct MissilePosition extends array
private static integer array recycler
private static integer alloc = 0
// Readonly members you can access.
readonly real x
readonly real y
readonly real z
readonly real angle
readonly real distance
readonly real square
readonly real slope
readonly real alpha
// Creates an origin - impact link.
private thistype ref
private static method math takes thistype a, thistype b returns nothing
local real dx
local real dy
loop
set dx = b.x - a.x
set dy = b.y - a.y
set dx = dx*dx + dy*dy
set dy = SquareRoot(dx)
exitwhen dx != 0. and dy != 0.
set b.x = b.x + .01
set b.z = b.z - Missile_GetLocZ(b.x -.01, b.y) + Missile_GetLocZ(b.x, b.y)
endloop
set a.square = dx
set a.distance = dy
set a.angle = Atan2(b.y - a.y, b.x - a.x)
set a.slope = (b.z - a.z)/dy
set a.alpha = Atan(a.slope)*bj_RADTODEG
// Set b.
if b.ref == a then
set b.angle = a.angle + bj_PI
set b.distance = dy
set b.slope = -a.slope
set b.alpha = -a.alpha
set b.square = dx
endif
endmethod
static method link takes thistype a, thistype b returns nothing
set a.ref = b
set b.ref = a
call math(a, b)
endmethod
method move takes real toX, real toY, real toZ returns nothing
set x = toX
set y = toY
set z = toZ + Missile_GetLocZ(toX, toY)
if ref != this then
call math(this, ref)
endif
endmethod
method destroy takes nothing returns nothing
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
static method create takes real x, real y, real z returns MissilePosition
local thistype this = recycler[0]
if 0 == this then
set alloc = alloc + 1
set this = alloc
else
set recycler[0] = recycler[this]
endif
set ref = this
call move(x, y, z)
return this
endmethod
endstruct
//! endtextmacro
// Delayed dummy recycling:
// ========================
//
// Ensures proper fx death animations.
//! textmacro WRITE_MISSILE_RECYCLE_BIN takes DO_THIS, AFTER_TIME
static if $DO_THIS$ then
private struct RecycleBin extends array
private static constant timer t = CreateTimer()
private static integer max = 0
private static unit array dummy
private static real array time
private static method onPeriodic takes nothing returns nothing
local integer dex = 0
loop
exitwhen dex == thistype.max
set thistype.time[dex] = thistype.time[dex] - 1
if 0 >= thistype.time[dex] then
static if LIBRARY_MissileRecycler then
call RecycleMissile(thistype.dummy[dex])
elseif Dummy.create.exists and LIBRARY_Dummy then
call Dummy[thistype.dummy[dex]].destroy()
elseif LIBRARY_xedummy and xedummy.release.exists then
call xedummy.release(thistype.dummy[dex])
else
call RemoveUnit(thistype.dummy[dex])
endif
set thistype.dummy[dex] = null
set thistype.max = thistype.max - 1
set thistype.dummy[dex] = thistype.dummy[thistype.max]
set thistype.time[dex] = thistype.time[thistype.max]
set thistype.dummy[thistype.max] = null
set dex = dex - 1
if 0 == thistype.max then
call PauseTimer(thistype.t)
endif
endif
set dex = dex + 1
endloop
endmethod
static method recycle takes unit toRecycle returns nothing
if 0 == thistype.max then
call TimerStart(thistype.t, 1., true, function thistype.onPeriodic)
endif
set thistype.dummy[max] = toRecycle
set thistype.time[max] = $AFTER_TIME$ + TimerGetRemaining(thistype.t)
set thistype.max = thistype.max + 1
endmethod
endstruct
endif
//! endtextmacro
// The end!
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LockBone uses optional TimerUtils, optional Table, optional MissileRecycler
/* v1.6 */
globals
// If only you don't use MissileRecycler
private constant integer DUMMY_ID = 'e00E'
endglobals
/*
Description
¯¯¯¯¯¯¯¯¯¯¯
Allows you to lock a unit's body part to face certain angle or point.
External Dependencies
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
(Requires) - Nothing
(Optional) - These libs are completely optional. But it's a win win to have all of these to go with the system.
1. This lib allows the system to save data into timers, which is useful for struct based spells/systems
| TimerUtils @ wc3c.net/showthread.php?t=101322
2. This lib can ease the handling of hashtables as well as make it perfectly efficient uses of hashtables.
| Table @ hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
3. This system uses a lot of dummy units. Unit creations are heavy process, and other problems as well.
Here is where dummy recycler comes important.
| MissileRecycler @ hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
API
¯¯¯
struct LockBone
1. Lock body part at given coordinate
| static method lockAtPoint takes unit whichUnit, string whichBone, real targetX, real targetY, real zOffset returns thistype
- targetX : x-axis of the target point
- targetY : y-axis of the target point
- zOffset : height level at which the bone will be locked
2. Lock body part at given angle (in radian)
| static method lockAtAngle takes unit whichUnit, string whichBone, real radian, real hOffset, real vOffset returns thistype
- radian : at what angle (in radian) unit's bone will be locked
- hOffset : horizontal offset
- vOffset : vertical offset
0 ............... -> lock point
unit <- | ______________: -> vOffset
\_____________/
hOffset
3. Reset any body locking
| static method resetLock takes unit whichUnit returns nothing
4. Bones part constants
BONE_PART_CHEST => rotate chest bone
BONE_PART_HEAD => rotate head bone
Resource Link
¯¯¯¯¯¯¯¯¯¯¯¯¯
hiveworkshop.com/forums/submissions-414/snippet-lockbone-259005/
*/
globals
constant string BONE_PART_CHEST = "bone_chest"
constant string BONE_PART_HEAD = "bone_head"
private constant real INTERVAL = 0.03125
private constant player PASSIVE = Player(PLAYER_NEUTRAL_PASSIVE)
endglobals
struct LockBone
private timer t
private unit u
private unit d
private real cos
private real sin
private static real MapMaxX
private static real MapMaxY
private static real MapMinX
private static real MapMinY
static if LIBRARY_Table then
private static Table Ht
else
private static hashtable Ht
endif
private static method onPeriodic takes nothing returns nothing
local thistype this
local real x
local real y
static if LIBRARY_TimerUtils then
set this = GetTimerData(GetExpiredTimer())
else
static if LIBRARY_Table then
set this = Ht.integer[GetHandleId(GetExpiredTimer())]
else
set this = LoadInteger(Ht, GetHandleId(GetExpiredTimer()), 0)
endif
endif
set x = GetUnitX(.u)+.cos
set y = GetUnitY(.u)+.sin
if x > MapMinX and x < MapMaxX and y > MapMinY and y < MapMaxY then
call SetUnitX(.d, x)
call SetUnitY(.d, y)
else
call SetUnitPosition(.d, x, y)
endif
endmethod
static method resetLock takes unit whichUnit returns nothing
local thistype this
local integer hand = GetHandleId(whichUnit)
static if LIBRARY_Table then
set this = Ht.integer[hand]
else
set this = LoadInteger(Ht, hand, 0)
endif
if this != 0 then
call ResetUnitLookAt(.u)
static if LIBRARY_MissileRecycler then
call RecycleMissile(.d)
call SetUnitX(.d, MapMaxX)
call SetUnitY(.d, MapMaxY)
else
call RemoveUnit(.d)
endif
static if LIBRARY_TimerUtils then
call ReleaseTimer(.t)
else
call DestroyTimer(.t)
static if LIBRARY_Table then
call Ht.integer.remove[GetHandleId(.t)]
else
call RemoveSavedInteger(Ht, GetHandleId(.t), 0)
endif
endif
static if LIBRARY_Table then
set Ht.integer[hand] = 0
else
call SaveInteger(Ht, hand, 0, 0)
endif
call destroy()
set .u = null
set .d = null
set .t = null
endif
endmethod
static method lockAtPoint takes unit whichUnit, string whichBone, real targetX, real targetY, real zOffset returns thistype
local thistype this
static if LIBRARY_Table then
set this = Ht.integer[GetHandleId(whichUnit)]
else
set this = LoadInteger(Ht, GetHandleId(whichUnit), 0)
endif
if this == 0 then
set this = allocate()
static if LIBRARY_Table then
set Ht.integer[GetHandleId(whichUnit)] = this
else
call SaveInteger(Ht, GetHandleId(whichUnit), 0, this)
endif
static if LIBRARY_TimerUtils then
set .t = NewTimerEx(this)
else
set .t = CreateTimer()
static if LIBRARY_Table then
set Ht.integer[GetHandleId(.t)] = this
else
call SaveInteger(Ht, GetHandleId(.t), 0, this)
endif
endif
static if LIBRARY_MissileRecycler then
set .d = GetRecycledMissile(0, 0, 0, 0)
else
set .d = CreateUnit(PASSIVE, DUMMY_ID, 0, 0, 0)
static if not LIBRARY_AutoFly then
if UnitAddAbility(.d, 'Amrf') and UnitRemoveAbility(.d, 'Amrf') then
endif
endif
endif
set .u = whichUnit
call UnitRemoveAbility(.d, 'Amov')
call SetUnitLookAt(.u, whichBone, .d, 0, 0, 0)
endif
if TimerGetTimeout(.t) > 0 then
call TimerStart(.t, 0, false, null)
call PauseTimer(.t)
endif
call SetUnitFlyHeight(.d, zOffset, 0)
call SetUnitX(.d, targetX)
call SetUnitY(.d, targetY)
return this
endmethod
static method lockAtAngle takes unit whichUnit, string whichBone, real radian, real hOffset, real vOffset returns thistype
local thistype this
static if LIBRARY_Table then
set this = Ht.integer[GetHandleId(whichUnit)]
else
set this = LoadInteger(Ht, GetHandleId(whichUnit), 0)
endif
if this == 0 then
set this = allocate()
static if LIBRARY_Table then
set Ht.integer[GetHandleId(whichUnit)] = this
else
call SaveInteger(Ht, GetHandleId(whichUnit), 0, this)
endif
static if LIBRARY_TimerUtils then
set .t = NewTimerEx(this)
else
set .t = CreateTimer()
static if LIBRARY_Table then
set Ht.integer[GetHandleId(.t)] = this
else
call SaveInteger(Ht, GetHandleId(.t), 0, this)
endif
endif
static if LIBRARY_MissileRecycler then
set .d = GetRecycledMissile(0, 0, 0, 0)
else
set .d = CreateUnit(PASSIVE, DUMMY_ID, 0, 0, 0)
static if not LIBRARY_AutoFly then
if UnitAddAbility(.d, 'Amrf') and UnitRemoveAbility(.d, 'Amrf') then
endif
endif
endif
set .u = whichUnit
call UnitRemoveAbility(.d, 'Amov')
call SetUnitLookAt(.u, whichBone, .d, 0, 0, 0)
endif
if TimerGetTimeout(.t) == 0 then
call TimerStart(.t, INTERVAL, true, function thistype.onPeriodic)
endif
set .cos = hOffset*Cos(radian)
set .sin = hOffset*Sin(radian)
call SetUnitFlyHeight(.d, vOffset, 0)
call SetUnitX(.d, GetUnitX(.u)+.cos)
call SetUnitY(.d, GetUnitY(.u)+.sin)
return this
endmethod
private static method onDeath takes nothing returns boolean
local unit u = GetTriggerUnit()
local thistype this
static if LIBRARY_Table then
set this = Ht.integer[GetHandleId(u)]
else
set this = LoadInteger(Ht, GetHandleId(u), 0)
endif
if this != 0 then
call resetLock(u)
endif
set u = null
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
static if LIBRARY_Table then
set Ht = Table.create()
else
set Ht = InitHashtable()
endif
set MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
set MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Condition(function thistype.onDeath))
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MissileRecycler initializer PreInit requires optional UnitIndexer, optional UnitDex, optional UnitIndexerGUI /*
MissileRecycler v 1.4.5.2
=========================================================================
Credits:
-------------------------------------------------------------------------
Written by Bribe
Vexorian, Anitarf and iNfraNe for the dummy.mdx model file
Nestharus for the Queue data structure and for finding that paused units
consume very few CPU resources.
=========================================================================
Introduction:
-------------------------------------------------------------------------
Recycling dummy units is important because the CreateUnit call is one of,
if not the, most processor-intensive native in the entire game. Creating
just a couple dozen dummy units in a single thread causes a visible frame
glitch for that instant. The overhead is even higher if you are using a
Unit Indexing library in the map which causes some extra evaluations per
new unit.
There are also reports of removed units leaving a little trail of RAM
surplus in their wake. I have not been able to reproduce this so I don't
know how serious it is.
I was motivated to create this system because removed units might be un-
safe in very large numbers and CreateUnit is a very heavy process.
The thing that makes this system different than others is the fact that
it considers the facing angle of the dummies being recycled, which I have
never seen another system even attempt before this. Since then,
MissileRecycler has inspired Anitarf to update XE with the same angle-retaining
capability and Nestharus has created Dummy. Considering the facing angle is
important because it takes 0.73 seconds for the unit to turn around,
which - when overlooked - looks especially weird if you are creating a unit-trail
or if you are shooting arrow-shaped objects as projectiles. For fireball effects or
effects that generally don't depend on facing angle, this system would be
a bit wasteful.
With default settings and the worst-case-scenario, it will take 0.09 seconds for
the projectile to turn to the angle you need. This is 1/8 of the normal worst case
scenario if you weren't recycling dummies considering facing. On average, it takes
roughly 0.045 seconds to turn to the angle you need (which is not noticable).
However, I have made this completely configurable and you are
able to change the values to whatever needs you have.
=========================================================================
Calibration Guide:
-------------------------------------------------------------------------
The thing that surprised me the most about this system was, no matter how
complex it turned out, it became very configurable. So I should let you
know what the constants do so you know if/how much you want to modify.
constant real DEATH_TIME = 2.0 //seconds
- Should not be less than the maximum time a death animation needs to play.
Should not be lower than .73 to ensure enough time to turn.
Should not be too high otherwise the dummies will take too long to recycle.
constant integer ANG_N = 8
- How many different angles are recognized by the system. Don't do
360 different angles because then you're going to have thousands of dummy
units stored and that's ridiculous, the game lags enough at 1000 units.
Increasing ANG_N increases realism but decreases the chance that a dummy
unit will be available to be recycled. I don't recommend making this any
lower, and the max I'd recommend would be 16.
constant integer ANG_STORAGE_MAX = 12
- How many dummy units are stored per angle. This limit is important
because you might have a spike at one point in the game where many units
are created, which could result in too high of a dummy population.
In general, I advise that the product of ANG_N x ANG_STORAGE_MAX does
not exceed 100 or 200. More than that is excessive, but you can
hypothetically have it up to 8190 if Warcraft 3's memory management
were better.
Preloads ANG_N x ANG_STORAGE_MAX dummy units. Preloading dummies is
useful as it dumps a lot of CreateUnit calls in initialization where you
won't see a frame glitch. In the 1.4 update, preloading is done 0 seconds
into the game to ensure any Indexers have already initialized.
private function ToggleIndexer takes boolean flag returns nothing
- Put what you need in here to disable/enable any indexer in your
map. if flag is true, enable indexer. If false, disable.
=========================================================================
API Guide:
-------------------------------------------------------------------------
You obviously need some functions so you can get a recycled dummy unit or
recycle it. Therefore I provide these:
function GetRecycledMissile
takes real x, real y, real z, real facing
returns unit
Returns a new dummy unit that acts as a projectile missile. The args
are simply the last three arguments you'd use for a CreateUnit call,
with the addition of a z parameter to represent the flying height -
it isn't the absolute z but relative to the ground because it uses
SetUnitFlyHeight on that value directly.
function RecycleMissile
takes unit u
returns nothing
When you are done with that dummy unit, recycle it via this function.
This function is pretty intelligent and resets that unit's animation
and its facing angle so you don't have to.
*/
//=======================================================================
// Save the map, then delete the exclaimation mark in the following line.
// Make sure that you don't have an object in your map with the rawcode
// 'dumi' and also configure the model path (war3mapImported\dummy.mdl)
// to the dummy.mdx model created by Vexorian.
///! external ObjectMerger w3u ewsp dumi unam "Missile Dummy" ufoo 0 utyp "" ubui "" uhom 1 ucol 0.01 umvt "None" umvr 1.00 utar "" uspa "" umdl "war3mapImported\dummy.mdl" umxr 0.00 umxp 0.00 ushr 0 uerd 0.00 udtm 0.00 ucbs 0.00 uble 0.00 uabi "Aloc,Amrf"
//Thanks to Vexorian that Optimizer 5.0 no longer kills natives
native UnitAlive takes unit id returns boolean
globals
//-------------------------------------------------------------------
// You must configure the dummy unit with the one created from the
// ObjectMerger statement above.
//
private constant integer DUMMY_ID = 'e00E' //The rawcode of the dummy unit.
private player OWNER = Player(14) //The owner of the dummy unit.
private constant integer ANG_N = 8 //# of indexed angles. Higher value increases realism but decreases recycle frequency.
private constant integer ANG_STORAGE_MAX = 12 //Max dummies per indexed angle. I recommend lowering this if you increase ANG_N.
private constant real DEATH_TIME = 3. //Allow the special effect on
//the unit to complete its "death" animation in this timeframe. Must
//be higher than 0.74 seconds to allow the unit time to turn. This
//number should not be lower than the maximum death-animation time of
//your missile-units' effect attachments, just to be safe.
endglobals
private function ToggleIndexer takes boolean flag returns nothing
static if LIBRARY_UnitIndexer then
set UnitIndexer.enabled = flag
elseif LIBRARY_UnitIndexerGUI then
set udg_UnitIndexerEnabled = flag
elseif LIBRARY_UnitDex then
set UnitDex.Enabled = flag
endif
endfunction
globals
private constant integer ANG_VAL = 360 / ANG_N //Generate angle value from ANG_N.
private constant integer ANG_MID = ANG_VAL / 2 //The middle value of angle value.
//Misc vars
private unit array stack //Recycled dummy units.
private real array timeStamp //Prevents early recycling of units.
private integer array queueNext
private integer array queueLast
private integer recycle = 0
private timer gameTime = CreateTimer() //Used for visual continuity.
private integer array queueStack
private integer queueStackN = 0 //Used to avoid searching the queues.
endglobals
static if DEBUG_MODE then
private function Print takes string s returns nothing
//Un-comment this next line if you want to know how the system works:
//call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 999, "[MissileRecycler] " + s)
endfunction
endif
//=======================================================================
// Get a recycled dummy missile unit. If there are no recycled dummies
// that are already facing the angle you need, it creates a new dummy for
// you.
//
function GetRecycledMissile takes real x, real y, real z, real facing returns unit
local integer i = ModuloInteger(R2I(facing), 360) / ANG_VAL
local integer this = queueNext[i]
local unit u
if this != 0 and TimerGetElapsed(gameTime) >= timeStamp[this] then
//Dequeue this
set queueNext[i] = queueNext[this]
if queueNext[i] == 0 then
set queueLast[i] = i
endif
//Recycle this index
set queueLast[this] = recycle
set recycle = this
//Add queue index to available stack
set queueStack[queueStackN] = i
set queueStackN = queueStackN + 1
//Old unit will return as new
set u = stack[this]
call SetUnitFacing(u, facing)
call SetUnitUserData(u, 0)
//Reset the dummy's properties.
call SetUnitVertexColor(u, 255, 255, 255, 255)
call SetUnitAnimationByIndex(u, 90)
call SetUnitScale(u, 1, 0, 0)
//call PauseUnit(u, false) -- you can disable "resets" that you don't need to worry about.
debug call Print("Recycling")
else
debug call Print("Creating new")
call ToggleIndexer(false)
set u = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
call ToggleIndexer(true)
call PauseUnit(u, true)
endif
call SetUnitX(u, x)
call SetUnitY(u, y)
call SetUnitFlyHeight(u, z, 0)
set bj_lastCreatedUnit = u
set u = null
return bj_lastCreatedUnit
endfunction
//=======================================================================
// You should recycle the dummy missile unit when its job is done.
//
function RecycleMissile takes unit u returns nothing
local integer i
local integer this = recycle
if GetUnitTypeId(u) == DUMMY_ID and UnitAlive(u) and GetUnitUserData(u) != -1 then
if queueStackN == 0 then
debug call Print("Stack is full - removing surplus unit")
call UnitApplyTimedLife(u, 'BTLF', DEATH_TIME)
return
endif
//Recycle this
set recycle = queueLast[this]
//Index the dummy unit to an available facing angle.
//Get the last vacant angle index.
set queueStackN = queueStackN - 1
set i = queueStack[queueStackN]
//Enqueue this
set queueNext[queueLast[i]] = this
set queueLast[i] = this
set queueNext[this] = 0
//Allow a time barrier for the effect to destroy/turn to complete.
set timeStamp[this] = TimerGetElapsed(gameTime) + DEATH_TIME
set stack[this] = u
call SetUnitFacing(u, i * ANG_VAL + ANG_MID)
call SetUnitOwner(u, OWNER, false)
//Prevent double-free of this unit.
call SetUnitUserData(u, -1)
debug else
debug call BJDebugMsg("[MissileRecycler] Error: Attempt to recycle invalid unit.")
endif
endfunction
//=======================================================================
// I didn't need this function after all
//
function RecycleMissileDelayed takes unit u, real r returns nothing
call RecycleMissile(u)
endfunction
//=======================================================================
// Map the dummy units to their facing angles (map below is if ANG_N is
// 4 and ANG_STORAGE_MAX is 3).
//
// angle[0] (0) - [4] [5] [6]
// angle[1] (90) - [7] [8] [9]
// angle[2] (180) - [10][11][12]
// angle[3] (270) - [13][14][15]
//
private function Init takes nothing returns nothing
local integer end
local integer i = ANG_N
local integer n = i
local integer angle
local real x = GetRectMaxX(bj_mapInitialPlayableArea)
local real y = GetRectMaxY(bj_mapInitialPlayableArea)
local unit u
call ToggleIndexer(false)
loop
set i = i - 1
set queueNext[i] = n
set angle = i * ANG_VAL + ANG_MID
set end = n + ANG_STORAGE_MAX
set queueLast[i] = end - 1
loop
set queueNext[n] = n + 1
set u = CreateUnit(OWNER, DUMMY_ID, x, y, angle)
set stack[n] = u
call PauseUnit(u, true)
call SetUnitUserData(u, -1)
set n = n + 1
exitwhen n == end
endloop
set queueNext[n - 1] = 0
exitwhen i == 0
endloop
call ToggleIndexer(true)
call TimerStart(gameTime, 1000000., false, null)
set u = null
endfunction
private function PreInit takes nothing returns nothing
static if LIBRARY_UnitIndexerGUI then
call OnUnitIndexerInitialized(function Init)
else
call Init()
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xecollider initializer init requires xefx, xebasic
//****************************************************************
//*
//* xecollider 0.8
//* --------------
//* A xecollider object is a special effect that has a collision
//* size that can trigger a hit event and also many options to
//* configure its automatic movement.
//*
//* Please use .terminate() instead of .destroy() this ensures
//* that it will be safe to destroy it (else you would have to
//* worry about destroying it during the animation loop/etc.)
//*
//* To use this struct is a little different than the other
//* current parts of xe. Instead of just creating the xecollider
//* (which works, but it would only be a xefx that can have speed)
//* you probably need to make it do something special on the
//* unit hit event... For this reason, you need to make a new
//* struct extending xecollider that declares an onUnitHit method
//* you may also declare a loopControl method, very useful, can
//* help you reduce 'attaching'.
//*
//****************************************************************
//================================================================
globals
private constant real DEFAULT_COLLISION_SIZE = 50.0 // These are defaults, on one hand you can change them
private constant real DEFAULT_MAX_SPEED = 1500.0 // on the other hand, if a spell relies on the defaults
private constant real DEFAULT_EXPIRATION_TIME = 100. // changing them would make the behavior vary...
private constant real PI2 = 6.28318 //It might not be wise to change this
endglobals
//===========================================================================
// So, this exists merely so you can declare your own event handler methods
// if interfaces make your brain blow out, please skip the next four lines.
//
private interface eventHandler
method onUnitHit takes unit hitTarget returns nothing defaults nothing
method loopControl takes nothing returns nothing defaults nothing
endinterface
//===========================================================================
struct xecollider extends eventHandler
// use terminate() instead of .destroy() to "kill" the collider.
// don't worry, terminate will call destroy automatically.
//============================================================================
// delegates:
// We are delegating a xefx object so that people call all the xefx methods
// and member from a xecollider object. This means that from a user
// perspective a xecollider is also an instance of xefx.
//
// Notable ones are: .x , .y , .fxpath and .z ,
// check out xefx's documentation for more info.
//
private delegate xefx fx
//##==========================================================================
// public variables:
//
public real expirationTime = DEFAULT_EXPIRATION_TIME
// Movement speed for the missile.
public real speed = 0.0
// Speed added per second (notice you can use a negative value here)
public real acceleration = 0.0
// If there is acceleration, it is wise to have a cap...
public real maxSpeed = DEFAULT_MAX_SPEED
public real minSpeed = 0.0
public real angleSpeed = 0.0 //The increment in radians per second to the
// direction angle, allows curved movement.
private static integer lastSeen = 0
private group seen
private boolean silent = false
//##==========================================================================
// public methods:
//
//----
// Well, it is a good idea to actually create the missiles.
// notice that if your custom missile struct needs to declare its own create
// method, you can call this as allocate(x,y,dir).
//
// Sorry, no Loc version.
//
public static method create takes real x, real y, real dir returns xecollider
local xecollider xc= xecollider.allocate()
set xc.fx = xefx.create(x,y,dir)
set xc.dir=dir
set xecollider.V[xecollider.N]=xc
set xecollider.N=xecollider.N+1
if(xecollider.N==1) then
call TimerStart(xecollider.T, XE_ANIMATION_PERIOD, true, xecollider.timerLoopFunction )
endif
if(.lastSeen < integer(xc)) then //with this I do group recycling
set .lastSeen = integer(xc)
set xc.seen = CreateGroup()
endif
return xc
endmethod
//----
// The direction is just the angle in radians to which the missile's model faces
// and the automatic movement uses.
//
method operator direction takes nothing returns real
return this.dir
endmethod
method operator direction= takes real v returns nothing
set this.dir=v
set this.fx.xyangle=v
endmethod
//----
// The collisionSize
//
method operator collisionSize takes nothing returns real
return this.csize
endmethod
method operator collisionSize= takes real value returns nothing
set this.csize = value
//good long attribute name, but we use csize in the loop
//don't worry this gets inlined, it would also be helpful if
//I ever decide to add a control for assignment.
endmethod
//---
// targetUnit is a unit to follow (or try to follow), notice that homing
// options require an angleSpeed different to 0.0
//
public method operator targetUnit takes nothing returns unit
return this.homingTargetUnit
endmethod
public method operator targetUnit= takes unit u returns nothing
if(u==null) then
set this.angleMode= ANGLE_NO_MOVEMENT
else
set this.angleMode= ANGLE_HOMING_UNIT
endif
set this.homingTargetUnit=u
endmethod
//----
// targetPoint is a point to reach (or try to reach), notice that homing
// options require an angleSpeed different to 0.0
//
public method setTargetPoint takes real x, real y returns nothing
set this.angleMode= ANGLE_HOMING_POINT
set this.homingTargetX=x
set this.homingTargetY=y
endmethod
public method setTargetPointLoc takes location loc returns nothing
set this.angleMode= ANGLE_HOMING_POINT
set this.homingTargetX=GetLocationX(loc)
set this.homingTargetY=GetLocationY(loc)
endmethod
public method operator targetPointX takes nothing returns real
return this.homingTargetX
endmethod
public method operator targetPointY takes nothing returns real
return this.homingTargetY
endmethod
//----
// Call this in case you used targetUnit or TargetPoint so the missile
// forgets the order to home that target.
//
public method forgetTarget takes nothing returns nothing
set this.angleMode = ANGLE_NO_MOVEMENT
endmethod
public method operator rotating takes nothing returns boolean
return (angleMode ==ANGLE_ROTATING)
endmethod
public method operator rotating= takes boolean val returns nothing
if(val) then
set angleMode = ANGLE_ROTATING
elseif (angleMode == ANGLE_ROTATING) then
set angleMode = ANGLE_NO_MOVEMENT
endif
endmethod
method terminate takes nothing returns nothing
set this.dead=true
set this.fxpath=""
endmethod
/* declare hiddenDestroy so people don't call directly on the delegate xefx */
method hiddenDestroy takes nothing returns nothing
set silent = true
call terminate()
endmethod
//--------
private static timer T
private static integer N=0
private static xecollider array V
private static code timerLoopFunction //I use a code var so create can be above the timerloop function, more readable
private boolean dead=false
private real csize = DEFAULT_COLLISION_SIZE
private real dir
private static constant integer ANGLE_HOMING_UNIT =1
private static constant integer ANGLE_HOMING_POINT=2
private static constant integer ANGLE_NO_MOVEMENT=0
private static constant integer ANGLE_ROTATING=3
private integer angleMode =0
private unit homingTargetUnit = null
private real homingTargetX
private real homingTargetY
private method onDestroy takes nothing returns nothing
call GroupClear(this.seen)
if(this.silent) then
call this.fx.hiddenDestroy()
else
call this.fx.destroy()
endif
endmethod
private static xecollider cinstance
private static real newx
private static real newy
private static group enumGroup
private static boolexpr enumBoolexpr
private static unit array picked
private static integer pickedN
static method timerLoop takes nothing returns nothing
local integer i=0
local integer j=0
local integer c=0
local xecollider this
local real d
local real ns
local real wa
local real df1
local real df2
local unit u
loop
exitwhen (i== xecollider.N )
set this=.V[i] //adopt-a-instance
set this.expirationTime = this.expirationTime - XE_ANIMATION_PERIOD
if(.dead or (this.expirationTime <=0.0) ) then
call this.destroy()
else
set ns=this.angleSpeed*XE_ANIMATION_PERIOD
if (ns!=0.0) then
if(this.angleMode== ANGLE_HOMING_UNIT ) then
set u=this.homingTargetUnit
if ( (GetUnitTypeId(u)==0) or IsUnitType(u, UNIT_TYPE_DEAD) ) then
set this.angleMode= ANGLE_NO_MOVEMENT
set this.homingTargetUnit = null
else
set this.homingTargetX=GetUnitX(u)
set this.homingTargetY=GetUnitY(u)
endif
set u=null
endif
if (this.angleMode == ANGLE_ROTATING) then
//nothing (ns is already ns)
elseif( this.angleMode != ANGLE_NO_MOVEMENT) then
if(ns<=0) then
set ns=-ns
endif
set wa=Atan2(this.homingTargetY - this.y , this.homingTargetX-this.x)
//if(wa<0.0) then
// set wa=wa+PI2
//endif
set df1=wa-this.dir
set df2=(PI2+wa)-this.dir
if (df1<=0) then
if(df2<=0) then
if(df2>=df1) then
set df1=df2
endif
else
if(-df1>=df2) then
set df1=df2
endif
endif
else
if(df2<=0) then
if(-df2<=df1) then
set df1=df2
endif
else
if(df2<=df1) then
set df1=df2
endif
endif
endif
if(df1<=0) then
if(-df1>=ns) then
set ns=-ns
else
set ns=df1
endif
else
if(df1<=ns) then
set ns=df1
endif
endif
else
set ns = 0
endif
set d=this.dir
set d = d + ns
if(d>=PI2) then
set d=d - PI2
elseif(d<0) then
set d=d + PI2
endif
set this.dir = d
set this.xyangle = d
endif
// function calls are expensive, damned we are, long code inside of loop
// correct software dev. tells us this should go to another function,
// but this is Jass, not real life.
set .cinstance = this
set ns = this.speed + this.acceleration*XE_ANIMATION_PERIOD
if ( ns<this.minSpeed) then
set ns=this.minSpeed
elseif (ns>this.maxSpeed) then
set ns=this.maxSpeed
endif
set d=((this.speed+ns)/2) * XE_ANIMATION_PERIOD
set this.speed=ns
set .newx= .x+d*Cos(this.dir)
set .newy= .y+d*Sin(this.dir)
set .x=.newx
set .y=.newy
set xecollider.pickedN = 0
call GroupEnumUnitsInRange( .enumGroup, .newx, .newy, .csize + XE_MAX_COLLISION_SIZE, .enumBoolexpr)
call GroupClear(this.seen)
set j=0
loop
exitwhen (j==xecollider.pickedN)
call GroupAddUnit( this.seen, xecollider.picked[j])
set j=j+1
endloop
set .V[c]=this
set c=c+1
if( this.loopControl.exists and not this.dead ) then
call this.loopControl()
endif
endif
set i=i+1
endloop
//call BJDebugMsg("}")
set xecollider.N=c
if(c==0) then
call PauseTimer(xecollider.T)
endif
endmethod
private static method inRangeEnum takes nothing returns boolean
local xecollider this= .cinstance //adopt-a-instance
local unit u=GetFilterUnit()
if not IsUnitType(u, UNIT_TYPE_DEAD) and not(this.dead) and (GetUnitTypeId(u)!=XE_DUMMY_UNITID) and IsUnitInRangeXY(u, .newx, .newy, .csize) then
// ah, the advantages of a standardized unit id...
set xecollider.picked[xecollider.pickedN] = u
set xecollider.pickedN=xecollider.pickedN + 1
if not IsUnitInGroup (u, this.seen ) then
call this.onUnitHit(u)
endif
endif
set u=null
return false
endmethod
//============================================================================
// you aren't supposed to call doInit yourself, try not to do it.
//
static method doInit takes nothing returns nothing
set xecollider.enumGroup = CreateGroup()
set xecollider.enumBoolexpr = Condition( function xecollider.inRangeEnum)
set xecollider.timerLoopFunction = (function xecollider.timerLoop)
set xecollider.T=CreateTimer()
endmethod
endstruct
private function init takes nothing returns nothing
call xecollider.doInit()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library RapidSound requires optional TimerUtils
globals
// Actually, just leave this value
private constant real MIN_DELAY_FACTOR = 4.0
endglobals
/* v1.6
Description
¯¯¯¯¯¯¯¯¯¯¯
Allows you to play sounds rapidly and flawlessly without limit.
Remember one sound file can only have one RSound instance.
External Dependencies
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
(Optional)
TimerUtils by Vexorian
wc3c.net/showthread.php?t=101322
User API
¯¯¯¯¯¯¯¯
struct RSound
Instantiate an RapidSound instance
| static method create takes string fileName, boolean is3D, boolean autoStop, integer inRate, integer outRate returns thistype
autoStop => stop when sound is out of range
inRate => fade in rate
outRate => fade out rate
Play the sound at given coordinate
| method play takes real x, real y, real z, integer volume returns nothing
Stop sound
| method stop takes boolean fadeOut returns nothing
Destroy RapidSound instance
| method kill takes nothing returns nothing
Sound file duration (in second)
| method operator duration takes nothing returns real
Resource Link
¯¯¯¯¯¯¯¯¯¯¯¯¯
hiveworkshop.com/threads/snippet-rapidsound.258991/
*/
struct RSound
private static constant integer MAX_COUNT = 4
private static integer Counter = -1
private static string array StrLib
private static thistype array StrDex
private integer ct
private integer lib
private integer dex
private real dur
private sound array snd[thistype.MAX_COUNT]
private timer array tmr[thistype.MAX_COUNT]
method operator duration takes nothing returns real
return .dur*MIN_DELAY_FACTOR
endmethod
method kill takes nothing returns nothing
local integer i
set .ct = .ct - 1
if .ct == 0 then
set i = 0
loop
exitwhen i == MAX_COUNT
call StopSound(.snd[i], true, false)
static if LIBRARY_TimerUtils then
call ReleaseTimer(.tmr[i])
else
call DestroyTimer(.tmr[i])
endif
set .snd[i] = null
set .tmr[i] = null
set i = i + 1
endloop
set StrLib[.lib] = StrLib[Counter]
set StrDex[.lib] = StrDex[Counter]
set Counter = Counter - 1
call deallocate()
endif
endmethod
method stop takes boolean fadeOut returns nothing
local integer i = 0
loop
exitwhen i == MAX_COUNT
call StopSound(.snd[i], false, fadeOut)
set i = i + 1
endloop
endmethod
method play takes real x, real y, real z, integer volume returns nothing
set .dex = .dex + 1
if .dex == MAX_COUNT then
set .dex = 0
endif
if TimerGetRemaining(.tmr[.dex]) == 0 then
call StopSound(.snd[.dex], false, false)
call SetSoundPosition(.snd[.dex], x, y, z)
call SetSoundVolume(.snd[.dex], volume)
call StartSound(.snd[.dex])
call TimerStart(.tmr[.dex], .dur, false, null)
endif
endmethod
static method create takes string fileName, boolean is3D, boolean autoStop, integer inRate, integer outRate returns thistype
local thistype this
local integer i = 0
local boolean b = true
loop
exitwhen i > Counter
if fileName == StrLib[i] then
set b = false
exitwhen true
endif
set i = i + 1
endloop
if b then
set this = allocate()
set Counter = Counter + 1
set StrLib[Counter] = fileName
set StrDex[Counter] = this
set .ct = 1
set .dex = -1
set .lib = Counter
set .dur = I2R(GetSoundFileDuration(fileName))/(1000.*MIN_DELAY_FACTOR)
set i = 0
loop
exitwhen i == MAX_COUNT
set .snd[i] = CreateSound(fileName, false, is3D, autoStop, inRate, outRate, "")
static if LIBRARY_TimerUtils then
set .tmr[i] = NewTimer()
call TimerStart(.tmr[i], 0, false, null)
call PauseTimer(.tmr[i])
else
set .tmr[i] = CreateTimer()
endif
set i = i + 1
endloop
else
set this = StrDex[i]
set .ct = .ct + 1
endif
return this
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly ")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
//TESH.scrollpos=75
//TESH.alwaysfold=0
function IsUnitMovementTracked takes integer i returns boolean
return udg_UMovPrev[i] != 0 or udg_UMovNext[0] == i
endfunction
function UnitMovementRegister takes nothing returns boolean
local integer i = udg_UDex
if not IsUnitMovementTracked(i) and TriggerEvaluate(gg_trg_Is_Unit_Moving_Config) then
set udg_UMovPrev[udg_UMovNext[0]] = i
set udg_UMovNext[i] = udg_UMovNext[0]
set udg_UMovNext[0] = i
set udg_UnitMovingX[i] = GetUnitX(udg_UDexUnits[i])
set udg_UnitMovingY[i] = GetUnitY(udg_UDexUnits[i])
endif
return false
endfunction
function UnitMovementUnregister takes nothing returns boolean
local integer i = udg_UDex
if IsUnitMovementTracked(i) then
set udg_UnitMoving[i] = false
set udg_UMovNext[udg_UMovPrev[i]] = udg_UMovNext[i]
set udg_UMovPrev[udg_UMovNext[i]] = udg_UMovPrev[i]
set udg_UMovPrev[i] = 0
endif
return false
endfunction
function RunUnitMovementEvent takes integer i, real e returns nothing
local integer pdex = udg_UDex
if e == 1.00 then
set udg_UnitMoving[i] = true
else
set udg_UnitMoving[i] = false
endif
set udg_UDex = i
set udg_UnitMovingEvent = e
set udg_UnitMovingEvent = 0.00
set udg_UDex = pdex
endfunction
//===========================================================================
// This function runs periodically to check if units are actually moving.
//
function UnitMovementTracker takes nothing returns nothing
local integer i = 0
local integer n
local real x
local real y
loop
set i = udg_UMovNext[i]
exitwhen i == 0
set x = GetUnitX(udg_UDexUnits[i])
set y = GetUnitY(udg_UDexUnits[i])
if x != udg_UnitMovingX[i] or y != udg_UnitMovingY[i] then
set udg_UnitMovingX[i] = x
set udg_UnitMovingY[i] = y
if not udg_UnitMoving[i] then
if GetUnitTypeId(udg_UDexUnits[i]) != 0 then
call RunUnitMovementEvent(i, 1.00)
else
set n = udg_UDex
set udg_UDex = i
set i = udg_UMovPrev[i] //avoid skipping checks
call UnitMovementUnregister()
set udg_UDex = n
endif
endif
elseif udg_UnitMoving[i] then
call RunUnitMovementEvent(i, 2.00)
endif
endloop
endfunction
//===========================================================================
function InitTrig_Is_Unit_Moving takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
call TriggerAddCondition(t, Filter(function UnitMovementRegister))
set t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Filter(function UnitMovementUnregister))
if gg_trg_Is_Unit_Moving_Config != null then
call TriggerExecute(gg_trg_Is_Unit_Moving_Config)
else
call ExecuteFunc("Trig_Is_Unit_Moving_Config_Actions")
endif
call TimerStart(CreateTimer(), udg_UnitMovementInterval, true, function UnitMovementTracker)
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.0
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 260 + 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=12
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary
//TESH.scrollpos=37
//TESH.alwaysfold=0
library StunSystem uses Table
//********************************************************************************
// Stun - Version 1.2.0.0 - By iAyanami aka Ayanami
//********************************************************************************
//
// Stun:
// - An easy to use system that stuns units
// - Able to keep track of the remaining stun duration
//
// Requirements:
// - JASS NewGen
// - Table
//
// Functions:
// - Stun.apply takes unit whichUnit, real duration, boolean stack returns nothing
// * whichUnit is the target to be stunned
// * duration is the duration of the stun
// * stack is to determine if the stun should be a stacking one or not
// * true - the stun will stack, the duration of the stun will add up with the previous duration
// * false - the stun will not stack, the unit will be stunned for the longer stun duration
//
// - Stun.getDuration takes unit whichUnit returns real
// * whichUnit is the target to check
// * returns the remaining stun duration
//
// - Stun.stop takes unit whichUnit returns nothing
// * removes stun from the target
//
// How to import:
// - Copy the whole "Stun" Trigger Folder into your map
// - Save the map. Close and re-opem the map.
// - You should have a new unit (Stun Dummy), ability (Stun (System)) and buff (Stun (System)) created
// - Read through the Configuration part of the code
//
// Credits:
// - Bribe for Table
//
//********************************************************************************
// CONFIGURABLES
//********************************************************************************
globals
// timer period. lower the value, the more accurate but might cause decrease in
// performance
private constant real PERIOD = 0.03125
// raw code of ability "Stun (System)"
private constant integer ABILID = 'ASTN'
// raw code of buff "Stun (System)"
private constant integer BUFFID = 'BSTN'
// raw code of unit "Stun Dummy"
private constant integer STUNID = 'sTUN'
endglobals
//********************************************************************************
// CODE
//********************************************************************************
// initialization
module Init
private static method onInit takes nothing returns nothing
set table = Table.create()
set caster = CreateUnit(Player(13), STUNID, 0, 0, 0)
call UnitAddAbility(caster, ABILID)
endmethod
endmodule
struct Stun extends array
private unit u
private real dur
private thistype next
private thistype prev
private static Table table
private static timer t = CreateTimer()
private static unit caster
private static integer count = 0
// remove the stun and deallocate
private method destroy takes nothing returns nothing
call UnitRemoveAbility(this.u, BUFFID)
if this.next != 0 then
set this.next.prev = this.prev
endif
set this.prev.next = this.next
set this.dur = 0
set this.prev = thistype(0).prev
set thistype(0).prev = this
if thistype(0).next == 0 then
call PauseTimer(t)
endif
call table.remove(GetHandleId(this.u))
endmethod
// iterating through all instances every PERIOD
private static method iterate takes nothing returns nothing
local thistype this = thistype(0)
loop
set this = this.next
exitwhen this == 0
if this.dur <= 0 or IsUnitType(this.u, UNIT_TYPE_DEAD) or GetUnitTypeId(this.u) == 0 then
call this.destroy()
else
set this.dur = this.dur - PERIOD
endif
endloop
endmethod
// immediately removes stun for the specified unit
// ex: call Stun.stop(whichTarget)
static method stop takes unit u returns nothing
local integer id = GetHandleId(u)
if table.has(id) then
call thistype(table[id]).destroy()
endif
endmethod
// gets the duration left for stun, not stunned units always return 0
// ex: local real r = Stun.getDuration(whichTarget)
static method getDuration takes unit u returns real
return thistype(table[GetHandleId(u)]).dur
endmethod
// stunning specified target and to see if the stun is a stacking one or not
// ex: call Stun.apply(whichTarget, 5.0, false)
static method apply takes unit u, real dur, boolean b returns nothing
local thistype this
local integer id = GetHandleId(u)
if table.has(id) then
set this = table[id]
else
if thistype(0).prev == 0 then
set count = count + 1
set this = count
else
set this = thistype(0).prev
set thistype(0).prev = thistype(0).prev.prev
endif
if thistype(0).next == 0 then
call TimerStart(t, PERIOD, true, function thistype.iterate)
else
set thistype(0).next.prev = this
endif
set this.next = thistype(0).next
set thistype(0).next = this
set this.prev = thistype(0)
set table[id] = this
set this.u = u
set this.dur = 0
call IssueTargetOrder(caster, "firebolt", this.u)
endif
if b and dur > 0 then
set this.dur = this.dur + dur
else
if this.dur < dur then
set this.dur = dur
endif
endif
endmethod
implement Init
endstruct
endlibrary
//TESH.scrollpos=21
//TESH.alwaysfold=0
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
* readonly static integer centerX
* readonly static integer centerY
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX=R2I(GetRectMaxX(world))
set maxY=R2I(GetRectMaxY(world))
set minX=R2I(GetRectMinX(world))
set minY=R2I(GetRectMinY(world))
set centerX=R2I((maxX+minX)/2)
set centerY=R2I((minY+maxY)/2)
set worldRegion=CreateRegion()
call RegionAddRect(worldRegion,world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
*=================================================================================*
* Map Bounds 1.1 *
* By Adiktuz *
* *
* *
* This is a short snippet that provides some useful functions utilizing *
* the WorldBounds library created by Nestharus *
* *
* *
* Credits: *
* Nestharus for the WorldBounds library *
* Magtheridon96 for suggestions *
* *
* Functions: *
* *
* -> mapIncludeX(real x) returns boolean *
* -> mapIncludeY(real y) returns boolean *
* -> GetBoundedX(real x) returns real *
* -> GetBoundedY(real y) returns real *
* *
* *
* The mapIncludeX/Y functions return if the given x/y value is inside the map *
* *
* The GetBoundedX/Y checks if the given value is within the map. If it's *
* within the map, it will return the value given and if Not *
* it will return the value of the border that it will exceed *
* *
*=================================================================================*
*/
library MapBounds requires WorldBounds
function mapIncludeX takes real x returns boolean
return x < WorldBounds.maxX and x > WorldBounds.minX
endfunction
function mapIncludeY takes real y returns boolean
return y < WorldBounds.maxY and y > WorldBounds.minY
endfunction
function GetBoundedX takes real x returns real
if x > WorldBounds.maxX then
return I2R(WorldBounds.maxX)
elseif x < WorldBounds.minX then
return I2R(WorldBounds.minX)
endif
return x
endfunction
function GetBoundedY takes real y returns real
if y > WorldBounds.maxY then
return I2R(WorldBounds.maxY)
elseif y < WorldBounds.minY then
return I2R(WorldBounds.minY)
endif
return y
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
Jump in Place version 1.02
by Adiktuz
Basically, this library handles jumping in place, but mainly for usage with the
impale system
It also allows the user to specify actions to be done after the jump is done
You're also allowed to choose what to do when a unit that is currently jumping
is re-jumped again by modifying the value of the jumptype parameter
You can use either:
JUMP_TYPE_STACK - stack each instance of jump for the unit
JUMP_TYPE_HEIGHT_STACK - stack only the jump height
JUMP_TYPE_RESET - stops the existing jump and resets unit height before doing the jump
JUMP_TYPE_NO_OVERWRITE - don't allow stacking
Disclaimer: Mixing these jump types might look weird...
Please take note that if you registered a StopEvent for the spell, and set the jumps to stack or reset,
the event will only run once when the unit hits the ground
How to Use:
call JumpInPlace.fire(unit flyUnit, unit causeUnit, real duration, real maxheight, integer abil, integer jumptype)
unit flyUnit -> unit that will jump
unit causeUnit -> the unit that caused the jump
real duration -> duration of jump
real maxheight -> maxheight of jump
integer abil -> rawcode of the ability that caused the jump
integer jumptype -> jump type
How to set action for when the unit returns to it's original height:
call JumpInPlace.registerStopEvent(integer abil, code action)
integer abil => the rawcode of the spell in which you want to add the action
use 0 if it won't be tied to any ability
code action => the function which will be run
You can also register global stop and start actions
call JumpInPlace.registerGlobalEndEvent(code action)
-> fires for any jump that finishes
call JumpInPlace.registerGlobalStartEvent(code action)
-> fires for any jump that starts
But before using them, make sure to set their correspoding variables to true on the globals block below
Variables you can use for the Event handlers
JumpInPlace.tmpFlyUnit => unit that triggered the event (the jumping unit)
JumpInPlace.tmpCauseUnit => the unit that caused the FlyUnit to jump
*/
library JumpInPlace requires T32, Table
globals
//Do you have an auto-fly library (like Magtheridon96's or Nestharus')?
private boolean AUTO_FLY = false
private constant integer FLY_ID = 'Amrf'
//Set to true if you're gonna use the global start event handler
private constant boolean USE_GLOBAL_START = false
//Set to true if you're gonna use the global end event handler
private constant boolean USE_GLOBAL_END = false
//Do not touch
constant integer JUMP_TYPE_STACK = 1
constant integer JUMP_TYPE_HEIGHT_STACK = 2
constant integer JUMP_TYPE_RESET = 3
constant integer JUMP_TYPE_NO_OVERWRITE = 4
endglobals
private module init
static method onInit takes nothing returns nothing
set FlyTable = Table.create()
set EventTable = Table.create()
static if USE_GLOBAL_START then
set globalStart = CreateTrigger()
endif
static if USE_GLOBAL_END then
set globalEnd = CreateTrigger()
endif
endmethod
endmodule
struct JumpInPlace extends array
private static integer instanceCount = 0
private static thistype recycle = 0
private thistype recycleNext
static Table FlyTable
static Table EventTable
static trigger globalEnd
static trigger globalStart
static unit tmpFlyUnit
static unit tmpCauseUnit
unit flyUnit
unit causeUnit
real maxheight
real hps
real height
real dfh
boolean up
integer abil
static method registerStopEvent takes integer abil, code toDo returns nothing
if not EventTable.handle.has(abil) then
set EventTable.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(EventTable.trigger[abil], Filter(toDo))
endmethod
static method registerGlobalStartEvent takes code toDo returns nothing
call TriggerAddCondition(globalStart,Filter(toDo))
endmethod
static method registerGlobalEndEvent takes code toDo returns nothing
call TriggerAddCondition(globalEnd,Filter(toDo))
endmethod
method stop takes nothing returns nothing
call SetUnitFlyHeight(this.flyUnit, this.dfh, 0.0)
call this.stopPeriodic()
set thistype.tmpFlyUnit = this.flyUnit
set thistype.tmpCauseUnit = this.causeUnit
if EventTable.handle.has(this.abil) then
call TriggerEvaluate(EventTable.trigger[this.abil])
endif
static if USE_GLOBAL_END then
call TriggerEvaluate(globalEnd)
endif
set FlyTable[GetHandleId(this.flyUnit)] = 0
set recycleNext = recycle
set recycle = this
endmethod
method periodic takes nothing returns nothing
if this.up then
set this.height = this.height + hps
if this.height >= this.maxheight then
set this.up = false
endif
call SetUnitFlyHeight(this.flyUnit, this.height, 0.0)
else
set this.height = this.height - hps
call SetUnitFlyHeight(this.flyUnit, this.height, 0.0)
if this.height <= this.dfh then
set this.up = true
call this.stop()
endif
endif
endmethod
implement T32x
static method fire takes unit flyUnit, unit causeUnit, real duration, real maxheight, integer abil, integer jumptype returns nothing
local thistype this
local integer id = GetHandleId(flyUnit)
if FlyTable[id] == 0 or jumptype == 1 then
if (recycle == 0) then
set instanceCount = instanceCount + 1
set this = instanceCount
else
set this = recycle
set recycle = recycle.recycleNext
endif
set FlyTable[id] = this
static if not AUTO_FLY then
if UnitAddAbility(flyUnit, FLY_ID) and UnitRemoveAbility(flyUnit, FLY_ID) then
endif
endif
set this.abil = abil
set this.causeUnit = causeUnit
set this.flyUnit = flyUnit
set this.dfh = GetUnitDefaultFlyHeight(flyUnit)
set this.maxheight = maxheight + dfh
set this.hps = (maxheight*2/duration)*T32_PERIOD
set this.height = dfh
set this.up = true
call this.startPeriodic()
else
set this = FlyTable[id]
if jumptype == 2 then
static if not AUTO_FLY then
if UnitAddAbility(flyUnit, FLY_ID) and UnitRemoveAbility(flyUnit, FLY_ID) then
endif
endif
set this.abil = abil
set this.causeUnit = causeUnit
set this.maxheight = this.maxheight + maxheight
set this.hps = ((this.maxheight*2 - this.height)/duration)*T32_PERIOD
set this.up = true
elseif jumptype == 3 then
static if not AUTO_FLY then
if UnitAddAbility(flyUnit, FLY_ID) and UnitRemoveAbility(flyUnit, FLY_ID) then
endif
endif
set this.causeUnit = causeUnit
set this.abil = abil
call SetUnitFlyHeight(this.flyUnit, dfh, 0.0)
set this.maxheight = maxheight + dfh
set this.hps = (maxheight*2/duration)*T32_PERIOD
set this.height = dfh
set this.up = true
elseif jumptype == 4 then
return
endif
endif
set thistype.tmpFlyUnit = this.flyUnit
set thistype.tmpCauseUnit = this.causeUnit
static if USE_GLOBAL_START then
call TriggerEvaluate(globalStart)
endif
endmethod
implement init
endstruct
endlibrary
//==========================================================================
// Dark Dragon Library Code v1.3
//
// * Made on Warcraft III v1.30.4
//
// Installation:
//
// 1) Export instantdummy.mdx from this map and import it to your map, leave path at "war3mapImported/instantdummy.mdx"
// 2) Copy this trigger to your map, save your map and then change below line "// external ... " or copy "DD Dummy" and paste it in your map
// 3) Copy and paste "Unit Chill" ability from this map to your map
// 4) Match the rawcodes below to your map or use same ones as below
//
// Credits:
// ('Vexorian' - dummy.mdx)
//============================================================================
// *** Change "// external" to "//! external", save your map, close map, change back from "//!" to "//" and save map again.
// *** This will create dummy in your map
//
// ==================================
// external ObjectMerger w3u ushd dumy uabi "Aloc,Amrf" uble 0 ucbs 0 ucpt 0 umxp 0 umxr 0 umdl "war3mapImported\instantdummy.mdx" ushu "None" umvh 0 umvs 1 umas 1 umis 1 ucol 0 ufoo 0 uhom 1 umpi 10000 umpm 10000 usid 1 usin 1 unam "DD Dummy"
// ==================================
//! zinc
library DDLib requires optional TimerUtils, optional GroupUtils
{
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// *** Lib constants ***
public {
// ----------------------------------------------------------------------
// * Start modify/match rawcodes to your map
constant integer DD_DUMMYCODE = 'dumy';
constant integer DD_ABILITY_CROWN_FORM = 'Amrf';
constant integer DD_CHILL = 'Achl';
constant integer DD_CHILL_BUFF = 'Bfro';
// * End modify
// ----------------------------------------------------------------------
constant integer p_null = (0x0);
constant real DD_INTERVAL = .017;
// map min and max coords
real DDMinX = 0.;
real DDMinY = 0.;
real DDMaxX = 0.;
real DDMaxY = 0.;
}
private {
constant integer HARVEST_ID = 'Ahrl';
constant real TRIGGER_REFRESH_RATE = (60.)*3.; /// damage detection trigger
unit TreeChecker = null;
trigger TempTrig = null;
integer NTrig = 0;
trigger DmgTrig[];
p_real EnumVec = p_null;
boolexpr EnumFilter = null;
sound ErrorSound = null;
timer GameElapsedTimer = null;
constant integer RND_INT_MAX_ARRAY_N = 100;
integer RndInt[], RndIntWriteN = 00, RndIntReadN = 00;
trigger TrigMouseEvent = null;
force RndGenForce = null;
real RndElapsedTime = 0.;
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// * types
public {
// *** pointer to list of data ***
type p_integer extends integer[8];
type p_real extends real[8];
type p_unit extends unit[8];
function H2ID(handle h) -> integer {
return GetHandleId(h) - 0x100000;
}
function New_pInteger(integer i) -> p_integer
{ p_integer p = p_integer.create(); p[0] = i; return p; }
function New_pReal(real r) -> p_real
{ p_real p = p_real.create(); p[0] = r; return p; }
function New_pUnit(unit u) -> p_unit
{ p_unit p = p_unit.create(); p[0] = u; return p; }
function pVector(real x, real y, real z) -> p_real {
p_real v = p_real.create();
v[0] = x; v[1] = y; v[2] = z;
return v;
}
// --------------------------------------------------------------------------------------
function DDMsg(string str) {
DisplayTimedTextFromPlayer(GetLocalPlayer(), 0., 0., 30., str);
}
// --------------------------------------------------------------------------------------
function DisplayErrorMsgPlayer(player p, real dur, string msg) {
if (GetLocalPlayer() == p) {
StartSound(ErrorSound);
DisplayTimedTextFromPlayer(p, 0., 0., dur, "|cffffcc00"+ msg +"|r");
}
}
}
// -----------------------------------------------------------------------
// -> ***** private globals *****
// -----------------------------------------------------------------------
private {
location TempLoc = Location(0., 0.);
timer TimerStack[];
integer TimN = 0;
group GroupStack[];
integer GrpN = 0;
unit DummyStack[];
integer DumN = 0;
integer TimTicks[];
integer TimData[];
timer TimTim1[];
timer TimTim2[];
integer UnitStackData = 0;
unit UnitStack[];
integer US_N = 0;
public hashtable DDHT = InitHashtable();
}
// -----------------------------------------------------------------------
public {
// *** Global funcs
function Pw_2(real x) -> real {
return x*x;
}
function DDHypot(real x, real y) -> real {
return (x*x) + (y*y);
}
function DDTerrZ(real x, real y) -> real {
MoveLocation(TempLoc, x, y);
return GetLocationZ(TempLoc);
}
function DDWidgetTerrZ(widget w) -> real {
MoveLocation(TempLoc, GetWidgetX(w), GetWidgetY(w));
return GetLocationZ(TempLoc);
}
function DDEffectTerrZ(effect e) -> real {
MoveLocation(TempLoc, BlzGetLocalSpecialEffectX(e), BlzGetLocalSpecialEffectY(e));
return GetLocationZ(TempLoc);
}
function DDGetUnitZ(unit u) -> real {
return BlzGetUnitZ(u) + GetUnitFlyHeight(u);
}
// =================================================================
// *** Save Handle data ****
// =================================================================
function DDSet(handle h, integer id, integer val) {
SaveInteger(DDHT, id+1, GetHandleId(h), val);
}
function DDGet(handle h, integer id) -> integer {
return LoadInteger(DDHT, id+1, GetHandleId(h));
}
function DDHas(handle h, integer id) -> boolean {
return HaveSavedInteger(DDHT, id+1, GetHandleId(h));
}
function DDFlush(integer id) {
FlushChildHashtable(DDHT, id+1);
}
// =================================================================
// *** Timer Handling ****
// =================================================================
// -> check if timer is recycled
function DDIsTimRecycled(timer t) -> boolean {
integer i;
for(i=TimN-01; i >= 00; i-=01)
if (TimerStack[i] == t)
return true;
return false;
}
// -> Load timer for recycling
function DDLoadTim() -> timer {
static if (LIBRARY_TimerUtils) { return NewTimer(); }
else {
if (TimN > 0) {
TimN -= 1;
return TimerStack[TimN];
}
return CreateTimer();
}
}
// -> recycle loaded timer
function DDRecycleTim(timer t) {
static if (LIBRARY_TimerUtils) { ReleaseTimer(t); }
else {
static if (DEBUG_MODE)
if (DDIsTimRecycled(t)) {
DDMsg("Multiple recycle of timer!");
return;
}
TimerStack[TimN] = t;
TimN += 1;
}
}
// ** Get data stored on expired timer
function DDTimData() -> integer {
return TimData[H2ID(GetExpiredTimer())];
}
// *** Custom timer tick
function DDCTimTick(timer t) -> integer {
return TimTicks[H2ID(t)];
}
// *** Gets current tick and adds next one ***
function DDTimTick() -> integer {
integer id = H2ID(GetExpiredTimer());
TimTicks[id] += 01;
return TimTicks[id];
}
// ** start timer with data storage
function DDStartTim(real secs, boolean looping, integer pdata, code func) -> timer {
timer t = DDLoadTim();
TimData[H2ID(t)] = pdata;
TimerStart(t, secs, looping, func);
return t;
}
// ** start timer with data storage, and launches it instantly
function DDStartTimInst(real secs, boolean looping, integer pdata, code func) -> timer {
timer t1 = DDLoadTim(), t2 = DDLoadTim(), t3 = DDLoadTim();
TimData[H2ID(t2)] = pdata;
TimerStart(t2, 0., false, func);
TimTim1[H2ID(t3)] = t1;
TimTim2[H2ID(t3)] = t2;
TimerStart(t3, .005, false, function() {
timer t = GetExpiredTimer();
integer id = H2ID(t);
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
t = TimTim2[id];
if (DDIsTimRecycled(t))
t = TimTim1[id];
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
});
TimData[H2ID(t1)] = pdata;
TimerStart(t1, secs, looping, func);
return t1;
}
// *** Quit expired timer ***
function DDQuitTim() {
timer t = GetExpiredTimer();
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
}
function DDQuitTimEx(timer t) {
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
}
// =================================================================
// *** Group Handling ****
// =================================================================
// -> Load timer for recycling
function DDLoadGroup() -> group {
static if (LIBRARY_GroupUtils) { return NewGroup(); }
else {
if (GrpN > 0) {
GrpN -= 1;
return GroupStack[GrpN];
}
return CreateGroup();
}
}
// -> Recycle group
function DDRecycleGroup(group g) {
static if (LIBRARY_GroupUtils) { ReleaseGroup(g); }
else {
GroupClear(g);
GroupStack[GrpN] = g;
GrpN += 1;
}
}
// --------------------------------------------------------
// -- Quick filter area
private integer GroupFilterData = 00;
function DDGroupFilterArea(real x, real y, real radius, integer data, code func) {
group g = DDLoadGroup();
GroupFilterData = data;
GroupEnumUnitsInRange(g, x, y, radius, Filter(func));
DDRecycleGroup(g);
}
// --------------------------------------------------------
// -- Quick filter rect
function DDGroupFilterRect(rect r, integer data, code func) {
group g = DDLoadGroup();
GroupFilterData = data;
GroupEnumUnitsInRect(g, r, Filter(func));
DDRecycleGroup(g);
}
function DDGFilterData() -> integer {
return GroupFilterData;
}
function DDGFilterDataSet(integer data) {
GroupFilterData = data;
}
// --------------------------------------------------------
// *** Filtrates and fills units in to memory
function DDGroupFillMemArea(real x, real y, real radius, integer data, code filter) {
group g = DDLoadGroup();
boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
UnitStack[US_N] = GetFilterUnit();
US_N += 1;
return false;
}));
US_N = 0;
UnitStack[0] = null;
UnitStackData = data;
GroupEnumUnitsInRange(g, x, y, radius, exp);
DDRecycleGroup(g);
DestroyBoolExpr(exp);
exp = null;
}
function DDGroupFillMemRect(rect r, integer data, code filter) {
group g = DDLoadGroup();
boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
UnitStack[US_N] = GetFilterUnit();
US_N += 1;
return false;
}));
US_N = 0;
UnitStack[0] = null;
UnitStackData = data;
GroupEnumUnitsInRect(g, r, exp);
DDRecycleGroup(g);
DestroyBoolExpr(exp);
exp = null;
}
function DDMemUnitN() -> integer { return US_N; }
function DDMemUnitData() -> integer { return UnitStackData; }
function DDMemUnit(integer index) -> unit {
if (US_N == 0) return null;
debug {
if (index < 0) {
BJDebugMsg("DDMemUnit: index less than 0");
index = 0;
} else if (index >= US_N) {
BJDebugMsg("DDMemUnit: index greater than units alloc size");
index = 0;
}
}
return UnitStack[index];
}
// --------------------------------------------------------
// --------------------------------------------------------
// ***
// =================================================================
// *** Dummy Handling ****
// =================================================================
// -> Load dummy for recycling
function DDLoadDummy() -> unit {
if (DumN > 0) {
DumN -= 1;
PauseUnit(DummyStack[DumN], false);
return DummyStack[DumN];
}
return CreateUnit(Player(0xF), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
}
// *** prepares/setups dummy for spell casting
function DDLoadSpellDummy(player owner, real x, real y, integer abil, integer abilLevel) -> unit {
unit dummy = DDLoadDummy();
SetUnitOwner(dummy, owner, false);
SetUnitX(dummy, x);
SetUnitY(dummy, y);
if (abil != p_null) {
UnitAddAbility(dummy, abil);
SetUnitAbilityLevel(dummy, abil, abilLevel);
}
return dummy;
}
// -> Recycle dummy
function DDRecycleDummy(unit u) {
PauseUnit(u, true);
DummyStack[DumN] = u;
DumN += 1;
}
// -> Recycle dummy timed
function DDRecycleDummyTimed(unit u, real secs) {
DDStartTim(secs, false, New_pUnit(u), function() {
DDRecycleDummy(p_unit(DDTimData())[0]);
p_unit(DDTimData()).destroy();
DDQuitTim();
});
}
// *** shares vision for timed amount, usually for dummy casting
function DDUnitShareVisionTimed(unit u, player toP, real secs) {
p_integer pi = p_integer.create();
pi[0] = New_pUnit(u);
pi[1] = GetPlayerId(toP);
UnitShareVision(u, toP, true);
DDStartTim(secs, false, pi, function() {
p_integer pi = DDTimData();
UnitShareVision(p_unit(pi[0])[0], Player(pi[1]), false);
p_unit(pi[0])[0] = null;
p_unit(pi[0]).destroy();
pi.destroy();
DDQuitTim();
});
}
// *** Remove ability timed ***
private struct uratimed {
private {
unit u;
integer abil;
}
static method create(unit whichUnit, integer id, real time) -> uratimed {
thistype this = allocate();
u = whichUnit;
abil = id;
DDStartTim(time, false, this, function() {
thistype this = DDTimData();
UnitRemoveAbility(u, abil);
DDQuitTim();
deallocate();
});
return 0;
}
}
function DDRemoveAbilityTimed(unit u, integer abil, real secs) { uratimed.create(u, abil, secs); }
function DDSpellDamage(unit u, unit v, real dmg) {
real life = GetWidgetLife(v);
real dmgfactor = 1.;
if (IsUnitType(v, UNIT_TYPE_HERO)) {
if (UnitHasItemOfTypeBJ(v, 'brac'))
dmgfactor = .5;
else
dmgfactor = .75;
}
if (life > dmg*dmgfactor) {
SetWidgetLife(v, life-(dmg*dmgfactor));
} else
UnitDamageTarget(u, v, 99999., false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
}
// -------------------------------------------------------------------------------------
// *** Chills target unit
private struct chill {
unit u, dmy;
real dur;
static chill Data[];
//static key CHILL_KEY;
}
function DDUnitChill(player p, unit u, real dur) -> boolean {
//chill c = DDGet(u, chill.CHILL_KEY);
chill c = chill.Data[H2ID(u)];
unit d;
real rad;
if (c == p_null) {
c = chill.create();
c.u = u; c.dur = dur;
chill.Data[H2ID(u)] = c;
//DDSet(u, chill.CHILL_KEY, c);
d = DDLoadDummy();
c.dmy = d;
rad = GetUnitFacing(d) * bj_DEGTORAD;
SetUnitOwner(d, p, false);
UnitAddAbility(d, DD_CHILL);
SetUnitX(d, GetUnitX(u) - 20.*Cos(rad));
SetUnitY(d, GetUnitY(u) - 20.*Sin(rad));
if (IssueTargetOrder(d, "frostnova", u)) {
DDStartTim(.1, true, c, function() {
chill c = DDTimData();
c.dur -= .1;
if (c.dur <= 0. || GetUnitAbilityLevel(c.u, DD_CHILL_BUFF) == 00) {
UnitRemoveAbility(c.u, DD_CHILL_BUFF);
UnitRemoveAbility(c.dmy, DD_CHILL);
DDRecycleDummy(c.dmy);
chill.Data[H2ID(c.u)] = p_null;
//DDSet(c.u, chill.CHILL_KEY, p_null);
c.u = null;
c.destroy();
DDQuitTim();
}
});
return true;
}
return false;
}
c.dur = dur;
return true;
}
// ------------------------------------------------------------------------------------------------
struct fade {
unit u;
real trans;
real rate, e_trans, dur;
static constant real INTERVAL = .1;
static method create(unit u, real dur, real s_trans, real e_trans) -> fade {
fade this = allocate();
this.u = u;
this.trans = s_trans;
this.rate = ((e_trans-s_trans)/dur)*fade.INTERVAL;
this.e_trans = e_trans;
this.dur = dur;
return this;
}
}
// *** Fades unit over time ***
public function DDFadeUnit(unit u, integer from_alpha, integer to_alpha, real duration) {
fade f = fade.create(u,
duration,
from_alpha/2.55,
to_alpha/2.55);
SetUnitVertexColor(u, 255, 255, 255, from_alpha);
// --- Start thread ---
DDStartTim(fade.INTERVAL, true, f, function() {
fade f = DDTimData();
f.trans += f.rate;
f.dur -= fade.INTERVAL;
SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.trans*2.55));
if (f.dur < 0.) {
SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.e_trans*2.55));
f.u = null;
f.destroy();
DDQuitTim();
}
});
}
// ------------------------------------------------------------------------------------------------
// Check if unit is invulnerable
function DDIsUnitInvulnerable(unit u) -> boolean {
unit d = DDLoadDummy();
real hp = GetWidgetLife(u);
boolean flag;
UnitDamageTarget(d, u, 1., true, false, null, null, null);
flag = GetWidgetLife(u) == hp;
SetWidgetLife(u, hp);
DDRecycleDummy(d);
return flag;
}
// *** check if unit is ward
function DDIsUnitWard(unit whichUnit) -> boolean {
return GetUnitDefaultMoveSpeed(whichUnit) == 0.;
}
// =================================================================
// *** Effect Handling ****
// =================================================================
// -----------------------------------------------
// *** Define movable effect
struct ddeffect {
private {
effect e;
real fac; // facing angle in radians
real effZ; // pitch in radians
real decay;
real stepTrans, cTrans, eTrans;
static constant real EFFECT_DECAY = 5.;
}
// =========================================================================================
// =========================================================================================
static method create(string mdl, real x, real y, real facRad, real size) -> ddeffect {
ddeffect this = allocate();
this.e = AddSpecialEffect(mdl, x, y);
this.fac = facRad;
this.effZ = 0.;
BlzSetSpecialEffectRoll(this.e, facRad);
BlzSetSpecialEffectScale(this.e, size);
return this;
}
static method createZ(string mdl, real x, real y, real z, real facRad, real size) -> ddeffect {
ddeffect this = allocate();
this.e = AddSpecialEffect(mdl, x, y);
this.fac = facRad;
this.effZ = z-DDTerrZ(x, y);
BlzSetSpecialEffectRoll(this.e, facRad);
BlzSetSpecialEffectScale(this.e, size);
BlzSetSpecialEffectZ(this.e, z);
return this;
}
// -----------------
method destroy() {
DestroyEffect(this.e);
this.e = null;
deallocate();
}
// *** destroys effect timed
method destroyx(real decayTime) {
DDStartTim(decayTime, false, this, function() {
ddeffect se = DDTimData();
BlzSetSpecialEffectPosition(se.e, DDMaxX, DDMaxY, 0.);
DestroyEffect(se.e);
se.e = null;
se.deallocate();
DDQuitTim();
});
}
// =========================================================================================
// =========================================================================================
method operator Z=(real z) { BlzSetSpecialEffectZ(this.e, z); }
method operator X() -> real { return BlzGetLocalSpecialEffectX(this.e); }
method operator Y() -> real { return BlzGetLocalSpecialEffectY(this.e); }
method operator Z() -> real { return BlzGetLocalSpecialEffectZ(this.e); }
method operator WZ() -> real { return DDEffectTerrZ(this.e); }
method operator Height() -> real { return this.Z-this.WZ; }
method operator Facing=(real facRad) { BlzSetSpecialEffectRoll(this.e, facRad); this.fac = facRad; }
method operator Facing() -> real { return this.fac; }
method Position(real x, real y) { BlzSetSpecialEffectPosition(this.e, x, y, this.effZ+this.WZ); }
method PositionZ(real x, real y, real z) { BlzSetSpecialEffectPosition(this.e, x, y, z); }
method Animation(animtype at) { BlzPlaySpecialEffect(this.e, at); }
method AnimationSpeed(real animSpeed) { BlzSetSpecialEffectTimeScale(this.e, animSpeed/100.); }
//method operator Pitch=(integer pitch) { SetUnitAnimationByIndex(u, pitch); }
//method Face(widget w) { Facing = Atan2(GetWidgetY(w)-Y, GetWidgetX(w)-X)*bj_RADTODEG; }
method Fade(real startTransparency, real endTransparency, real duration) {
this.cTrans = startTransparency;
this.eTrans = endTransparency;
this.stepTrans = .1*(endTransparency-startTransparency) / duration;
BlzSetSpecialEffectAlpha(this.e, R2I(startTransparency*2.55));
DDStartTim(.1, true, this, function() {
ddeffect dde = DDTimData();
dde.cTrans += dde.stepTrans;
if (dde.stepTrans > 0.)
if (dde.cTrans >= dde.eTrans) {
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
DDQuitTim();
return;
}
else
if (dde.cTrans <= dde.eTrans) {
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
DDQuitTim();
return;
}
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.cTrans*2.55));
});
}
}
private type timedeffect extends effect[01];
function DDDestroyEffectTimed(effect e, real secs) {
timedeffect te = timedeffect.create();
te[00] = e;
DDStartTim(secs, true, te, function() {
timedeffect te = DDTimData();
DestroyEffect(te[00]);
te.destroy();
DDQuitTim();
});
}
}
// ----------------------------------------------------------------------------
// *** Main damage detection function, registers any damage dealt to units ***
public function DDTriggerRegisterAnyUnitDamaged(trigger t) {
DmgTrig[NTrig] = t;
NTrig += 1;
}
function InitDamageDetection() {
code c = function() {
if (TempTrig != null)
DestroyTrigger(TempTrig);
TempTrig = CreateTrigger();
TriggerRegisterEnterRectSimple(TempTrig, bj_mapInitialPlayableArea);
TriggerAddCondition(TempTrig, function() -> boolean {
integer i;
// *** Check if we need to exec triggers or register an unit ***
if (GetTriggerEventId() == EVENT_UNIT_DAMAGED) {
for(i=0; i < NTrig; i+=1)
if (IsTriggerEnabled(DmgTrig[i]))
if (TriggerEvaluate(DmgTrig[i]))
TriggerExecute(DmgTrig[i]);
}
else
// *** Register new unit ***
TriggerRegisterUnitEvent(GetTriggeringTrigger(),
GetTriggerUnit(),
EVENT_UNIT_DAMAGED);
return false;
});
DDGroupFilterRect(bj_mapInitialPlayableArea, 00, function() -> boolean {
TriggerRegisterUnitEvent(TempTrig, GetFilterUnit(), EVENT_UNIT_DAMAGED);
return false;
});
};
trigger t = CreateTrigger();
TriggerAddAction(t, c);
TriggerExecute(t);
DestroyTrigger(t);
TimerStart(CreateTimer(), TRIGGER_REFRESH_RATE, true, c);
t = null;
}
// ---------------------------------------------------------------------------------
// *** Main enum dests in range function ***
public function DDEnumDestsInRange(p_real vec, real radius, boolexpr filter, code pfunc) {
rect r = Rect(vec[0]-radius, vec[1]-radius, vec[0]+radius, vec[1]+radius);
EnumVec[0] = vec[0];
EnumVec[1] = vec[1];
EnumVec[2] = radius;
if (filter != null) filter = And(EnumFilter, filter);
else filter = EnumFilter;
EnumDestructablesInRect(r, filter, pfunc);
if (filter != EnumFilter) { DestroyBoolExpr(filter); filter = null; }
RemoveRect(r);
r = null;
}
function InitEnumDests() {
EnumVec = p_real.create();
EnumFilter = Filter(function() -> boolean {
return Pw_2(EnumVec[0]-GetDestructableX(GetFilterDestructable())) + Pw_2(EnumVec[1]-GetDestructableY(GetFilterDestructable())) < Pw_2(EnumVec[2]);
});
}
// --------------------------------------------------------------------------------------
// *** checks is destruct tree ***
public function DDIsDestructableTree(destructable d) -> boolean {
if (d != null) {
PauseUnit(TreeChecker, false);
if (IssueTargetOrder(TreeChecker, "harvest", d)) {
PauseUnit(TreeChecker, true);
return true;
}
PauseUnit(TreeChecker, true);
}
return false;
}
function InitDestTreeCheck() {
TreeChecker = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
UnitAddAbility(TreeChecker, HARVEST_ID);
PauseUnit(TreeChecker, true);
}
// --------------------------------------------------------------------------------------
public function DDNewTextTagUnit(unit whichUnit, string text, real dur, real red, real green, real blue, real transparency) {
CreateTextTagUnitBJ( text, whichUnit, 0., 11.00, red, green, blue, transparency );
SetTextTagPermanentBJ( bj_lastCreatedTextTag, false );
SetTextTagVelocityBJ( bj_lastCreatedTextTag, 48.00, 90 );
SetTextTagFadepointBJ( bj_lastCreatedTextTag, dur-1.25 );
SetTextTagLifespanBJ( bj_lastCreatedTextTag, dur );
}
// --------------------------------------------------------------------------------------
struct cameranoisedata {
player p[12];
integer n=00;
}
public function DDCameraSetSourceNoiseForPlayers(real x, real y, real mag, real vel, real maxDist, real duration) {
integer i;
real d;
cameranoisedata cnd = cameranoisedata.create();
for (i=00; i < bj_MAX_PLAYERS; i+=01) {
if (GetLocalPlayer() == Player(i)) {
d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
if (d < maxDist) {
cnd.p[cnd.n] = Player(i);
CameraSetSourceNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
CameraSetTargetNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
cnd.n += 01;
}
}
}
DDStartTim(duration, false, cnd, function() {
cameranoisedata cnd = DDTimData();
while(cnd.n != 00) {
cnd.n -= 01;
if (GetLocalPlayer() == cnd.p[cnd.n])
CameraSetSourceNoise(0., 0.);
CameraSetTargetNoise(0., 0.);
}
cnd.destroy();
DDQuitTim();
});
}
// --------------------------------------------------------------------------------------
hashtable GenSndTable = null;
public function DDGenericSound(string file, real vol, real x, real y, real mxDist, real pitch) {
sound s;
real d;
integer i;
integer snd_n, sh;
// Play sound and shake camera for players within spell cast range
for (i=00; i < bj_MAX_PLAYERS; i+=01) {
if (GetLocalPlayer() == Player(i)) {
d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
if (d < mxDist) {
sh = StringHash(file);
snd_n = LoadInteger(GenSndTable, sh, 04);
s = LoadSoundHandle(GenSndTable, sh, snd_n);
if (s == null) {
s = CreateSound(file, false, false, false, 10, 10, "");
SaveSoundHandle(GenSndTable, sh, snd_n, s);
} else if (GetSoundIsPlaying(s)) {
StopSound(s, false, false);
}
SetSoundPitch(s, pitch);
SetSoundVolume(s, R2I((vol-d*(vol/mxDist))*1.27));
StartSound(s);
snd_n += 01;
if (snd_n == 04)
snd_n = 00;
SaveInteger(GenSndTable, sh, 04, snd_n);
}
}
}
}
public function DDGetGameElapsedTime() -> real {
return TimerGetElapsed(GameElapsedTimer);
}
public function DDGetRndReal(real min, real max) -> real {
real rnd = ((max-min)/1000000.)*I2R(RndInt[RndIntReadN]);
debug if (max > 1000000.)
DDMsg("ERROR: \"DDGetRndNumber\" - 'max' variable is greater than 1000000!");
RndIntReadN += 01; if (RndIntReadN == RND_INT_MAX_ARRAY_N) RndIntReadN = 00;
return min + rnd;
}
public function DDGetRndInt(integer min, integer max) -> integer {
return R2I( DDGetRndReal(I2R(min), I2R(max)) );
}
// ====================================================================
function onInit() {
InitDamageDetection();
InitDestTreeCheck();
InitEnumDests();
DDMinX = GetRectMinX(bj_mapInitialPlayableArea);
DDMinY = GetRectMinY(bj_mapInitialPlayableArea);
DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea);
DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea);
GenSndTable = InitHashtable();
ErrorSound = CreateSound( "Sound\\Interface\\Error.wav", false, false, false, 10, 10, "" );
SetSoundParamsFromLabel( ErrorSound, "InterfaceError" );
SetSoundDuration( ErrorSound, 614 );
SetSoundVolume(ErrorSound, 127);
GameElapsedTimer = CreateTimer();
TimerStart(GameElapsedTimer, 10800., false, null);
for(RndIntWriteN=00; RndIntWriteN < RND_INT_MAX_ARRAY_N; RndIntWriteN+=01)
RndInt[RndIntWriteN] = GetRandomInt(00, 1000000);
RndIntWriteN = 00;
RndGenForce = CreateForce();
TrigMouseEvent = CreateTrigger();
ForForce(bj_FORCE_ALL_PLAYERS, function() {
if (GetPlayerController(GetEnumPlayer()) == MAP_CONTROL_USER)
TriggerRegisterPlayerEvent(TrigMouseEvent, GetEnumPlayer(), EVENT_PLAYER_MOUSE_MOVE);
});
TriggerAddCondition(TrigMouseEvent, Condition(function() -> boolean {
real mouseN;
boolean xFirst = GetRandomInt(00, 01) == 01;
if (!IsPlayerInForce(GetTriggerPlayer(), RndGenForce)) {
// example: input x = 578.4571496
// output rnd n = 4571498
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
if (mouseN == 0.)
return false;
//mouseN *= 100.;
RndInt[RndIntWriteN] = R2I((mouseN - I2R(R2I(mouseN))) * 1000.);
//DDMsg(I2S(RndInt[RndIntWriteN]));
//RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
RndInt[RndIntWriteN] += R2I((mouseN - I2R(R2I(mouseN))) * 1000.)*1000;
//DDMsg(I2S(RndInt[RndIntWriteN]));
RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
ForceAddPlayer(RndGenForce, GetTriggerPlayer());
}
if (DDGetGameElapsedTime()-RndElapsedTime > .125) {
ForceClear(RndGenForce);
RndElapsedTime = DDGetGameElapsedTime();
}
return false;
}));
}
}
//! endzinc
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
Impale System 1.1c requires T32, Table, JumpInPlace, StunSystem, *MapBound*
by Adiktuz
Credits to Bribe, Magtheridon96 and iAyanami for their system that were used
on this library and the sample spells
An impale system which allows you to create custom impale-like spells
Features:
- Allows creation of more than one-type of impale spell
- *Can multi-impale a unit (requires balancing with the settings of the JumpInPlace library)
- *Can easily change which units can be hit
- *Can easily change the way damage is dealt
- *Can set actions for when the impale wave ends and for when the unit falls
back to it's initial height (dependent on JumpInPlace library)
*Can be set per impale-type spell
How to Import:
1)If you don't have the dummy.mdx and a dummy unit yet,
export it and the Dummy unit and import into your map
2)Copy the whole Spell folder into your map
3)Enable the Objects Trigger
4)Save your map
5)Close map
6)Reopen map
7)Disable/Delete the objects trigger
8)Save your map
9)Edit the configurables below and read the How to use
10)Enjoy!
Stub methods:
//onStop action, can be overwritten by structs that extend this struct
stub method Stop takes nothing returns nothing
//place your actions here
//It is important that you don't remove these three function calls
call this.unitTable.destroy()
call this.stopPeriodic()
call this.destroy()
endmethod
//onDamage action, can be overwritten by structs that extend this struct
stub method onDamage takes unit target returns nothing
-placed it here so that they can change the way the damage and "flying" are done
We added the stun duration and the air-time duration for the real duration of the stun
since the given stun duration is counted after the unit lands but we will stun
it right after it is hit by impale-
call Stun.apply(target, this.duration + this.stduration, false)
call UnitDamageTarget(this.caster, target, GetRandomReal(this.lowdamage, this.highdamage), false, false, this.at, this.dt, null)
call JumpIP.Fire(target, this.caster, this.duration, this.maxheight, this.abil)
endmethod
//determines which units can be hit by impale, can be overwritten by structs that extend this struct
stub method hitFilter takes unit target returns boolean
return IsUnitEnemy(target, this.owner) and GetWidgetLife(target) > .405 and this.unitTable[GetHandleId(target)] == 0
endmethod
How to Use:
For normal usage (if you're not gonna replace any of the stub methods)
-> Impale.CreateFire()
-> Impale.CreateFireCID()
-> Impale.CreateFireEX()
call Impale.CreateFire(takes unit caster, integer level, string model, string modelhit,
real angle, real scale, real aoe, real distance, real distanceperwave,
real damage returns nothing)
unit Caster -> the unit that casted the impale
integer level -> level of the spell
string model -> path to the model of the impale wave
string modelhit -> path to the model of the hit effect
real angle -> angle to which the impale is going
real scale -> scale of each impale wave
real aoe -> filter aoe for each impale wave
real distance -> max range of the impale
real distanceperwave -> distance between each impale wave
real damage -> damage of the impale
call Impale.CreateFireCID(takes unit caster, integer level, string model, string modelhit,
real angle, real scale, real aoe, real distance, real distanceperwave,
real damage, intger abil returns nothing)
integer abil -> rawcode of the spell
call Impale.CreateFireEX(takes unit caster, integer level, string model, string modelhit, real x,real y,
real angle, real wave, real scale, real aoe, real distance, real distanceperwave,
real damagelow, real damagehigh, real stduration, real duration,
real maxheight, integer abil, attacktype at, damagetype dt, integer jumptype)
real x -> starting x coordinate of the impale
real y -> starting y coordinate of the impale
real wave -> time interval between each impale wave
real damagelow -> lower bound damage of the impale
real damagehigh -> higher bound damage of the impale
real stduration -> duration of stun (counted from after the unit lands)
real duration -> duration of the air-time
real maxheight -> max height of the air-time
integer abil -> rawcode of the spell
attacktype at -> attack type of the spell
damagetype dt -> damage type of the spell
integer jumptype -> jump type, refer to JIP library
For more info, look at the sample spells
If you're gonna replace the stub methods
Method list are same as above, but without the Create word
-> StructVariableName.Fire()
-> StructVariableName.FireCID()
-> StructVariableName.FireEX()
First you need to create a struct which extends the Impale struct
ex. struct A extends Impale
endstruct
then you create the replacement method for the stub method that you want to replace
without the stub word anymore
ex. struct A extends Impale
method onDamage takes unit target returns nothing
endmethod
endstruct
Note: make sure that they still have the same parameter list as the original method
else, it will cause an error
then you create a local variable of type Impale and set it to an instance of your
new struct, then use that local variable to call the function you need
ex. local Impale ex = A.create()
call ex.Fire()
Note: It is important that the type of the local variable should be Impale
You can view the Death Siphon trigger for a working example
How to set action for when the unit returns to it's original height:
call JumpIP.RegisterStopEvent(integer abil, code action)
integer abil => the rawcode of the spell in which you want to add the action
code action => the function which will be run
Variables you can use for the StopEvent
JumpIP.tmpFlyUnit => unit that triggered the StopEvent (the jumping unit)
JumpIP.tmpCauseUnit => the unit that caused the FlyUnit to jump
See the Death Siphon trigger for a working example
*/
library ImpaleSystem requires T32, Table, JumpInPlace, StunSystem, MapBounds
globals
//rawcode of dummy unit
private constant integer DUMMY_ID = 'e000'
/*
The next set of data is for impales created using the Fire and FireCID methods
*/
//sets the time interval for each impale wave
private constant real DEF_WAVE = .09
//sets the air-time duration
private constant real DEF_DURATION = 1.00
//sets the stun duration (counted from when the unit lands)
private constant real DEF_STDURATION = 1.00
//max height of the air-time
private constant real DEF_HEIGHT = 300.0
//default jump type
//see the JIP library for definition
private constant integer JUMP = JUMP_TYPE_HEIGHT_STACK
//used for registering stop event for the normal impales
//you can set it to the rawcode of the dummy stun spell
//since it won't probably be used to register an impale anyway
//or you can also use the rawcode of any passive/aura spell
private constant integer DUMMY_SPELL = 'A002'
//set this to false if you won't use the onEnd event for the default impales
private constant boolean USE_ON_END = false
//attack type of the impale
private constant attacktype DEF_AT = ATTACK_TYPE_MAGIC
//damage type of the impale
private constant damagetype DEF_DT = DAMAGE_TYPE_NORMAL
//required, DO NOT EDIT
private group tmpGroup = CreateGroup()
endglobals
//End of Configuration
struct Impale
unit caster
integer level
integer jumptype
string model
real wave
real waved
real scale
real aoe
real distance
real distx
real disty
real lowdamage
real highdamage
real duration
real stduration
real maxheight
real angle
real x
real y
real current
real total
player owner
integer abil
attacktype at
damagetype dt
string modelhit
Table unitTable
static unit tmpUnit
/*
registered as the stop action for all normal/simple impales
this is run after the air-time ends
make sure to set USE_ON_END to true if you're gonna put actions
into this method
*/
static method onEnd takes nothing returns nothing
endmethod
//onStop action, can be overwritten by structs that extend this struct
stub method stop takes nothing returns nothing
//It is important that you don't remove these three function calls
call this.unitTable.destroy()
call this.stopPeriodic()
call this.destroy()
endmethod
//onDamage action, can be overwritten by structs that extend this struct
stub method onDamage takes unit target returns nothing
/*
placed it here so that they can change the way the damage and "flying" are done
We added the stun duration and the air-time duration for the real duration of the stun
since the given stun duration is counted after the unit lands but we will stun
it right after it is hit by impale
*/
call Stun.apply(target, this.duration + this.stduration, false)
call UnitDamageTarget(this.caster, target, GetRandomReal(this.lowdamage, this.highdamage), false, false, this.at, this.dt, null)
call JumpInPlace.fire(target, this.caster, this.duration, this.maxheight, this.abil,this.jumptype)
endmethod
//determines which units can be hit by impale, can be overwritten by structs that extend this struct
stub method hitFilter takes unit target returns boolean
return IsUnitEnemy(target, this.owner) and GetWidgetLife(target) > .405 and this.unitTable[GetHandleId(target)] == 0
endmethod
method periodic takes nothing returns nothing
set this.current = this.current + T32_PERIOD
if this.current >= this.wave then
set this.wave = this.wave + this.waved
set this.x = this.x + this.distx
set this.y = this.y + this.disty
if not (mapIncludeX(this.x) and mapIncludeY(this.y))then
call this.stop()
return
endif
call GroupEnumUnitsInRange(tmpGroup, this.x,this.y,this.aoe, null)
set tmpUnit = CreateUnit(Player(15), DUMMY_ID, this.x, this.y, 0.0)
call SetUnitScale(tmpUnit, this.scale, 0, 0)
call UnitApplyTimedLife(tmpUnit, 'BTLF', 1.00)
call DestroyEffect(AddSpecialEffectTarget(this.model, tmpUnit, "origin"))
loop
set tmpUnit = FirstOfGroup(tmpGroup)
exitwhen tmpUnit == null
if this.hitFilter(tmpUnit) then
set this.unitTable[GetHandleId(tmpUnit)] = 1
//made the hit effect on the ground because attaching it to the unit
//causes visual bugs if the unit is hit by multiple impales
//and the effect stays for a while after the destroy is called (both effects will show)
call DestroyEffect(AddSpecialEffect(this.modelhit, GetUnitX(tmpUnit), GetUnitY(tmpUnit)))
call this.onDamage(tmpUnit)
endif
call GroupRemoveUnit(tmpGroup, tmpUnit)
endloop
if this.current >= this.total then
call this.stop()
endif
else
endif
endmethod
implement T32x
//Methods to be used if you're gonna replace any of the stub methods
method fireEX takes unit caster, integer level, string model, string modelhit, real x,real y, /*
*/real angle, real wave, real scale, real aoe, real distance, real distanceperwave,/*
*/real damagelow, real damagehigh, real stduration, real duration, /*
*/real maxheight, integer abil, attacktype at, damagetype dt,integer jumptype returns nothing
set this.caster = caster
set this.level = level
set this.model = model
set this.wave = wave
set this.angle = angle
set this.scale = scale
set this.aoe = aoe
set this.distance = distance
set this.distx = distanceperwave*Cos(this.angle)
set this.disty = distanceperwave*Sin(this.angle)
set this.lowdamage = damagelow
set this.highdamage = damagehigh
set this.stduration = stduration
set this.duration = duration
set this.maxheight = maxheight
set this.x = x
set this.y = y
set this.current = 0.0
set this.waved = this.wave
set this.total = (distance/distanceperwave)*this.wave
set this.unitTable = Table.create()
set this.at = at
set this.dt = dt
set this.owner = GetOwningPlayer(caster)
set this.modelhit = modelhit
set this.abil = abil
set this.jumptype = jumptype
call this.startPeriodic()
endmethod
method fire takes unit caster, integer level, string model, string modelhit,/*
*/real angle, real scale, real aoe, real distance, real distanceperwave,/*
*/real damage returns nothing
set this.caster = caster
set this.level = level
set this.model = model
set this.scale = scale
set this.angle = angle
set this.aoe = aoe
set this.distance = distance
set this.distx = distanceperwave*Cos(this.angle)
set this.disty = distanceperwave*Sin(this.angle)
set this.lowdamage = damage
set this.highdamage = damage
set this.duration = DEF_DURATION
set this.stduration = DEF_STDURATION
set this.maxheight = DEF_HEIGHT
set this.wave = DEF_WAVE
set this.x = GetUnitX(caster)
set this.y = GetUnitY(caster)
set this.current = 0.0
set this.waved = this.wave
set this.total = (distance/distanceperwave)*this.wave
set this.unitTable = Table.create()
set this.at = DEF_AT
set this.dt = DEF_DT
set this.owner = GetOwningPlayer(caster)
set this.modelhit = modelhit
set this.abil = DUMMY_SPELL
set this.jumptype = JUMP
call this.startPeriodic()
endmethod
method fireCID takes unit caster, integer level, string model, string modelhit,/*
*/real angle, real scale, real aoe, real distance, real distanceperwave,/*
*/real damage, integer abil returns nothing
set this.caster = caster
set this.level = level
set this.model = model
set this.scale = scale
set this.angle = angle
set this.aoe = aoe
set this.distance = distance
set this.distx = distanceperwave*Cos(this.angle)
set this.disty = distanceperwave*Sin(this.angle)
set this.lowdamage = damage
set this.highdamage = damage
set this.duration = DEF_DURATION
set this.stduration = DEF_STDURATION
set this.maxheight = DEF_HEIGHT
set this.wave = DEF_WAVE
set this.x = GetUnitX(caster)
set this.y = GetUnitY(caster)
set this.current = 0.0
set this.waved = this.wave
set this.total = (distance/distanceperwave)*this.wave
set this.unitTable = Table.create()
set this.at = DEF_AT
set this.dt = DEF_DT
set this.owner = GetOwningPlayer(caster)
set this.modelhit = modelhit
set this.abil = abil
set this.jumptype = JUMP
call this.startPeriodic()
endmethod
//Methods to be used if you're not gonna replace any of the stub methods
static method createFireEX takes unit caster, integer level, string model, string modelhit, real x,real y, /*
*/real angle, real wave, real scale, real aoe, real distance, real distanceperwave,/*
*/real damagelow, real damagehigh, real stduration, real duration, /*
*/real maxheight, integer abil, attacktype at, damagetype dt,integer jumptype returns nothing
local thistype this = .allocate()
set this.caster = caster
set this.level = level
set this.model = model
set this.wave = wave
set this.angle = angle
set this.scale = scale
set this.aoe = aoe
set this.distance = distance
set this.distx = distanceperwave*Cos(this.angle)
set this.disty = distanceperwave*Sin(this.angle)
set this.lowdamage = damagelow
set this.highdamage = damagehigh
set this.stduration = stduration
set this.duration = duration
set this.maxheight = maxheight
set this.x = x
set this.y = y
set this.current = 0.0
set this.waved = this.wave
set this.total = (distance/distanceperwave)*this.wave
set this.unitTable = Table.create()
set this.at = at
set this.dt = dt
set this.owner = GetOwningPlayer(caster)
set this.modelhit = modelhit
set this.abil = abil
set this.jumptype = jumptype
call this.startPeriodic()
endmethod
static method createFire takes unit caster, integer level, string model, string modelhit,/*
*/real angle, real scale, real aoe, real distance, real distanceperwave,/*
*/real damage returns nothing
local thistype this = .allocate()
set this.caster = caster
set this.level = level
set this.model = model
set this.scale = scale
set this.angle = angle
set this.aoe = aoe
set this.distance = distance
set this.distx = distanceperwave*Cos(this.angle)
set this.disty = distanceperwave*Sin(this.angle)
set this.lowdamage = damage
set this.highdamage = damage
set this.duration = DEF_DURATION
set this.stduration = DEF_STDURATION
set this.maxheight = DEF_HEIGHT
set this.wave = DEF_WAVE
set this.x = GetUnitX(caster)
set this.y = GetUnitY(caster)
set this.current = 0.0
set this.waved = this.wave
set this.total = (distance/distanceperwave)*this.wave
set this.unitTable = Table.create()
set this.at = DEF_AT
set this.dt = DEF_DT
set this.owner = GetOwningPlayer(caster)
set this.modelhit = modelhit
set this.abil = DUMMY_SPELL
call this.startPeriodic()
endmethod
static method createFireCID takes unit caster, integer level, string model, string modelhit,/*
*/real angle, real scale, real aoe, real distance, real distanceperwave,/*
*/real damage, integer abil returns nothing
local thistype this = .allocate()
set this.caster = caster
set this.level = level
set this.model = model
set this.scale = scale
set this.angle = angle
set this.aoe = aoe
set this.distance = distance
set this.distx = distanceperwave*Cos(this.angle)
set this.disty = distanceperwave*Sin(this.angle)
set this.lowdamage = damage
set this.highdamage = damage
set this.duration = DEF_DURATION
set this.stduration = DEF_STDURATION
set this.maxheight = DEF_HEIGHT
set this.wave = DEF_WAVE
set this.x = GetUnitX(caster)
set this.y = GetUnitY(caster)
set this.current = 0.0
set this.waved = this.wave
set this.total = (distance/distanceperwave)*this.wave
set this.unitTable = Table.create()
set this.at = DEF_AT
set this.dt = DEF_DT
set this.owner = GetOwningPlayer(caster)
set this.modelhit = modelhit
set this.abil = abil
set this.jumptype = JUMP
call this.startPeriodic()
endmethod
//I didn't use a module initializer because it returns an error
//on the impale samples
static method onInit takes nothing returns nothing
static if USE_ON_END then
call JumpInPlace.RegisterStopEvent(DUMMY_SPELL, function thistype.onEnd)
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=20
//TESH.alwaysfold=0
scope NormalImpale initializer init //Requires ImpaleSystem, SpellEvent
globals
private constant integer IMPALE_ID = 'A03X'
private constant string IMPALE_MODEL = "none.mdl"
private constant string IMPALE_HIT_MODEL = "Dust.mdx"
private constant real WAVE_DISTANCE = 50.0
private constant real WAVE_AOE = 100.0
endglobals
private function GetDamage takes unit u, integer level returns real
return 0.00*level
endfunction
private function GetDistance takes unit u, integer level returns real
return 50.00 + 0.00*level
endfunction
private function action takes nothing returns nothing
local unit caster = GetTriggerUnit()
local real x = GetUnitX(caster)
local real y = GetUnitY(caster)
local real tx = GetSpellTargetX()
local real ty = GetSpellTargetY()
local real angle = Atan2(ty-y,tx-x)
local integer level = GetUnitAbilityLevel(caster, IMPALE_ID)
//Since were not gonna replace any stub method, we use the CreateFire method
call Impale.createFire(caster, level, IMPALE_MODEL,IMPALE_HIT_MODEL,/*
*/angle, 1.00, WAVE_AOE, GetDistance(caster,level), WAVE_DISTANCE,/*
*/GetDamage(caster,level))
set caster = null
endfunction
private function init takes nothing returns nothing
call RegisterSpellEffectEvent(IMPALE_ID,function action)
endfunction
endscope
// Arcing Text Tag v1.0.0.3 by Maker
library FloatingTextArc
globals
private constant real SIZE_MIN = 0.014 // Minimum size of text
private constant real SIZE_BONUS = 0.010 // Text size increase
private constant real TIME_LIFE = 0.8 // How long the text lasts
private constant real TIME_FADE = 0.7 // When does the text start to fade
private constant real Z_OFFSET = 50 // Height above unit
private constant real Z_OFFSET_BON = 50 // How much extra height the text gains
private constant real VELOCITY = 2 // How fast the text move in x/y plane
private constant real ANGLE = bj_PI/2 // Movement angle of the text. Does not apply if
// ANGLE_RND is true
private constant boolean ANGLE_RND = true // Is the angle random or fixed
private timer TMR = CreateTimer()
endglobals
struct ArcingTextTag extends array
private texttag tt
private real as // angle, sin component
private real ac // angle, cos component
private real ah // arc height
private real t // time
private real x // origin x
private real y // origin y
private string s // text
private static integer array next
private static integer array prev
private static integer array rn
private static integer ic = 0 // Instance count
private static method update takes nothing returns nothing
local thistype this=next[0]
local real p
loop
set p = Sin(bj_PI*.t)
set .t = .t - 0.03125
set .x = .x + .ac
set .y = .y + .as
call SetTextTagPos(.tt, .x, .y, Z_OFFSET + Z_OFFSET_BON * p)
call SetTextTagText(.tt, .s, SIZE_MIN + SIZE_BONUS * p)
if .t <= 0 then
set .tt = null
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
set rn[this] = rn[0]
set rn[0] = this
if next[0]==0 then
call PauseTimer(TMR)
endif
endif
set this = next[this]
exitwhen this == 0
endloop
endmethod
public static method create takes string s, unit u returns thistype
local thistype this = rn[0]
static if ANGLE_RND then
local real a = GetRandomReal(0, 2*bj_PI)
else
local real a = ANGLE
endif
if this == 0 then
set ic = ic + 1
set this = ic
else
set rn[0] = rn[this]
endif
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
set .s = s
set .x = GetUnitX(u)
set .y = GetUnitY(u)
set .t = TIME_LIFE
set .as = Sin(a)*VELOCITY
set .ac = Cos(a)*VELOCITY
set .ah = 0.
if IsUnitVisible(u, GetLocalPlayer()) then
set .tt = CreateTextTag()
call SetTextTagPermanent(.tt, false)
call SetTextTagLifespan(.tt, TIME_LIFE)
call SetTextTagFadepoint(.tt, TIME_FADE)
call SetTextTagText(.tt, s, SIZE_MIN)
call SetTextTagPos(.tt, .x, .y, Z_OFFSET)
endif
if prev[this] == 0 then
call TimerStart(TMR, 0.03125, true, function thistype.update)
endif
return this
endmethod
endstruct
endlibrary
//===========================================================================
//
// Damage Engine 5.6.0.0 - update requires replacing the JASS script and
// adding the below four variables to the Variable Editor:
//
// boolean NextDamageIsAttack
// boolean NextDamageIsMelee
// boolean NextDamageIsRanged
// integer NextDamageWeaponT
//
//===========================================================================
library DamageEngine initializer Init
globals
private constant boolean USE_GUI = true //If you don't use any of the GUI events, set to false to slightly improve performance
private constant integer CODE = 1 //Must be the same as udg_DamageTypeCode, or 0 if you prefer to disable the automatic flag.
private constant integer PURE = 2 //Must be the same as udg_DamageTypePure
private constant integer LIMBO = 16 //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
private constant real DEATH_VAL = 0.405 //In case Blizz ever changes this, it'll be a quick fix here.
private timer alarm = CreateTimer()
private boolean alarmSet = false
//Values to track the original pre-spirit Link/defensive damage values
private integer lastInstance = 0
private boolean canKick = true
private boolean totem = false
//Added in 5.4 to silently eliminate infinite recursion.
private integer userTrigs = 9
private integer eventTrig = 0
private integer array nextTrig
private trigger array userTrig
private real array trigWeight
private boolean array usingGUI
private boolean array trigFrozen
//Added/re-tooled in 5.4.1 to allow forced recursion (for advanced users only).
private integer array levelsDeep //How deep the user recursion currently is.
public boolean inception = false //You must set DamageEngine_inception = true before dealing damage to utlize this.
//When true, it allows your trigger to potentially go recursive up to LIMBO.
private boolean dreaming = false
private boolean array inceptionTrig //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
private integer sleepLevel = 0
private group proclusGlobal = CreateGroup() //track sources of recursion
private group fischerMorrow = CreateGroup() //track targets of recursion
//Improves readability in the code to have these as named constants.
private constant integer MOD_EVENT = 1
private constant integer SHIELD_EVENT = 4
private constant integer DAMAGE_EVENT = 5
private constant integer ZERO_EVENT = 6
private constant integer AFTER_EVENT = 7
private constant integer LETHAL_EVENT = 8
private constant integer AOE_EVENT = 9
//Stuff to track recursive UnitDamageTarget calls.
private Damage prepped = 0
private Damage array stackRef
private integer damageStack = 0
private boolean kicking = false
private boolean eventsRun = false
endglobals
//GUI Vars:
/*
Retained from 3.8 and prior:
----------------------------
unit udg_DamageEventSource
unit udg_DamageEventTarget
unit udg_EnhancedDamageTarget
group udg_DamageEventAOEGroup
integer udg_DamageEventAOE
integer udg_DamageEventLevel
real udg_DamageModifierEvent
real udg_DamageEvent
real udg_AfterDamageEvent
real udg_DamageEventAmount
real udg_DamageEventPrevAmt
real udg_AOEDamageEvent
boolean udg_DamageEventOverride
boolean udg_NextDamageType
boolean udg_DamageEventType
boolean udg_IsDamageSpell
//Added in 5.0:
boolean udg_IsDamageMelee
boolean udg_IsDamageRanged
unit udg_AOEDamageSource
real udg_LethalDamageEvent
real udg_LethalDamageHP
real udg_DamageScalingWC3
integer udg_DamageEventAttackT
integer udg_DamageEventDamageT
integer udg_DamageEventWeaponT
//Added in 5.1:
boolean udg_IsDamageCode
//Added in 5.2:
integer udg_DamageEventArmorT
integer udg_DamageEventDefenseT
//Addded in 5.3:
real DamageEventArmorPierced
real udg_DamageScalingUser
//Added in 5.4.2 to allow GUI users to re-issue the exact same attack and damage type at the attacker.
attacktype array udg_CONVERTED_ATTACK_TYPE
damagetype array udg_CONVERTED_DAMAGE_TYPE
//Added after Reforged introduced the new native BlzGetDamageIsAttack
boolean udg_IsDamageAttack
//Added in 5.6 to give GUI users control over the "IsDamageAttack", "IsDamageRanged" and "DamageEventWeaponT" field
boolean udg_NextDamageIsAttack //The first boolean value in the UnitDamageTarget native
boolean udg_NextDamageIsMelee //Flag the damage classification as melee
boolean udg_NextDamageIsRanged //The second boolean value in the UnitDamageTarget native
integer udg_NextDamageWeaponT //Allows control over damage sound effect
*/
//This struct has been added due to the potential need for additional security around damage instance properties.
//It will help me to keep things in this script better organized as well as make the API more vJass-friendly for
//advanced users coming from other vJass damage systems.
//Additionally, this opens up the EnhancedDamageEvent and AOEDamageEvent with very important access.
struct Damage extends array
static Damage index = 0 //the value of the currently-running damage event index, or 0 if none are running
//index 1 represents the values of the original damage instance.
//Two lists - one for processing the normal events and another for processing recursive ones.
static integer count = 0//The number of currently-running queued or sequential damage instances
unit source //stores udg_DamageEventSource
unit target //stores udg_DamageEventTarget
real amount //stores udg_DamageEventAmount
real prevAmt //stores udg_DamageEventPrevAmt
attacktype attackType //stores udg_DamageEventAttackT
damagetype damageType //stores udg_DamageEventDamageT
weapontype weaponType //stores udg_DamageEventWeaponT
integer userTrig //stores the trigger integer reference where recursive damage came from
integer userType //stores udg_DamageEventType
boolean isAttack //stores udg_IsDamageAttack
boolean isCode //stores udg_IsDamageCode
boolean isMelee //stores udg_IsDamageMelee
boolean isRanged //stores udg_IsDamageRanged
boolean isSpell //stores udg_IsDamageSpell
real armorPierced //stores udg_DamageEventArmorPierced
integer armorType //stores udg_DamageEventArmorT
integer defenseType //stores udg_DamageEventDefenseT
integer prevArmorT //internal
integer prevDefenseT //internal
endstruct
public function SetGUIFromStruct takes boolean full returns nothing
set udg_DamageEventAmount = Damage.index.amount
set udg_DamageEventAttackT = GetHandleId(Damage.index.attackType)
set udg_DamageEventDamageT = GetHandleId(Damage.index.damageType)
set udg_DamageEventWeaponT = GetHandleId(Damage.index.weaponType)
set udg_DamageEventType = Damage.index.userType
set udg_DamageEventArmorPierced = Damage.index.armorPierced
set udg_DamageEventArmorT = Damage.index.armorType
set udg_DamageEventDefenseT = Damage.index.defenseType
if full then
set udg_DamageEventSource = Damage.index.source
set udg_DamageEventTarget = Damage.index.target
set udg_DamageEventPrevAmt = Damage.index.prevAmt
set udg_IsDamageAttack = Damage.index.isAttack
set udg_IsDamageCode = Damage.index.isCode
set udg_IsDamageMelee = Damage.index.isMelee
set udg_IsDamageRanged = Damage.index.isRanged
set udg_IsDamageSpell = Damage.index.isSpell
endif
endfunction
public function SetStructFromGUI takes nothing returns nothing
set Damage.index.amount = udg_DamageEventAmount
set Damage.index.attackType = ConvertAttackType(udg_DamageEventAttackT)
set Damage.index.damageType = ConvertDamageType(udg_DamageEventDamageT)
set Damage.index.weaponType = ConvertWeaponType(udg_DamageEventWeaponT)
set Damage.index.userType = udg_DamageEventType
set Damage.index.armorPierced = udg_DamageEventArmorPierced
set Damage.index.armorType = udg_DamageEventArmorT
set Damage.index.defenseType = udg_DamageEventDefenseT
endfunction
private function RunTrigs takes integer i returns nothing
local integer cat = i
local Damage d = Damage.index
local boolean setJASS = true
static if USE_GUI then
local boolean setGUI = true
local boolean mod = cat == MOD_EVENT or cat == SHIELD_EVENT or cat == LETHAL_EVENT
else
local boolean setGUI = cat != MOD_EVENT and cat != SHIELD_EVENT and cat != LETHAL_EVENT
endif
if dreaming then
return
endif
set dreaming = true
//call BJDebugMsg("Start of event running")
loop
set i = nextTrig[i]
exitwhen i == 0
exitwhen cat == MOD_EVENT and (udg_DamageEventOverride or udg_DamageEventType == PURE)
exitwhen cat == SHIELD_EVENT and udg_DamageEventAmount <= 0.00
exitwhen cat == LETHAL_EVENT and udg_LethalDamageHP > DEATH_VAL
if not trigFrozen[i] and IsTriggerEnabled(userTrig[i]) then
set eventTrig = d
static if USE_GUI then
if mod then
if usingGUI[i] then
set setJASS = false
if not setGUI then
set setGUI = true
if cat == MOD_EVENT then
call SetGUIFromStruct(false)
else
set udg_DamageEventAmount = d.amount
endif
endif
else
set setGUI = false
if not setJASS then
set setJASS = true
if cat == MOD_EVENT then
call SetStructFromGUI()
else
set d.amount = udg_DamageEventAmount
endif
endif
endif
endif
endif
if TriggerEvaluate(userTrig[i]) then
call TriggerExecute(userTrig[i])
endif
endif
endloop
//call BJDebugMsg("End of event running")
if not setGUI then
if cat == MOD_EVENT then
call SetGUIFromStruct(false)
else
set udg_DamageEventAmount = d.amount
endif
elseif not setJASS then
if cat == MOD_EVENT then
call SetStructFromGUI()
else
set d.amount = udg_DamageEventAmount
endif
endif
set dreaming = false
endfunction
private function OnAOEEnd takes nothing returns nothing
if udg_DamageEventAOE > 1 then
call RunTrigs(AOE_EVENT)
set udg_DamageEventAOE = 1
endif
set udg_DamageEventLevel = 1
set udg_EnhancedDamageTarget = null
set udg_AOEDamageSource = null
call GroupClear(udg_DamageEventAOEGroup)
endfunction
private function AfterDamage takes nothing returns nothing
if udg_DamageEventPrevAmt != 0.00 and udg_DamageEventDamageT != udg_DAMAGE_TYPE_UNKNOWN then
call RunTrigs(AFTER_EVENT)
endif
endfunction
private function Finish takes nothing returns nothing
local integer i = 0
local integer exit
if eventsRun then
set eventsRun = false
call AfterDamage()
endif
if canKick and not kicking then
if damageStack > 0 then
set kicking = true
loop
set exit = damageStack
set sleepLevel = sleepLevel + 1
loop
set prepped = stackRef[i]
call UnitDamageTarget(prepped.source, prepped.target, prepped.amount, prepped.isAttack, prepped.isRanged, prepped.attackType, prepped.damageType, prepped.weaponType)
set prepped = 0
call AfterDamage()
set i = i + 1 //Need to loop bottom to top to make sure damage order is preserved.
exitwhen i == exit
endloop
exitwhen i == damageStack
endloop
set sleepLevel = 0
loop
set i = i - 1
set trigFrozen[stackRef[i].userTrig] = false //Only re-enable recursive triggers AFTER all damage is dealt.
set levelsDeep[stackRef[i].userTrig] = 0 //Reset this stuff if the user tried some nonsense
exitwhen i == 0
endloop
set damageStack = 0 //Can only be set after all the damage has successfully ended.
set kicking = false
endif
call GroupClear(proclusGlobal)
call GroupClear(fischerMorrow)
endif
endfunction
private function SetArmor takes boolean reset returns nothing
local real pierce
local integer at
local integer dt
if reset then
set pierce = Damage.index.armorPierced
set at = Damage.index.prevArmorT
set dt = Damage.index.prevDefenseT
else
set pierce = -Damage.index.armorPierced
set at = Damage.index.armorType
set dt = Damage.index.defenseType
endif
if pierce != 0.00 then
call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
endif
if Damage.index.prevArmorT != udg_DamageEventArmorT then
call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, at) //revert changes made to the damage instance
endif
if Damage.index.prevDefenseT != udg_DamageEventDefenseT then
call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, dt)
endif
endfunction
private function FailsafeClear takes nothing returns nothing
//call BJDebugMsg("Damage from " + GetUnitName(udg_DamageEventSource) + " to " + GetUnitName(udg_DamageEventTarget) + " has been messing up Damage Engine.")
//call BJDebugMsg(R2S(udg_DamageEventAmount) + " " + " " + R2S(udg_DamageEventPrevAmt) + " " + udg_AttackTypeDebugStr[udg_DamageEventAttackT] + " " + udg_DamageTypeDebugStr[udg_DamageEventDamageT])
call SetArmor(true)
set canKick = true
set totem = false
if udg_DamageEventDamageT != udg_DAMAGE_TYPE_UNKNOWN then
call RunTrigs(DAMAGE_EVENT) //Run the normal on-damage event based on this failure.
set eventsRun = true //Run the normal after-damage event based on this failure.
endif
call Finish()
endfunction
private function WakeUp takes nothing returns nothing
set alarmSet = false //The timer has expired. Flag off to allow it to be restarted when needed.
if totem then
call FailsafeClear() //WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
else
if not canKick and damageStack > 0 then
set canKick = true
endif
call Finish() //Wrap up any outstanding damage instance
endif
call OnAOEEnd() //Reset things so they don't perpetuate for AoE/Level target detection
set Damage.count = 0 //Set the stack back to 0
set Damage.index = 0
endfunction
private function OnDamaging takes nothing returns boolean
local Damage d = prepped
if d > 0 then
set prepped = 0 //damage has been queued by the engine already.
else
set Damage.count = Damage.count + 1
set d = Damage.count
set d.source = GetEventDamageSource()
set d.target = GetTriggerUnit()
set d.amount = GetEventDamage()
set d.prevAmt = d.amount
set d.attackType = BlzGetEventAttackType()
set d.damageType = BlzGetEventDamageType()
set d.weaponType = BlzGetEventWeaponType()
set d.userType = udg_NextDamageType
set d.isCode = udg_NextDamageType != 0 or udg_NextDamageIsAttack or udg_NextDamageIsRanged or udg_NextDamageIsMelee
set d.isAttack = udg_NextDamageIsAttack or BlzGetEventIsAttack()
set d.isSpell = d.attackType == null and not d.isAttack
if d.isCode then
set d.isMelee = udg_NextDamageIsMelee
set d.isRanged = udg_NextDamageIsRanged
elseif d.damageType == DAMAGE_TYPE_NORMAL and not d.isSpell then //This damage type is the only one that can get reduced by armor.
set d.isMelee = IsUnitType(udg_DamageEventSource, UNIT_TYPE_MELEE_ATTACKER)
set d.isRanged = IsUnitType(udg_DamageEventSource, UNIT_TYPE_RANGED_ATTACKER)
if d.isMelee and d.isRanged then
set d.isMelee = d.weaponType != null // Melee units play a sound when damaging
set d.isRanged = not d.isMelee // In the case where a unit is both ranged and melee, the ranged attack plays no sound.
endif // The Huntress has a melee sound for her ranged projectile, however it is only an issue
endif //if she also had a melee attack, because by default she is only UNIT_TYPE_RANGED_ATTACKER.
set udg_NextDamageIsAttack = false
set udg_NextDamageIsMelee = false
set udg_NextDamageIsRanged = false
set udg_NextDamageType = 0
endif
if dreaming then
if d.amount != 0.00 then
set stackRef[damageStack] = d
set d.userTrig = eventTrig
if udg_NextDamageType == 0 then
set d.userType = CODE
else
set d.userType = udg_NextDamageType
endif
set inception = inception or inceptionTrig[eventTrig]
call GroupAddUnit(proclusGlobal, d.source)
call GroupAddUnit(fischerMorrow, d.target)
if kicking and IsUnitInGroup(d.source, proclusGlobal) and IsUnitInGroup(d.target, fischerMorrow) then
if inception and not trigFrozen[eventTrig] then
set inceptionTrig[eventTrig] = true
if levelsDeep[eventTrig] < sleepLevel then
set levelsDeep[eventTrig] = levelsDeep[eventTrig] + 1
if levelsDeep[eventTrig] >= LIMBO then
set trigFrozen[eventTrig] = true
endif
endif
else
set trigFrozen[eventTrig] = true
endif
endif
set damageStack = damageStack + 1
//call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(levelsDeep[eventTrig]) + " sleepLevel: " + I2S(sleepLevel))
call BlzSetEventDamage(0.00) //queue the damage instance instead of letting it run recursively
endif
else
if not kicking then
if alarmSet then
if totem then
if d.damageType != DAMAGE_TYPE_SPIRIT_LINK and d.damageType != DAMAGE_TYPE_DEFENSIVE and d.damageType != DAMAGE_TYPE_PLANT then
//if 'totem' is still set and it's not due to spirit link distribution or defense retaliation,
//the next function must be called as a debug. This reverts an issue I created in patch 5.1.3.
call FailsafeClear()
else
set totem = false
set lastInstance = Damage.index
set canKick = false
endif
else
call Finish()
endif
if d.source != udg_AOEDamageSource then //Source has damaged more than once
call OnAOEEnd() //New damage source - unflag everything
set udg_AOEDamageSource = d.source
elseif d.target == udg_EnhancedDamageTarget then
set udg_DamageEventLevel= udg_DamageEventLevel + 1 //The number of times the same unit was hit.
elseif not IsUnitInGroup(d.target, udg_DamageEventAOEGroup) then
set udg_DamageEventAOE = udg_DamageEventAOE + 1 //Multiple targets hit by this source - flag as AOE
endif
else
call TimerStart(alarm, 0.00, false, function WakeUp)
set alarmSet = true
set udg_AOEDamageSource = d.source
set udg_EnhancedDamageTarget= d.target
endif
call GroupAddUnit(udg_DamageEventAOEGroup, d.target)
endif
set d.armorType = BlzGetUnitIntegerField(d.target, UNIT_IF_ARMOR_TYPE) //Introduced in Damage Engine 5.2.0.0
set d.defenseType = BlzGetUnitIntegerField(d.target, UNIT_IF_DEFENSE_TYPE)
set d.armorPierced = 0.00
//Set the GUI stuff
set Damage.index = d
call SetGUIFromStruct(true)
set d.prevArmorT = udg_DamageEventArmorT
set d.prevDefenseT = udg_DamageEventDefenseT
if udg_DamageEventAmount != 0.00 then
set udg_DamageEventOverride = udg_DamageEventDamageT == null
if not udg_DamageEventOverride then
call RunTrigs(MOD_EVENT)
//All events have run and the pre-damage amount is finalized.
call BlzSetEventAttackType(d.attackType)
call BlzSetEventDamageType(d.damageType)
call BlzSetEventWeaponType(d.weaponType)
call BlzSetEventDamage(udg_DamageEventAmount)
call SetArmor(false)
endif
set totem = true
else
call RunTrigs(ZERO_EVENT)
set canKick = true
call Finish()
endif
endif
set inception = false
return false
endfunction
//The traditional on-damage response, where armor reduction has already been factored in.
private function OnDamaged takes nothing returns boolean
local real r = GetEventDamage()
local Damage d = Damage.index
//call BJDebugMsg("Second damage event running")
if dreaming or d.prevAmt == 0.00 then
return false
endif
if totem then
set totem = false //This should be the case in almost all circumstances
else
//Spirit Link and Thorns Aura/Spiked Carapace fire the DAMAGED event out of sequence with the DAMAGING event
call AfterDamage()
set canKick = true
set Damage.index = lastInstance
call SetGUIFromStruct(true)
endif
call SetArmor(true)
if udg_DamageEventAmount != 0.00 and r != 0.00 then
set udg_DamageScalingWC3 = r / udg_DamageEventAmount
elseif udg_DamageEventAmount > 0.00 then
set udg_DamageScalingWC3 = 0.00
else
set udg_DamageScalingWC3 = 1.00
set udg_DamageScalingUser = udg_DamageEventAmount / udg_DamageEventPrevAmt
endif
set udg_DamageEventAmount = udg_DamageEventAmount*udg_DamageScalingWC3
set d.amount = udg_DamageEventAmount
if udg_DamageEventAmount > 0.00 then
//This event is used for custom shields which have a limited hit point value
//The shield here kicks in after armor, so it acts like extra hit points.
call RunTrigs(SHIELD_EVENT)
set udg_LethalDamageHP = GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
if udg_LethalDamageHP <= DEATH_VAL then
call RunTrigs(LETHAL_EVENT) //Added 10 May 2019 to detect and potentially prevent lethal damage. Instead of
//modifying the damage, you need to modify LethalDamageHP instead (the final HP of the unit).
set udg_DamageEventAmount = GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
set d.amount = udg_DamageEventAmount
if udg_DamageEventType < 0 and udg_LethalDamageHP <= DEATH_VAL then
call SetUnitExploded(udg_DamageEventTarget, true) //Explosive damage types should blow up the target.
endif
endif
set udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt/udg_DamageScalingWC3
endif
call BlzSetEventDamage(udg_DamageEventAmount) //Apply the final damage amount.
if udg_DamageEventDamageT != udg_DAMAGE_TYPE_UNKNOWN then
call RunTrigs(DAMAGE_EVENT)
endif
set eventsRun = true
if udg_DamageEventAmount == 0.00 then
call Finish()
endif
return false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DAMAGED) //Thanks to this I no longer have to create an event for every unit in the map.
call TriggerAddCondition(trig, Filter(function OnDamaged))
set trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DAMAGING) //The new 1.31 event which fires before damage.
call TriggerAddCondition(trig, Filter(function OnDamaging))
set trig = null
endfunction
public function DebugStr takes nothing returns nothing
local integer i = 0
loop
set udg_CONVERTED_ATTACK_TYPE[i] = ConvertAttackType(i)
exitwhen i == 6
set i = i + 1
endloop
set i = 0
loop
set udg_CONVERTED_DAMAGE_TYPE[i] = ConvertDamageType(i)
exitwhen i == 26
set i = i + 1
endloop
set udg_AttackTypeDebugStr[0] = "SPELLS" //ATTACK_TYPE_NORMAL in JASS
set udg_AttackTypeDebugStr[1] = "NORMAL" //ATTACK_TYPE_MELEE in JASS
set udg_AttackTypeDebugStr[2] = "PIERCE"
set udg_AttackTypeDebugStr[3] = "SIEGE"
set udg_AttackTypeDebugStr[4] = "MAGIC"
set udg_AttackTypeDebugStr[5] = "CHAOS"
set udg_AttackTypeDebugStr[6] = "HERO"
set udg_DamageTypeDebugStr[0] = "UNKNOWN"
set udg_DamageTypeDebugStr[4] = "NORMAL"
set udg_DamageTypeDebugStr[5] = "ENHANCED"
set udg_DamageTypeDebugStr[8] = "FIRE"
set udg_DamageTypeDebugStr[9] = "COLD"
set udg_DamageTypeDebugStr[10] = "LIGHTNING"
set udg_DamageTypeDebugStr[11] = "POISON"
set udg_DamageTypeDebugStr[12] = "DISEASE"
set udg_DamageTypeDebugStr[13] = "DIVINE"
set udg_DamageTypeDebugStr[14] = "MAGIC"
set udg_DamageTypeDebugStr[15] = "SONIC"
set udg_DamageTypeDebugStr[16] = "ACID"
set udg_DamageTypeDebugStr[17] = "FORCE"
set udg_DamageTypeDebugStr[18] = "DEATH"
set udg_DamageTypeDebugStr[19] = "MIND"
set udg_DamageTypeDebugStr[20] = "PLANT"
set udg_DamageTypeDebugStr[21] = "DEFENSIVE"
set udg_DamageTypeDebugStr[22] = "DEMOLITION"
set udg_DamageTypeDebugStr[23] = "SLOW_POISON"
set udg_DamageTypeDebugStr[24] = "SPIRIT_LINK"
set udg_DamageTypeDebugStr[25] = "SHADOW_STRIKE"
set udg_DamageTypeDebugStr[26] = "UNIVERSAL"
set udg_WeaponTypeDebugStr[0] = "NONE" //WEAPON_TYPE_WHOKNOWS in JASS
set udg_WeaponTypeDebugStr[1] = "METAL_LIGHT_CHOP"
set udg_WeaponTypeDebugStr[2] = "METAL_MEDIUM_CHOP"
set udg_WeaponTypeDebugStr[3] = "METAL_HEAVY_CHOP"
set udg_WeaponTypeDebugStr[4] = "METAL_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[5] = "METAL_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[6] = "METAL_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[7] = "METAL_MEDIUM_BASH"
set udg_WeaponTypeDebugStr[8] = "METAL_HEAVY_BASH"
set udg_WeaponTypeDebugStr[9] = "METAL_MEDIUM_STAB"
set udg_WeaponTypeDebugStr[10] = "METAL_HEAVY_STAB"
set udg_WeaponTypeDebugStr[11] = "WOOD_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[12] = "WOOD_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[13] = "WOOD_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[14] = "WOOD_LIGHT_BASH"
set udg_WeaponTypeDebugStr[15] = "WOOD_MEDIUM_BASH"
set udg_WeaponTypeDebugStr[16] = "WOOD_HEAVY_BASH"
set udg_WeaponTypeDebugStr[17] = "WOOD_LIGHT_STAB"
set udg_WeaponTypeDebugStr[18] = "WOOD_MEDIUM_STAB"
set udg_WeaponTypeDebugStr[19] = "CLAW_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[20] = "CLAW_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[21] = "CLAW_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[22] = "AXE_MEDIUM_CHOP"
set udg_WeaponTypeDebugStr[23] = "ROCK_HEAVY_BASH"
set udg_DefenseTypeDebugStr[0] = "LIGHT"
set udg_DefenseTypeDebugStr[1] = "MEDIUM"
set udg_DefenseTypeDebugStr[2] = "HEAVY"
set udg_DefenseTypeDebugStr[3] = "FORTIFIED"
set udg_DefenseTypeDebugStr[4] = "NORMAL" //Typically deals flat damage to all armor types
set udg_DefenseTypeDebugStr[5] = "HERO"
set udg_DefenseTypeDebugStr[6] = "DIVINE"
set udg_DefenseTypeDebugStr[7] = "UNARMORED"
set udg_ArmorTypeDebugStr[0] = "NONE" //ARMOR_TYPE_WHOKNOWS in JASS, added in 1.31
set udg_ArmorTypeDebugStr[1] = "FLESH"
set udg_ArmorTypeDebugStr[2] = "METAL"
set udg_ArmorTypeDebugStr[3] = "WOOD"
set udg_ArmorTypeDebugStr[4] = "ETHEREAL"
set udg_ArmorTypeDebugStr[5] = "STONE"
endfunction
//This function exists mainly to make it easier to switch from another DDS, like PDD.
function UnitDamageTargetEx takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns boolean
if udg_NextDamageType == 0 then
set udg_NextDamageType = CODE
endif
call UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
return dreaming
endfunction
//===========================================================================
//
// Setup of automatic events from GUI and custom ones from JASS alike
//
//===========================================================================
public function SetupEvent takes trigger whichTrig, string var, real weight, boolean GUI returns nothing
local integer index = R2I(weight)
local integer i = 0
if var == "udg_DamageModifierEvent" then
if index > 4 then
set index = SHIELD_EVENT //4.00 or higher
else
set index = MOD_EVENT //Less than 4.00
endif
elseif var == "udg_DamageEvent" then
if weight == 2.00 or weight == 0.00 then
set index = ZERO_EVENT
else
set index = DAMAGE_EVENT //Above 0.00 but less than 2.00, generally would just be 1.00
endif
elseif var == "udg_AfterDamageEvent" then
set index = AFTER_EVENT
elseif var == "udg_LethalDamageEvent" then
set index = LETHAL_EVENT
elseif var == "udg_AOEDamageEvent" then
set index = AOE_EVENT
else
return
endif
set userTrigs = userTrigs + 1 //User list runs from index 10 and up
set userTrig[userTrigs] = whichTrig
set usingGUI[userTrigs] = GUI
set trigWeight[userTrigs] = weight
loop
set i = nextTrig[index]
exitwhen i == 0 or weight < trigWeight[i]
set index = i
endloop
set nextTrig[index] = userTrigs
set nextTrig[userTrigs] = i
//call BJDebugMsg("Registered " + I2S(userTrigs) + " to " + I2S(index) + " and before " + I2S(i))
endfunction
private function PreSetup takes trigger whichTrig, string var, limitop op, real value returns nothing
call SetupEvent(whichTrig, var, value, true)
endfunction
hook TriggerRegisterVariableEvent PreSetup
function TriggerRegisterDamageEvent takes trigger whichTrig, string eventName, real value returns nothing
//API:
//"" for standard damage event
//"Modifier/After/Lethal/AOE" for the others
if eventName == "Modifier" then
set eventName = "udg_DamageModifierEvent"
else
set eventName = "udg_" + eventName + "DamageEvent"
endif
call SetupEvent(whichTrig, eventName, value, false)
endfunction
//Similar to TriggerRegisterDamageEvent, although takes code instead of trigger as the first argument.
function RegisterDamageEvent takes code c, string eventName, real value returns nothing
local trigger t = CreateTrigger()
call TriggerAddCondition(t, Filter(c))
call TriggerRegisterDamageEvent(t, eventName, value)
set t = null
endfunction
endlibrary
//TESH.scrollpos=43
//TESH.alwaysfold=0
<<==>>CUSTOM PROJECTILES SYSTEM<<==>>
<<==>>Ver 1.2c<<==>>
<<==>>Created by LeoNguyen112<<==>>
I. Features:
o Simply create a straight projectile
o Create a projectile with vertical arc (normal missile) or horizontal arc (like
DotA's Wild Axe of Rexxar)
o Create a projectile toward a unit or location
o Can activate following trigger when hit (allow you to create attaching effect)
II. Installation:
1. Import missiledummy.mdx with contained in this map to your map
2. Create a new dummy which uses the model above and have Harvest ability.
3. Copy triggers: CPS Loop, CPS Creating, CPS Init to your map.
4. In trigger CPS Init, Set CPS_Dummy = "Your missile dummy"
III. Variables' Functions:
Projectile_AttackType: attack type of damage
Projectile_Damage: impact damage (real)
Projectile_DamageType: type of impact damage
Projectile_Impact: effect will be created at the impact
Projectile_Model: model of projectile
Projectile_Height: flying height of projectile
Projectile_Size: size (%) of projectile, this value decides both Length, Width, Height.
Projectile_Speed: flying range of projectile every 0.02 second
Projectile_Source: source unit of projectile
Projectile_TargetUnit: target unit of projectile
Projectile_TargetPoint: target point of projectile
Projectile_UnitOrPoint: this value decides target type of projectile
- True: target type of projectile is unit.
- False: target type of projectile is point. When it is false, you need to set the
following variable:
- Projectile_AoE - decide projectile's area of effect.
- Projectile_DestroyTree - allow to destroy trees or not (true/false)
Projectile_Trigger: trigger will be activated at the projectile's impact
Projectile_HArc: true = projectile will move with horizontal arc, false = without arc.
Once this value is true, there are variables you need to set:
- Projectile_HArcWidth: decide arc's width, it also depends on projectile's speed
- Projectile_HArcLeftOrRight: true = left, false = right
Projectile_VArc: true = projectile will move with vertical arc, false = without arc.
Once this value is true, there is a variable you need to set:
- Projectile_VArcHeight: decide arc's height, it also depend on projectile's speed
IV. How to Use:
1. You need 1 trigger to set all (or what neccesary) variables above, then run
CPS Creating by using:
"Custom script: call TriggerExecute( gg_trg_CPS_Creating )"
2. To use a following trigger:
a) You can use directly stored Projectile System's data through those variables:
+ SPSD_Caster
+ SPSD_TargetUnit
+ SPSD_TargetPoint (it's unnecessary to remove leak this variable, because the
system will automatically do it)
b) In my onpinion, you should set those variables above into yours own and then use
it normally:
+ Set ABC_Caster = SPSD_Caster
+ Set ABC_Target = SPSD_TargetUnit
+ Set ABC_Point = SPSD_TargetPoint
(ABC is your spell's name)
3. See "Example Spells" - "Frost Arrow / Immolation Arrow Spell" if you need some
example about how to use following trigger.
V. Special Thanks to:
o redscores for the dummy model.
o Magtheridon96 for useful tips.
o GhostHunter123
o Almia
- - - - - Introduction - - - - -
This system is created by The_Flood (Flood @ hiveworkshop) and allow you to design and easily modify Rain of X spells.
You may create a Rain of Fire, Rain of Water or Rain of Lightning.
With this system you can create all kind of raining abilities!
Notice that you'll not have to channel those Rain spells. Future updates may have an option if the caster should channel or not.
- - - - - How can I create my own Rain of X spell? - - - - -
Well, copy either the Test Spell 1 or Test Spell 2 trigger and read through the actions they've got.
After that you change the condition ((Ability being cast) Equal to Test 1) to the ability you'll use for this Rain of X.
If you've read through the action you can begin your editing, change your Rain of X's AoE, damage, effects,
number of shards/waves and more... You can easily read the comments above each action if you aren't sure of what this action does.
When you're done with your editing, test your spell ingame! :D
- - - - - What things do I need to import into my map? - - - - -
Lucky, there isn't alot of things, the things you need to import are:
> Triggers (F4)
- RoX Loop
- Shard Loop
- Test Spell 1 / Test Spell 2 / Your spell trigger
> Units (F6)
- Shard_Dummy
> Imports (F12)
- DUMMY.mdx (This is an invisible unit that has attachment points, the Shard_Dummy unit uses this model)
> Abilities (F6)
- Test Spell 1 / Test Spell 2 / Your spell ability
- - - - - Warnings! - - - - -
This system may enter in high loop numbers if you're using alot of shards each wave of a spell.
This will cause heavy lagg to all players, but if you set the number of shards each wave below 15 it should not be a problem.
So... Take it easy with this system, I tried to create a Rain of X spell and try how much my computer could take with casting alot of
Rain of X spells, I had 10 waves and 20 shards on this spell I edited. I had to cast the spell about 30 times before my computer started
to lagg, but the lagg will dissapear when all the wave ends. (:
//TESH.scrollpos=0
//TESH.alwaysfold=0
//***********************************************************************************
//*
//* ____ __ ____ _______________________________________
//* /_ _| _\ / __/ | T I M E D A B I L I T Y |
//* | || — \\__ \ | A N D |
//* |_||_|\_\___/ | S T A C K S Y S T E M |
//* By Spinnaker '—————————————————————————————————————'
//* v3.0.0.1
//*
//* What is TAS?
//* ————————————
//*
//* There are lots situations when you want your spells/abilities to add some
//* kind of timed buff/debuff which can be increased with new applications.
//* Timed ability/stack system allows you to time any ability you want,
//* and makes ability-stacking an easy job.
//*
//* System consists of one trigger. Script - depending on option chosen -
//* divides abilities into two groups:
//*
//* 1) Without refreshing - runs abilities that don't refresh with
//* new applications, but each stack has it's own private duration.
//*
//* 2) With refreshing - runs abilities that do refresh with every
//* new application.
//*
//*
//* Reason - why?
//* —————————————
//*
//* I decided to create this system due to lack of such and the high
//* frequency of adding timed stuff in various triggers.
//* Great example where such stacking abilities exist is obviously DotA.
//*
//*
//* How to implement?
//* —————————————————
//*
//* 1) Be sure "Automatically create unknown variables while pasting
//* trigger data" is enabled in the World Editor general preferences.
//*
//* 2) Copy and paste trigger 'TAS Veriable Creator' into your map for
//* automatical variable creation
//*
//* 3) Copy trigger "Timed AS" and paste it into your map.
//* System requires no Object Editor stuff.
//*
//* System is implemented. To run it properly see the next part.
//*
//*
//* What needs to be done?
//* ——————————————————————
//*
//* Set TASability to ability you want to be timed.
//* Now, choose unit that a timed ability will be added to.
//* Next step is setting duration of the ability in field TASduration.
//*
//* Select the refresh option. Choosing 'True' will let your ability
//* refresh even if maximum value of stacks has been reached, while 'False'
//* converts abilities to private stacks which don't interfere with each other.
//*
//* Enter maximum amount of applications in TASmaxstack.
//* Abilities with maximum value won't be increased, but can refreash.
//*
//* Leave the rest for the system, enjoy!
//* —————————————————————————————————————
//*
//*
//***********************************************************************************
//***************************************************************************
//*
//* Global Variables
//*
//***************************************************************************
//* udg_TAShash Hashtable for refreshable abilities issues
//* udg_TASindex Counts all running instances
//* udg_TAStimer Global timer for periodic issues
//* udg_TASdur[] Stores duration of each instance
//* udg_TASend[] Checks if given instance should be removed
//* udg_TASids[] Contains all the ability data
//* udg_TASref[] Checks if ability manipulated is refreshable or not
//* udg_TASunits[] For unit storage
//* User friendly parameters
//* udg_TASability Gets new ability
//* udg_TASunit Gets new unit
//* udg_TASduration Gets duration for new ability
//* udg_TASrefresh Sets instance type
//* udg_TASmaxstack Maximum amount of stacks for given ability
//***************************************************************************
//* Constant interval function
//***************************************************************************
constant function TASinterval takes nothing returns real
return 0.031250000
endfunction
//***************************************************************************
//*
//* System itself
//*
//***************************************************************************
function TASallocate takes integer id returns nothing
set udg_TASindex = udg_TASindex + 1
set udg_TASids[udg_TASindex] = id
set udg_TASunits[udg_TASindex] = udg_TASunit
set udg_TASdur[udg_TASindex] = udg_TASduration
set udg_TASref[udg_TASindex] = udg_TASrefresh
set udg_TASend[udg_TASindex] = false
endfunction
function TASdeallocate takes integer i returns nothing
set udg_TASunits[i] = udg_TASunits[udg_TASindex]
set udg_TASids[i] = udg_TASids[udg_TASindex]
set udg_TASdur[i] = udg_TASdur[udg_TASindex]
set udg_TASref[i] = udg_TASref[udg_TASindex]
set udg_TASend[i] = udg_TASend[udg_TASindex]
set udg_TASindex = udg_TASindex - 1
endfunction
function TASunitcheck takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0
endfunction
function TAScallback takes nothing returns nothing
local integer i = 1
local integer h
local integer n
local boolean b
local player p
loop
exitwhen i > udg_TASindex
if udg_TASref[i] then
set h = GetHandleId(udg_TASunits[i])
set udg_TASdur[i] = LoadReal(udg_TAShash, h, udg_TASids[i])
endif
set udg_TASdur[i] = udg_TASdur[i] - TASinterval()
set b = udg_TASref[i] or GetUnitAbilityLevel(udg_TASunits[i], udg_TASids[i]) < 2
if (udg_TASdur[i] <= 0. and b) or TASunitcheck(udg_TASunits[i]) then
set udg_TASend[i] = true
call UnitRemoveAbility(udg_TASunits[i], udg_TASids[i])
elseif udg_TASdur[i] <= 0. and not b then
set udg_TASend[i] = true
set p = GetOwningPlayer(udg_TASunits[i])
call SetPlayerAbilityAvailable(p, udg_TASids[i], false)
call DecUnitAbilityLevel(udg_TASunits[i], udg_TASids[i])
call SetPlayerAbilityAvailable(p, udg_TASids[i], true)
elseif udg_TASref[i] then
call SaveReal(udg_TAShash, h, udg_TASids[i], udg_TASdur[i])
endif
if udg_TASend[i] then
if udg_TASref[i] then
set n = LoadInteger(udg_TAShash, h, 0) - 1
if 0 == n then
call FlushChildHashtable(udg_TAShash, h)
else
call SaveInteger(udg_TAShash, h, 0, n)
endif
endif
call TASdeallocate(i)
set i = i - i
if 0 == udg_TASindex then
call PauseTimer(udg_TAStimer)
endif
endif
set i = i + 1
endloop
set p = null
endfunction
function TASexecute takes nothing returns nothing
local integer id = udg_TASability
local integer n = GetUnitAbilityLevel(udg_TASunit, id)
local player p
if 0 == udg_TASindex then
call TimerStart(udg_TAStimer, TASinterval(), true, function TAScallback)
endif
if UnitAddAbility(udg_TASunit, id) then
call TASallocate(id)
elseif n < udg_TASmaxstack then
set p = GetOwningPlayer(udg_TASunit)
call SetPlayerAbilityAvailable (p, id, false)
call IncUnitAbilityLevel (udg_TASunit, id)
call SetPlayerAbilityAvailable (p, id, true)
if not udg_TASrefresh then
call TASallocate(id)
endif
set p = null
endif
if udg_TASrefresh then
set n = GetHandleId(udg_TASunit)
if LoadReal(udg_TAShash, n, id) == 0. then
call SaveInteger (udg_TAShash, n, 0, LoadInteger(udg_TAShash, n, 0) + 1)
endif
call SaveReal(udg_TAShash, n, id, udg_TASduration)
endif
endfunction
//***************************************************************************
function InitTrig_Timed_AS takes nothing returns nothing
set gg_trg_Timed_AS = CreateTrigger()
set udg_TAStimer = CreateTimer()
set udg_TAShash = InitHashtable()
call TriggerAddAction(gg_trg_Timed_AS, function TASexecute)
endfunction
//===========================================================================
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 UnitEventCheckAfter takes nothing returns nothing
local integer i = 0
loop
set i = udg_CheckDeathList[i]
exitwhen i == 0
if udg_IsUnitNew[i] then
//The unit was just created.
set udg_IsUnitNew[i] = false
elseif udg_IsUnitTransforming[i] then
//Added 21 July 2017 to fix the issue re-adding this ability in the same instant
set udg_UDex = i
set udg_UnitTypeEvent = 0.00
set udg_UnitTypeEvent = 1.00
set udg_UnitTypeOf[i] = GetUnitTypeId(udg_UDexUnits[i]) //Set this afterward to give the user extra reference
set udg_IsUnitTransforming[i] = false
call UnitAddAbility(udg_UDexUnits[i], udg_DetectTransformAbility)
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 udg_CheckDeathInList[i] = false
endloop
//Empty the list
set udg_CheckDeathList[0] = 0
endfunction
function UnitEventCheckAfterProxy takes integer i returns nothing
if udg_CheckDeathList[0] == 0 then
call TimerStart(udg_CheckDeathTimer, 0.00, false, function UnitEventCheckAfter)
endif
if not udg_CheckDeathInList[i] then
set udg_CheckDeathList[i] = udg_CheckDeathList[0]
set udg_CheckDeathList[0] = i
set udg_CheckDeathInList[i] = true
endif
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 UnitEventCheckAfterProxy(i)
endif
elseif GetUnitAbilityLevel(u, udg_DetectTransformAbility) == 0 and not udg_IsUnitTransforming[i] then
set udg_IsUnitTransforming[i] = true
call UnitEventCheckAfterProxy(i) //This block has been updated on 21 July 2017
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
set udg_CheckDeathInList[i] = false
call UnitAddAbility(u, udg_DetectRemoveAbility)
call UnitMakeAbilityPermanent(u, true, udg_DetectRemoveAbility)
call UnitAddAbility(u, udg_DetectTransformAbility)
set udg_UnitTypeOf[i] = GetUnitTypeId(u)
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 UnitEventCheckAfterProxy(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 = bj_MAX_PLAYER_SLOTS //update to make it work with 1.29
local player p
local trigger t = CreateTrigger()
local trigger load = CreateTrigger()
local trigger death = CreateTrigger()
local trigger summon = CreateTrigger()
local rect r = GetWorldBounds()
local region re = CreateRegion()
local boolexpr enterB = Filter(function UnitEventEnter)
local boolexpr orderB = Filter(function UnitEventOnOrder)
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_CheckDeathList[0] = 0
set udg_UnitIndexerEnabled = true
call TriggerRegisterEnterRegion(CreateTrigger(), re, enterB)
call TriggerAddCondition(load, Filter(function UnitEventOnLoad))
call TriggerAddCondition(death, Filter(function UnitEventOnDeath))
call TriggerAddCondition(summon, Filter(function UnitEventOnSummon))
loop
set i = i - 1
set p = Player(i)
call SetPlayerAbilityAvailable(p, udg_DetectRemoveAbility, false)
call SetPlayerAbilityAvailable(p, udg_DetectTransformAbility, false)
call TriggerRegisterPlayerUnitEvent(summon, p, EVENT_PLAYER_UNIT_SUMMON, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, orderB)
call TriggerRegisterPlayerUnitEvent(death, p, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(load, p, EVENT_PLAYER_UNIT_LOADED, null)
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, enterB)
exitwhen i == 0
endloop
set summon = null
set death = null
set load = null
set re = null
set enterB = null
set orderB = null
set p = null
set r = null
set t = null
endfunction
function InitTrig_Unit_Event takes nothing returns nothing
endfunction