/*
function B2S takes boolean bool returns string
if(bool)then
return "true"
endif
return "false"
endfunction
function Player2S takes player p returns string
return "Player("+I2S(GetPlayerId(p))+")"
endfunction
function Unit2S takes unit u returns string
return GetUnitName(u)+ "_"+I2S(GetHandleId(u)-0x100000)
endfunction
*/
Name | Type | is_array | initial_value |
AdmiralCutlassHolder | unit | No | |
AdmiralCutlassMarker | minimapicon | No | |
AfterDamageEvent | real | No | |
AfterHealEvent | real | No | |
AlreadyPausedUnits | group | No | |
AOEDamageEvent | real | No | |
AOEDamageSource | unit | No | |
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 | |
ArmorDamageEvent | real | No | |
ArmorTypeDebugStr | string | Yes | |
ArthasAttackCount | integer | No | |
ArthasIsCoffined | boolean | Yes | |
Arthaslines | string | Yes | |
ArthaslinesCountPerPhase | integer | Yes | |
ArthaslinesSound | sound | Yes | |
AssassinPackSize | integer | No | |
AssassinTimer | timer | No | |
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 | |
AttackTypeDebugStr | string | Yes | |
BAmr_Attribute | real | Yes | |
BAmr_Boolean | boolean | Yes | |
BAmr_Config_ABSORBTION | real | No | |
BAmr_Config_ATT_BONUS | real | Yes | |
BAmr_Config_BASE_HP | real | No | |
BAmr_Config_BUFF | buffcode | No | |
BAmr_Config_FLAVOR | integer | No | |
BAmr_Config_FLAVOR_PATH | string | Yes | |
BAmr_Config_LVL_HP_BONUS | real | Yes | |
BAmr_Config_PERIODIC | real | No | |
BAmr_Config_RESIST_PHYSICAL | real | No | |
BAmr_Config_RESIST_SPELL | real | No | |
BAmr_Config_SEG_VALUE | real | Yes | |
BAmr_Config_SEGMENT_SPAWNED | integer | No | |
BAmr_Config_SPEF | effect | No | |
BAmr_Config_SPEF_PATH | string | Yes | |
BAmr_Config_SPEF_VALUE | real | Yes | |
BAmr_Config_SPELL_ID | abilcode | No | |
BAmr_Current_Rot_Speed | real | Yes | |
BAmr_DMG_Event | real | No | |
BAmr_Enchanted_Unit | unit | Yes | |
BAmr_Health | real | Yes | |
BAmr_Index | integer | No | |
BAmr_Loop | integer | No | |
BAmr_Loop_Segment | integer | No | |
BAmr_Max | real | Yes | |
BAmr_Point | location | Yes | |
BAmr_Real | real | Yes | |
BAmr_Seg_CValue | integer | No | |
BAmr_Seg_CValue_DeIndex | integer | No | |
BAmr_Segment_Missile | effect | Yes | |
BAmr_Segment_Missile_State | string | Yes | |
BAmr_Spef_Current_Height | real | Yes | |
BAmr_Total | real | Yes | |
BAmr_Unit | unit | Yes | |
Banehallow | unit | No | |
Beast_Attack_Ability_Level | integer | No | |
Beast_Attack_Atribute | integer | No | |
Beast_Attack_Atribute_Damage | real | No | |
Beast_Attack_Caster | unit | No | |
Beast_Attack_Damage_Splitter | integer | No | |
Beast_Attack_Life_Damage | real | No | |
Beast_Attack_Location | location | No | |
Beast_Attack_Location2 | location | No | |
Beast_Attack_Max_Life | real | No | |
Beast_Attack_Target | unit | No | |
Beast_Attack_Total_Damage | real | No | |
BloodyRingSacrifice | real | Yes | |
BloodyRingSacrificeIsOnCD | boolean | Yes | |
BoostLevel | integer | No | |
Boss1Point | location | No | |
BossDamageTaken | real | No | |
BossDamageThreshold | real | No | |
BossDummies | unit | Yes | |
BossDummiesCount | integer | No | |
BossEffectsArray | effect | Yes | |
BossEffectsCount | integer | No | |
Bosses | unit | Yes | |
BossMarker | minimapicon | No | |
BossPhase | integer | No | |
BossSpellLocations | location | Yes | |
BossSpellLocationsCount | integer | No | |
BossTargets | unit | Yes | |
BossTargetsCount | integer | No | |
BossTargetsCount1 | integer | No | |
BossUnits | group | No | |
BuildingDeadPackSize | integer | No | |
BUS_Loc | location | No | |
BUS_MapMaxX | real | No | |
BUS_MapMaxY | real | No | |
BUS_MapMinX | real | No | |
BUS_MapMinY | real | No | |
BUS_Target | unit | No | |
BUS_Unit | unit | No | |
BUS_X | real | No | |
BUS_Y | real | No | |
BUSC_Ability | real | Yes | |
BUSC_Cancel | boolean | Yes | |
BUSC_CChangeHueDelay | real | Yes | |
BUSC_CDelay | real | Yes | |
BUSC_CDuration | real | Yes | |
BUSC_ChangeHueDelay | real | Yes | |
BUSC_ChannelId | integer | Yes | |
BUSC_CHueAlpha | integer | Yes | |
BUSC_CHueBlue | integer | Yes | |
BUSC_CHueGreen | integer | Yes | |
BUSC_CHueRed | integer | Yes | |
BUSC_Delay | real | Yes | |
BUSC_Event | real | No | |
BUSC_HueChange | boolean | Yes | |
BUSC_HueChangeAlpha | integer | Yes | |
BUSC_HueChangeBlue | integer | Yes | |
BUSC_HueChangeGreen | integer | Yes | |
BUSC_HueChangeRed | integer | Yes | |
BUSC_NextNode | integer | Yes | |
BUSC_NodeNumber | integer | No | |
BUSC_Order | ordercode | Yes | |
BUSC_PCaster | unit | Yes | |
BUSC_PCurrentEffect | effect | Yes | |
BUSC_PCurrentZ | real | Yes | |
BUSC_PNextNode | integer | Yes | |
BUSC_PNodeNumber | integer | No | |
BUSC_PPrevNode | integer | Yes | |
BUSC_PRecyclableNodes | integer | No | |
BUSC_PRecycleNodes | integer | Yes | |
BUSC_PrevNode | integer | Yes | |
BUSC_PUnit | unit | Yes | |
BUSC_PXVelocity | real | Yes | |
BUSC_PYVelocity | real | Yes | |
BUSC_PZVelocity | real | Yes | |
BUSC_RecyclableNodes | integer | No | |
BUSC_RecycleNodes | integer | Yes | |
BUSC_Running | boolean | Yes | |
BUSC_Target | unit | Yes | |
BUSC_TargetX | real | Yes | |
BUSC_TargetY | real | Yes | |
BUSC_Timer | timer | No | |
BUSC_Unit | unit | Yes | |
BUSCR_AbsorbAOE | real | Yes | |
BUSCR_Counter | integer | No | |
BUSCR_Effect | string | Yes | |
BUSCR_HueAlpha | real | Yes | |
BUSCR_HueBlue | real | Yes | |
BUSCR_HueGreen | real | Yes | |
BUSCR_HueRed | real | Yes | |
BUSCR_HueSpeed | real | Yes | |
BUSCR_MaxAOE | real | Yes | |
BUSCR_MaxSize | real | Yes | |
BUSCR_MinAOE | real | Yes | |
BUSCR_MinSize | real | Yes | |
BUSCR_Name | string | Yes | |
BUSCR_Power | real | Yes | |
BUSCR_SpawnCount | integer | Yes | |
BUSCR_SpawnRate | real | Yes | |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CastleUpgradeLevel | integer | No | |
ChallengerNumber | integer | No | |
CheckDeathInList | boolean | Yes | |
CheckDeathList | integer | Yes | |
CheckDeathTimer | timer | No | |
CinermaticArthas | unit | No | |
CleanedItem | item | Yes | |
ClockTimer | timer | No | |
ConfirmDialog | dialog | No | |
ConnectedPlayers | force | No | |
CONVERTED_ATTACK_TYPE | attacktype | Yes | |
CONVERTED_DAMAGE_TYPE | damagetype | Yes | |
CountdownTimer | timer | No | |
CountdownTimerW | timerdialog | No | |
CreepKills | integer | Yes | 0 |
CreepSlayerEventDuration | real | No | |
CreepTimer | timer | No | |
CreepTimerW | timerdialog | 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_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 | |
DamageEventTarget | unit | No | |
DamageEventType | integer | No | |
DamageEventWeaponT | integer | No | |
DamageFilterAttackT | integer | No | |
DamageFilterDamageT | integer | No | |
DamageFilterFailChance | real | No | |
DamageFilterMinAmount | real | No | |
DamageFilterRunChance | real | No | |
DamageFilterSource | unit | No | |
DamageFilterSourceA | abilcode | No | |
DamageFilterSourceB | buffcode | No | |
DamageFilterSourceC | integer | No | |
DamageFilterSourceI | itemcode | No | |
DamageFilterSourceT | unitcode | No | |
DamageFilterTarget | unit | No | |
DamageFilterTargetA | abilcode | No | |
DamageFilterTargetB | buffcode | No | |
DamageFilterTargetC | integer | No | |
DamageFilterTargetI | itemcode | No | |
DamageFilterTargetT | unitcode | No | |
DamageFilterType | integer | No | |
DamageModifierEvent | real | No | |
DamageReturnAbsolutes | real | Yes | |
DamageReturnAllowedPlayers | force | No | |
DamageReturnCodeMultiplier | real | No | |
DamageReturnComputeTimes | real | Yes | |
DamageReturnExpireTime | real | No | |
DamageReturnFactor | real | No | |
DamageReturnInputUnit | unit | No | |
DamageReturnMeleeMultiplier | real | No | |
DamageReturnOutputAbsolute | real | No | |
DamageReturnOutputPercentage | real | No | |
DamageReturnPercentages | real | Yes | |
DamageReturnRangedMultiplier | real | No | |
DamageReturnSpellMultiplier | real | No | |
DamageReturnTempInteger | integer | No | |
DamageReturnUnitIndex | integer | No | |
DamageScalingUser | real | No | |
DamageScalingWC3 | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCode | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeDebugStr | string | Yes | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypePure | integer | No | |
DamageTypePureExplosive | integer | No | |
DamageTypeReduced | integer | No | |
DarkCome | rect | Yes | |
DarkHeroAbilities | abilcode | Yes | |
DarkHeroArmorIncPerSqrtDiffLvl | real | No | |
DarkHeroArmorInit | real | No | |
DarkHeroStatsIncPerSqrtDiffLvl | integer | No | |
DarkHeroStatsInit | integer | No | |
DarkHeroType | unitcode | Yes | |
DarkTowerDestroyedCount | integer | Yes | 0 |
DarkUnitType | unitcode | Yes | |
DeathEvent | real | No | |
DeathKnightArmorAbsolute | real | Yes | |
DeathKnightArmorPercent | real | Yes | |
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 | |
DefensePoint | location | No | |
DefenseTypeDebugStr | string | Yes | |
DemonicHandHolder | unit | No | |
DemonicHandMarker | minimapicon | No | |
DetectRemoveAbility | abilcode | No | |
DetectTransformAbility | abilcode | No | |
Difficulty | integer | No | 1 |
DotaHashtable | hashtable | No | |
DragonAttackPeriod | real | No | |
DragonAttackPoint | location | Yes | |
DragonAttackTypes | unitcode | Yes | |
DragonPackSize | integer | No | |
DragonTimer | timer | No | |
DualHeroChange | boolean | Yes | |
DualHeroPoint | location | Yes | |
EasyItem__ERRORMESSAGE | string | No | |
EasyItem__ERRORSOUND | string | No | |
EasyItem__ShopChargedItemCount | integer | No | |
EasyItem__ShopDummyCharges | itemcode | Yes | |
EasyItem__ShopRealCharges | itemcode | Yes | |
EasyItem__SPLIT | boolean | No | |
EasyItem__SPLITDROP | boolean | No | |
EasyItem__SPLITSIZE | integer | No | |
EasyItem__SPLITSTACK | boolean | No | |
EasyItem__SPLITSTACKDELAY | real | No | |
EasyItem__USEITEMLEVEL | boolean | No | |
EasyItem_integer | integer | Yes | |
EasyItem_item | item | No | |
EasyItem_items | item | Yes | |
EasyItem_loopIndex | integer | No | |
EasyItem_point | location | Yes | |
EasyItem_sound | sound | No | |
EasyItem_string | string | Yes | |
EasyItem_timer | timer | No | |
EasyItem_unit | unit | No | |
EasyItem_units | unit | Yes | |
EffectCenter | effect | No | |
EffectIn1 | effect | Yes | |
EffectIn2 | effect | Yes | |
EffectIn3 | effect | Yes | |
EffectIn4 | effect | Yes | |
EffectOut1 | effect | Yes | |
EffectOut2 | effect | Yes | |
EnhancedDamageTarget | unit | No | |
ExtremeGroup | group | No | |
ExtremePowerupPeriod | real | No | |
FarmEventCooldown | real | No | |
FarmEventDuration | real | No | |
FarmEventKillTracker | integer | Yes | |
FarmEventMaxFrequency | real | No | |
FarmEventRespawnPending | boolean | Yes | |
FarmEventSpawns | group | Yes | |
FarmEventTimer | timer | Yes | |
FarmUnitType | unitcode | Yes | |
FarmUnitTypeIndex | integer | Yes | |
FinalBossCenter | location | No | |
FinalWaveArmorIncPerPlayer | real | No | |
FinalWaveArmorIncPerSqrtDiff | real | No | |
FinalWaveArmorInit | real | No | |
FinalWaveCooldown | real | No | |
FinalWaveStatsIncPerPlayer | integer | No | |
FinalWaveStatsIncPerSqrtDiff | integer | No | |
FinalWaveStatsInit | integer | No | |
GameLevel | integer | Yes | 0 |
GameLevelCooldowns | real | Yes | |
GeodeAttacksCount | integer | Yes | |
GeodeIsDisabled | boolean | Yes | |
GlobalTempAbilityCode | abilcode | No | |
GlobalTempBoolean | boolean | No | |
GlobalTempForce | force | No | |
GlobalTempGroup | group | No | |
GlobalTempInteger | integer | No | |
GlobalTempInteger1 | integer | No | |
GlobalTempItem | item | No | |
GlobalTempItem1 | item | No | |
GlobalTempPlayer | player | No | |
GlobalTempPlayer1 | player | No | |
GlobalTempPoint | location | No | |
GlobalTempReal | real | No | |
GlobalTempReal1 | real | No | |
GlobalTempRegion | rect | No | |
GlobalTempString | string | No | |
GlobalTempUnit | unit | No | |
GlobalTempUnit1 | unit | No | |
GlobalTempUnitType | unitcode | 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_regen | real | Yes | |
HEAL_THRESHOLD | real | No | |
heal_timer | timer | No | |
HealAmount | real | No | |
HealSource | unit | No | |
HealTarget | unit | No | |
HeroAttackInterval | real | No | |
HeroBaseDamage | integer | No | |
HeroDefense | real | No | |
HeroMaxHP | integer | No | |
Host | player | No | |
HydraAttackPeriod | real | No | |
HydraAttackPoint | location | Yes | |
HydraAttackTypes | unitcode | Yes | |
HydraPackSize | integer | No | |
HydraTimer | timer | No | |
IceGates | destructable | Yes | |
IcePillars | unit | Yes | |
IceTowers | unit | Yes | |
IceWayOpened | boolean | Yes | |
InitialPlayers | force | No | |
InnerCircleHeroCount | integer | No | 11 |
InvaderKills | integer | Yes | 0 |
InvaderSlayerAvailable | boolean | No | true |
InvaderSlayerBoss | unit | No | |
InvaderSlayerEventDuration | real | No | |
IsDamageAttack | boolean | No | |
IsDamageCode | boolean | No | |
IsDamageMelee | boolean | No | |
IsDamageRanged | boolean | No | |
IsDamageSpell | boolean | No | |
IsSelfHeal | boolean | No | |
IsUnitAlive | boolean | Yes | |
IsUnitBeingKnockedBack | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
IsUnitTransforming | boolean | Yes | |
ItemAbilities | abilcode | Yes | |
ItemAbilityCount | integer | No | |
ItemCleanupFlag | boolean | No | |
ItemCleanupTimer | timer | No | |
ItemsToClean | integer | No | |
K2DAmphibious | boolean | Yes | |
K2DAngle | real | Yes | |
K2DBounce | boolean | Yes | |
K2DCenterPoint | location | No | |
K2DCollision | real | Yes | |
K2DCos | real | Yes | |
K2DCosD1 | real | Yes | |
K2DCosD2 | real | Yes | |
K2DCosH | real | Yes | |
K2DDebrisKiller | unit | No | |
K2DDestRadius | real | Yes | |
K2DDistanceLeft | real | Yes | |
K2DFlying | boolean | Yes | |
K2DFreeze | boolean | Yes | |
K2DFriction | real | Yes | |
K2DFXModel | string | Yes | |
K2DFXRate | real | Yes | |
K2DFXTimeLeft | real | Yes | |
K2DHeight | real | Yes | |
K2DHeightThreshold | real | Yes | |
K2DImpact | trigger | Yes | |
K2DItem | item | No | |
K2DItemOffset | boolean | No | |
K2DItemsFound | boolean | No | |
K2DKillTrees | boolean | Yes | |
K2DLastX | real | Yes | |
K2DLastY | real | Yes | |
K2DMaxDestRadius | real | No | |
K2DMaxX | real | No | |
K2DMaxY | real | No | |
K2DMinX | real | No | |
K2DMinY | real | No | |
K2DNext | integer | Yes | |
K2DOverride | boolean | Yes | |
K2DPause | boolean | Yes | |
K2DPrev | integer | Yes | |
K2DRadians_QuarterPi | real | No | |
K2DRadians_QuarterTurn | real | No | |
K2DRadians_Turn | real | No | |
K2DRadius | integer | Yes | |
K2DRegion | rect | No | |
K2DSimple | boolean | Yes | |
K2DSin | real | Yes | |
K2DSinD1 | real | Yes | |
K2DSinD2 | real | Yes | |
K2DSinH | real | Yes | |
K2DSource | unit | Yes | |
K2DTargetPoint | location | No | |
K2DTimeLeft | real | Yes | |
K2DTimeout | real | No | |
K2DTimer | timer | No | |
K2DUnbiasedCollision | boolean | Yes | |
K2DVelocity | real | Yes | |
K2DX | real | No | |
K2DY | real | No | |
KillerOfUnit | unit | Yes | |
KnightArmorAbsolute | real | Yes | |
KnightArmorPercent | real | Yes | |
Knockback2DAmphibious | boolean | No | |
Knockback2DAngle | real | No | |
Knockback2DBounces | boolean | No | |
Knockback2DCollision | real | No | |
Knockback2DDefaultBounce | boolean | No | |
Knockback2DDefaultDestRadius | real | No | |
Knockback2DDefaultFriction | real | No | |
Knockback2DDefaultFX | string | No | |
Knockback2DDefaultFXRate | real | No | |
Knockback2DDefaultGravity | real | No | |
Knockback2DDefaultKillTrees | boolean | No | |
Knockback2DDefaultPause | boolean | No | |
Knockback2DDestRadius | real | No | |
Knockback2DDistance | real | No | |
Knockback2DFriction | real | No | |
Knockback2DFXRate | real | No | |
Knockback2DGravity | real | No | |
Knockback2DHeight | real | No | |
Knockback2DKillTrees | boolean | No | |
Knockback2DLoopFX | string | No | |
Knockback2DOnImpact | trigger | No | |
Knockback2DOverride | boolean | No | |
Knockback2DPause | boolean | No | |
Knockback2DRobustPathing | integer | No | |
Knockback2DSimple | boolean | No | |
Knockback2DSource | unit | No | |
Knockback2DTime | real | No | |
Knockback2DTreeOrDebris | string | No | |
Knockback2DUnbiasedCollision | boolean | No | |
Knockback2DUnit | unit | No | |
Knockback_Loc1 | location | No | |
Knockback_Loc2 | location | No | |
Knockback_Target | unit | No | |
KnockbackAngle | real | No | |
KnockbackDistance | real | No | |
KnockbackSpeed | real | No | |
KnockbackTable | hashtable | No | |
KnockbackUnits | group | No | |
LeaklessRadius | integer | No | |
LethalDamageEvent | real | No | |
LethalDamageHP | real | No | |
LevelDragonTypes | unitcode | Yes | |
LifeStealAllowedPlayers | force | No | |
LifeStealCodeMultiplier | real | No | |
LifeStealComputeTimes | real | Yes | |
LifeStealExpireTime | real | No | |
LifeStealFactor | real | No | |
LifeStealInputUnit | unit | No | |
LifeStealMeleeMultiplier | real | No | |
LifeStealOutputPercentage | real | No | |
LifeStealPercentages | real | Yes | |
LifeStealRangedMultiplier | real | No | |
LifeStealSpellMultiplier | real | No | |
LifeStealUnitIndex | integer | No | |
LightHeroes | unit | Yes | |
LightHeroesAddedStats | integer | Yes | |
LightHeroRestTime | real | No | 0.00 |
LightHeroRestTimers | timer | Yes | |
LightHeroSaveDirection | real | Yes | 0 |
LightHeroSavePoint | location | Yes | |
LightHeroTimerW | timerdialog | Yes | |
LocalTempAbilityCode | abilcode | No | |
LocalTempEffect | effect | No | |
LocalTempEffect1 | effect | No | |
LocalTempEffect2 | effect | No | |
LocalTempGroup | group | No | |
LocalTempGroup1 | group | No | |
LocalTempIndex | integer | No | |
LocalTempIndex1 | integer | No | |
LocalTempIndex2 | integer | No | |
LocalTempInteger | integer | No | |
LocalTempInteger1 | integer | No | |
LocalTempItem | item | No | |
LocalTempItem1 | item | No | |
LocalTempPlayer | player | No | |
LocalTempPlayer1 | player | No | |
LocalTempPoint | location | No | |
LocalTempPoint1 | location | No | |
LocalTempPoint2 | location | No | |
LocalTempReal | real | No | |
LocalTempReal1 | real | No | |
LocalTempReal2 | real | No | |
LocalTempString | string | No | |
LocalTempUnit | unit | No | |
LocalTempUnit1 | unit | No | |
LocalTempUnit2 | unit | No | |
Loop | integer | No | |
MasterBladeHolder | unit | No | |
MasterBladeMarker | minimapicon | No | |
ModeDual | boolean | No | |
ModeEasy | boolean | No | |
ModeRandom | boolean | No | |
ModeSame | boolean | No | |
MUAdditionalAbilities | abilcode | Yes | |
MUAdditionalAbilitiesCount | integer | No | |
MUAttackSpeedBonusPercent | real | No | |
MUBaseDamageBonus | integer | No | |
MUBaseDamageBonusPercent | real | No | |
MUDefenseBonus | real | No | |
MUDefenseBonusPercent | real | No | |
MUMaxHPBonus | integer | No | |
MUMaxHPBonusPercent | real | No | |
MUPrevAdditionalAbilities | abilcode | Yes | |
MUPrevAdditionalAbilitiesCount | integer | No | |
MUPrevAttackSpeedBonusPercent | real | No | |
MUPrevBaseDamageBonus | integer | No | |
MUPrevBaseDamageBonusPercent | real | No | |
MUPrevDefenseBonus | real | No | |
MUPrevDefenseBonusPercent | real | No | |
MUPrevMaxHPBonus | integer | No | |
MUPrevMaxHPBonusPercent | real | No | |
Muradin | unit | No | |
MuradinEventCooldown | real | No | |
MuradinEventDuration | real | No | |
MURefreshTrigger | trigger | No | |
MURegisteredPlayers | force | No | |
MurlocAttackGroup | group | No | |
MurlocAttackPeriod | real | No | |
MurlocAttackSpawnTypes | unitcode | Yes | |
MurlocAttackTypes | unitcode | Yes | |
MurlocPackSize | integer | No | |
MurlocTimer | timer | No | |
MusicPoint | integer | No | 1 |
MUTempIndex | integer | No | |
MUTempReal | real | No | |
MUTempUnit | unit | No | |
MUTempUnitGroup | group | No | |
NeutralHeroType | unitcode | Yes | |
NextDamageIsAttack | boolean | No | |
NextDamageIsMelee | boolean | No | |
NextDamageIsRanged | boolean | No | |
NextDamageType | integer | No | |
NextDamageWeaponT | integer | No | |
NextHealAmount | real | No | |
NextHealSource | unit | No | |
NextHealTarget | unit | No | |
NextSpecialTimer | timer | No | |
NextSpecialTimerW | timerdialog | No | |
OnDamageEvent | real | No | |
OptionGameButtonEasy | button | No | |
OptionGameButtonExtreme | button | No | |
OptionGameButtonNormal | button | No | |
OptionGameDialog | dialog | No | |
OptionHeroButtonDual | button | No | |
OptionHeroButtonDualRandom | button | No | |
OptionHeroButtonDualSame | button | No | |
OptionHeroButtonNormal | button | No | |
OptionHeroButtonRandom | button | No | |
OptionHeroButtonSame | button | No | |
OptionHeroDialog | dialog | No | |
OptionTimer | timer | No | |
OuterCircleHeroCount | integer | No | 19 |
PDex | integer | No | |
PickMockHeroes | unit | Yes | |
PlayerColors | string | Yes | |
PlayerCount | integer | No | |
PlayerScore | integer | Yes | 0 |
PlayerSelection | group | Yes | |
PlayerSelectionEvent | real | No | |
POINT | location | No | |
PowerUpDummy | group | No | |
PreDamageEvent | real | No | |
PreHealEvent | real | No | |
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 | |
Quests | quest | Yes | |
RandomHero | integer | Yes | 0 |
regen_buildup | real | Yes | |
REGEN_EVENT_INTERVAL | real | No | |
REGEN_STRENGTH_VALUE | real | No | |
REGEN_THRESHOLD | real | No | |
regen_timeleft | real | Yes | |
RemoveDamageEvent | boolean | No | |
RemovedPlayerIndex | integer | No | |
RosHashtable | hashtable | No | |
RunePoint | location | No | |
RuneTypeCount | integer | No | |
RuneTypes | itemcode | Yes | |
Scream_Ability_Level | integer | No | |
Scream_Area_of_Effect | real | No | |
Scream_Base_Area_of_Effect | real | No | |
Scream_Base_Damage | real | No | |
Scream_Bounus_Area_of_Effect | real | No | |
Scream_Caster | unit | No | |
Scream_Damage_Group | group | No | |
Scream_Dummy_Ability | abilcode | No | |
Scream_Location | location | No | |
Scream_Owner | player | No | |
Scream_Sound | sound | No | |
Scream_Sound_Volume | real | No | |
Scream_Total_Damage | real | No | |
SelectAreaCenter | location | No | |
SelectedHero | unit | Yes | |
SelectionTimer | timer | No | |
SelectionTimerW | timerdialog | No | |
ShieldOfInvincibilityAbsolute | real | Yes | |
ShieldOfInvincibilityPercent | real | Yes | |
SilentAssassinAttackPeriod | real | No | |
SilentAssassinTypes | unitcode | Yes | |
SkeletonArmorHolder | unit | No | |
SkeletonArmorMarker | minimapicon | No | |
SorcererCuirassDamageTaken | real | Yes | |
SorcererCuirassIsOnCD | boolean | Yes | |
SourceDamageEvent | real | No | |
Special4Challenger | integer | No | |
Special4Group | group | No | |
Special5Challenger | integer | No | |
Special5Group | group | No | |
Special8Group | group | No | |
Special9Challenger | integer | No | |
SpecialArea | rect | Yes | |
SpecialArenaCenter | location | No | |
Specials04Check | boolean | Yes | false |
Specials05Check | boolean | Yes | false |
SpecialsButtonExtreme1 | button | No | |
SpecialsButtonExtreme2 | button | No | |
SpecialsButtonExtreme3 | button | No | |
SpecialsButtonExtreme4 | button | No | |
SpecialsDialog | dialog | No | |
SpecialsExit | button | No | |
SpecialsNo | button | No | |
SpecialsYes | button | No | |
SpellDummyEffect | effect | Yes | |
SpellDummyUnit | unit | Yes | |
SpikedCarapaceAbsolute | real | Yes | |
SpikedCarapacePercent | real | Yes | |
StartPoint | location | No | |
SummonerOfUnit | unit | Yes | |
SuperItemTypeCount | integer | No | |
SuperItemTypes | itemcode | Yes | |
Survival2Center | location | No | |
SurvivalBountyPerPlayer | integer | No | |
SurvivalCome | rect | Yes | |
SurvivalWave | integer | No | |
TCAAvalancheCaster | unit | No | |
TCAFixAbilityCount | integer | No | |
TCAFixAbilityList | abilcode | Yes | |
TCAFixDeathTriggers | trigger | Yes | |
TCAFixOrderIssuedTriggers | trigger | Yes | |
TCAFixRegisteredUnits | group | No | |
TCAFixResumeHoldPosTrigger | trigger | No | |
TCAFixTargetAcquiredTriggers | trigger | Yes | |
TCAFixTempBoolean | boolean | No | |
TCAFixTempBoolean1 | boolean | No | |
TCAFixTempInteger | integer | No | |
TCAFixTempInteger1 | integer | No | |
TCAFixTempPoint | location | No | |
TCAFixTempPoint1 | location | No | |
TCAFixTempString0 | string | No | |
TCAFixTempString1 | string | No | |
TCAFixTempString2 | string | No | |
TCAFixTempUnit | unit | No | |
TCAFixUnitAttackedTrigger | trigger | No | |
TCAFixUnitHoldPositionBroken | boolean | Yes | |
TCAFixUnitHoldsPosition | boolean | Yes | |
TCAFixUnitLastAcquireTime | real | Yes | |
TCAFixUnitLastAttackTime | real | Yes | |
TCAFixUnitPatrols | boolean | Yes | |
TCATempIndex | integer | No | |
TeleportMarker1 | minimapicon | No | |
TeleportMarker2 | minimapicon | No | |
TempAvalancheDamages | real | No | |
TempTossCaster | unit | No | |
TempTossDamages | real | No | |
TempTossProjectedUnit | unit | No | |
ThornsAuraAbsolute | real | Yes | |
ThornsAuraPercent | real | Yes | |
TomesCount | integer | No | 3 |
TowerWeaponryLevel | integer | No | |
TYPE | unitcode | No | |
UDex | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexUnits | unit | Yes | |
UG | group | No | |
UNIT | unit | No | |
UNIT_CLASS_ANCIENT | integer | No | |
UNIT_CLASS_ATTACKS_FLYING | integer | No | |
UNIT_CLASS_ATTACKS_GROUND | integer | No | |
UNIT_CLASS_DEAD | integer | No | |
UNIT_CLASS_ETHEREAL | integer | No | |
UNIT_CLASS_FLYING | integer | No | |
UNIT_CLASS_GIANT | integer | No | |
UNIT_CLASS_GROUND | integer | No | |
UNIT_CLASS_HERO | integer | No | |
UNIT_CLASS_MAGIC_IMMUNE | integer | No | |
UNIT_CLASS_MECHANICAL | integer | No | |
UNIT_CLASS_MELEE | integer | No | |
UNIT_CLASS_PEON | integer | No | |
UNIT_CLASS_PLAGUED | integer | No | |
UNIT_CLASS_POISONED | integer | No | |
UNIT_CLASS_POLYMORPHED | integer | No | |
UNIT_CLASS_RANGED | integer | No | |
UNIT_CLASS_RESISTANT | integer | No | |
UNIT_CLASS_SAPPER | integer | No | |
UNIT_CLASS_SLEEPING | integer | No | |
UNIT_CLASS_SNARED | integer | No | |
UNIT_CLASS_STRUCTURE | integer | No | |
UNIT_CLASS_STUNNED | integer | No | |
UNIT_CLASS_SUMMONED | integer | No | |
UNIT_CLASS_TAUREN | integer | No | |
UNIT_CLASS_TOWNHALL | integer | No | |
UNIT_CLASS_UNDEAD | integer | No | |
UnitAttackInterval | real | No | |
UnitBaseDamage | integer | No | |
UnitDefense | real | No | |
UnitInAction | boolean | Yes | |
UnitInActionEvent | real | No | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitMaxHP | integer | No | |
UnitQuantity | integer | Yes | 0 |
UnitRace | integer | Yes | 0 |
UnitType | integer | Yes | 0 |
UnitTypeEvent | real | No | |
UnitTypeOf | unitcode | Yes | |
VictoryCooldown | real | No | |
ViewCircle | fogmodifier | Yes | |
WarnTimer | timer | No | |
WaveCome | rect | Yes | |
WaveKills | integer | Yes | 0 |
WaveNumber | integer | No | |
WavePeriod | real | No | |
WaveSlayerEventDuration | real | No | |
WaveSpecialEffect | effect | No | |
WaveTimer | timer | No | |
WaveTimerW | timerdialog | No | |
WaveUnitType | unitcode | Yes | |
WayCount | integer | No | 0 |
WayGates | destructable | Yes | |
WayNecropolis | unit | Yes | |
WayOpened | boolean | Yes | false |
WayTowers | unit | Yes | |
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 | |
WorldMaxX | real | No | |
WorldMaxY | real | No | |
ZeroDamageEvent | real | No | |
ZeroHealEvent | real | No |
library DebugLogger requires TimeUtils
globals
// --------------------------------------------------------------------
// Enum log levels
// --------------------------------------------------------------------
constant integer LVL_DEBUG = 0
constant integer LVL_INFO = 1
constant integer LVL_WARN = 2
constant integer LVL_ERROR = 3
private constant integer LVL_NONE = 999 // Only for internal reference, do not use
private string array LevelTags [4]
private string array ColorTags [4]
// --------------------------------------------------------------------
// Config: display
// --------------------------------------------------------------------
private integer DISPLAY_LEVEL = LVL_NONE // Everything of lower level is not displayed
private constant real DISPLAY_TIME = 3.00 // Display time in seconds
// --------------------------------------------------------------------
// Config: write
// --------------------------------------------------------------------
private integer WRITE_LEVEL = LVL_NONE
private integer WRITENOW_LEVEL = LVL_NONE
private constant integer FILE_MAX_ENTRIES = 500 // Min 1, Max 8192
private constant string OUTPUT_FILE_NAME = "XndHeroSiegeLog" // Output file prefix. File number & .txt extensions are automatically added.
// --------------------------------------------------------------------
// Internal
// --------------------------------------------------------------------
private string array WriteLogs
private integer FirstWriteLogIndex = -1 // Start iterator (for read)
private integer LastWriteLogIndex = -1 // End iterator (for read & write)
private integer FileIndex = 0
endglobals
static if (FILTER_USERS) then
// --------------------------------------------------------------------
// DebugUserFilter: logs will display only to the players matching it
// Does not impact log writing in text file.
// --------------------------------------------------------------------
private function DebugUserFilter takes nothing returns boolean
local integer index = 0
loop
if ( GetPlayerName(GetEnumPlayer()) == ONLY_USERS[index] ) then
return true
endif
set index = index + 1
exitwhen (index >= ONLY_USERS.size)
endloop
return false
endfunction
endif
// --------------------------------------------------------------------
// SetLogLevel: set display & write log level
// --------------------------------------------------------------------
function SetLogLevel takes integer displayLevel, integer writeLevel, integer writeNowLevel returns nothing
set DISPLAY_LEVEL = displayLevel
set WRITE_LEVEL = writeLevel
set WRITENOW_LEVEL = writeNowLevel
endfunction
// --------------------------------------------------------------------
// WriteInFile: write up to last 8191 logs to the output file
// --------------------------------------------------------------------
function WriteInFile takes nothing returns nothing
local integer index
// There are logs to write in file
call PreloadGenClear()
call PreloadGenStart()
call Preload("Log file")
if ( (FirstWriteLogIndex != -1) and (LastWriteLogIndex != -1) ) then
set index = FirstWriteLogIndex
loop
call Preload(WriteLogs[index])
set index = index + 1
if ( index == 8191 ) then
set index = 0
endif
exitwhen index == LastWriteLogIndex
endloop
else
call Preload("<Empty log file>")
endif
call PreloadGenEnd(OUTPUT_FILE_NAME + "-" + I2S(FileIndex) + ".txt" )
endfunction
// --------------------------------------------------------------------
// DisplayLog: displays log as text
// --------------------------------------------------------------------
private function DisplayLog takes integer level, string message returns nothing
call DisplayTimedTextToForce( GetPlayersAll() , DISPLAY_TIME, LevelTags[level] + " " + "|c00" + ColorTags[level] + message +"|r" )
endfunction
// --------------------------------------------------------------------
// SavePrintLog: format logs and save them internally.
// If reached max file entry: prints file and goes to next file index.
// If level >= WRITENOW_LEVEL, then calls the WriteInFile() function.
// --------------------------------------------------------------------
private function SavePrintLog takes integer level, string message returns nothing
local string printMessage = ""
local real elapsedTime = TimerGetElapsed(udg_ClockTimer)
local string elapsedTimeString = FormatTime(elapsedTime)
if (FILE_MAX_ENTRIES == LastWriteLogIndex + 1 ) then
// Write this log file to disk and go to the next log file
call WriteInFile()
set FileIndex = FileIndex + 1
set FirstWriteLogIndex = -1
set LastWriteLogIndex = -1
endif
// Move last cursor
set LastWriteLogIndex = ModuloInteger(LastWriteLogIndex + 1, 8191)
// Move first cursor (when it is pushed front by last cursor)
if ( -1 == FirstWriteLogIndex ) then
set FirstWriteLogIndex = 0 // Start
elseif ( FirstWriteLogIndex == LastWriteLogIndex ) then
set FirstWriteLogIndex = ModuloInteger(FirstWriteLogIndex + 1, 8191) // Rolling logs
endif
// Print formating
set printMessage = LevelTags[level] + " [" + elapsedTimeString + "] " + message
// Save logs in a string array
set WriteLogs[LastWriteLogIndex] = printMessage
if (level >= WRITENOW_LEVEL) then
// Write this log file to disk
call WriteInFile()
endif
endfunction
// --------------------------------------------------------------------
// DebugLog: API function to push a new log.
// --------------------------------------------------------------------
function DebugLog takes integer level, string message returns nothing
if ( level >= DISPLAY_LEVEL ) then
call DisplayLog(level, message)
endif
if ( level >= WRITE_LEVEL ) then
call SavePrintLog(level, message)
endif
endfunction
// ----------------------------------------------------------------------------
// onInit: system initializer
// ----------------------------------------------------------------------------
private module M
private static method onInit takes nothing returns nothing
set LevelTags[LVL_DEBUG] = "[DEBUG]"
set LevelTags[LVL_INFO] = "[INFO] "
set LevelTags[LVL_WARN] = "[WARN] "
set LevelTags[LVL_ERROR] = "[ERROR]"
set ColorTags[LVL_DEBUG] = "999999"
set ColorTags[LVL_INFO] = "fffff"
set ColorTags[LVL_WARN] = "ff7f00"
set ColorTags[LVL_ERROR] = "ff0000"
endmethod
endmodule
private struct S extends array
implement M
endstruct
endlibrary
// Just set the log levels when REPLAY is detected
library LogDuringReplay requires DebugLogger, GameStatus
private module LogDuringReplayInit
private static method onInit takes nothing returns nothing
if ( GAME_STATUS_REPLAY == GetGameStatus() ) then
call SetLogLevel(LVL_ERROR, LVL_DEBUG, LVL_DEBUG)
endif
endmethod
endmodule
private struct LogDuringReplay
implement LogDuringReplayInit
endstruct
endlibrary
// -------------------------------------------------------------------------------
// Library: automatically log some primitives known for desync
// -------------------------------------------------------------------------------
library PrimitiveDebugAutomation requires DebugLogger
// -------------------------------------------------------------------------------
// LOG_NATIVE: macro that adds a log function to a native (with a hook)
// -------------------------------------------------------------------------------
//! textmacro LOG_NATIVE takes NATIVE, PARAMETERS, LOG_LEVEL
public function on$NATIVE$ takes $PARAMETERS$ returns nothing
call DebugLog(LVL_$LOG_LEVEL$, "$NATIVE$")
endfunction
hook $NATIVE$ on$NATIVE$
//! endtextmacro
// -------------------------------------------------------------------------------
// Logged native declarations
// -------------------------------------------------------------------------------
/* Natives that may be watched for desync debugging
GetDestructableName
GetSoundDuration
GetSoundFileDuration
GetCameraBoundMinX
GetCameraBoundMinY
GetCameraBoundMaxX
GetCameraBoundMaxY
GetCameraField
GetCameraTargetPositionX
GetCameraTargetPositionY
GetCameraTargetPositionZ
GetCameraTargetPositionLoc
GetCameraEyePositionX
GetCameraEyePositionY
GetCameraEyePositionZ
GetCameraEyePositionLoc
GetObjectName
IsMultiboardMinimized
GetLocalPlayer
GetLocationZ
GetLocalizedString
GetLocalizedHotkey
GetUnitName
BlzGetLocalUnitZ
BlzGetUnitZ
GetItemName
BlzGetItemDescription
BlzGetItemTooltip
BlzGetItemExtendedTooltip
SmartCameraPanBJ
GetPlayerSlotState
GetPlayerController
BlzGetLocalSpecialEffectX
BlzGetLocalSpecialEffectY
BlzGetLocalSpecialEffectZ
*/
////! runtextmacro LOG_NATIVE ("GetDestructableName", "destructable d", "WARN")
//! runtextmacro LOG_NATIVE ("GetSoundDuration", "sound soundHandle", "WARN")
//! runtextmacro LOG_NATIVE ("GetSoundFileDuration", "string musicFileName", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraBoundMinX", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraBoundMinY", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraBoundMaxX", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraBoundMaxY", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraField", "camerafield whichField", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraTargetPositionX", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraTargetPositionY", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraTargetPositionZ", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraTargetPositionLoc", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraEyePositionX", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraEyePositionY", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraEyePositionZ", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetCameraEyePositionLoc", "nothing", "WARN")
////! runtextmacro LOG_NATIVE ("GetObjectName", "integer objectId", "WARN")
//! runtextmacro LOG_NATIVE ("IsMultiboardMinimized", "multiboard lb", "WARN")
//! runtextmacro LOG_NATIVE ("GetLocalPlayer", "nothing", "WARN")
//! runtextmacro LOG_NATIVE ("GetLocationZ", "location whichLocation", "WARN")
//! runtextmacro LOG_NATIVE ("GetLocalizedString", "string source", "WARN")
//! runtextmacro LOG_NATIVE ("GetLocalizedHotkey", "string source", "WARN")
////! runtextmacro LOG_NATIVE ("GetUnitName", "unit whichUnit", "WARN")
//! runtextmacro LOG_NATIVE ("BlzGetLocalUnitZ", "unit whichUnit", "WARN")
//! runtextmacro LOG_NATIVE ("BlzGetUnitZ", "unit whichUnit", "WARN")
////! runtextmacro LOG_NATIVE ("GetItemName", "item whichItem", "WARN")
//! runtextmacro LOG_NATIVE ("BlzGetItemDescription", "item whichItem", "WARN")
//! runtextmacro LOG_NATIVE ("BlzGetItemTooltip", "item whichItem", "WARN")
//! runtextmacro LOG_NATIVE ("BlzGetItemExtendedTooltip", "item whichItem", "WARN")
//! runtextmacro LOG_NATIVE ("SmartCameraPanBJ", "player whichPlayer, location loc, real duration", "WARN")
//! runtextmacro LOG_NATIVE ("GetPlayerSlotState", "player whichPlayer", "WARN")
//! runtextmacro LOG_NATIVE ("GetPlayerController", "player whichPlayer", "WARN")
//! runtextmacro LOG_NATIVE ("BlzGetLocalSpecialEffectX", "effect whichEffect", "WARN")
//! runtextmacro LOG_NATIVE ("BlzGetLocalSpecialEffectY", "effect whichEffect", "WARN")
//! runtextmacro LOG_NATIVE ("BlzGetLocalSpecialEffectZ", "effect whichEffect", "WARN")
// ----------------------------------------------------------------------------
// onInit: system initializer
// ----------------------------------------------------------------------------
//private module M
// private static method onInit takes nothing returns nothing
// endmethod
//endmodule
//private struct S extends array
// implement M
//endstruct
endlibrary
// -------------------------------------------------------------------------------
// DECLARE_LOG_FUNCTIONS: automatically log all global triggers.
// -------------------------------------------------------------------------------
library TriggerDebugAutomation requires DebugLogger
//! textmacro TRIGGER_IS_READY takes GLOBALVARIABLENAME
call TriggerIsReady($GLOBALVARIABLENAME$, "$GLOBALVARIABLENAME$")
//! endtextmacro
//! textmacro EVENTID takes GLOBALVARIABLENAME
if (whichEvent == EVENT_$GLOBALVARIABLENAME$) then
return "$GLOBALVARIABLENAME$"
endif
//! endtextmacro
globals
private hashtable pData
private integer kIsRegistered = StringHashBJ("IsRegistered")
private integer kHasConditions = StringHashBJ("HasConditions")
private integer kHasActions = StringHashBJ("HasAction")
private integer kDisplayName = StringHashBJ("DisplayName")
private integer kCondition = StringHashBJ("Condition")
private integer kConditionValue = StringHashBJ("ConditionValue")
private integer iLastTriggerIndex = 0
private trigger array aRegisteredTriggers
endglobals
// -------------------------------------------------------------------------------
// getEventName: returns the name of an event.
// -------------------------------------------------------------------------------
private function getEventName takes eventid whichEvent returns string
if (whichEvent == EVENT_GAME_VICTORY) then
return "MAP_INIT"
endif
//! runtextmacro EVENTID("GAME_END_LEVEL")
//! runtextmacro EVENTID("GAME_VARIABLE_LIMIT")
//! runtextmacro EVENTID("GAME_STATE_LIMIT")
//! runtextmacro EVENTID("GAME_TIMER_EXPIRED")
//! runtextmacro EVENTID("GAME_ENTER_REGION")
//! runtextmacro EVENTID("GAME_LEAVE_REGION")
//! runtextmacro EVENTID("GAME_TRACKABLE_HIT")
//! runtextmacro EVENTID("GAME_TRACKABLE_TRACK")
//! runtextmacro EVENTID("GAME_SHOW_SKILL")
//! runtextmacro EVENTID("GAME_BUILD_SUBMENU")
//! runtextmacro EVENTID("PLAYER_STATE_LIMIT")
//! runtextmacro EVENTID("PLAYER_ALLIANCE_CHANGED")
//! runtextmacro EVENTID("PLAYER_DEFEAT")
//! runtextmacro EVENTID("PLAYER_VICTORY")
//! runtextmacro EVENTID("PLAYER_LEAVE")
//! runtextmacro EVENTID("PLAYER_CHAT")
//! runtextmacro EVENTID("PLAYER_END_CINEMATIC")
//! runtextmacro EVENTID("PLAYER_UNIT_ATTACKED")
//! runtextmacro EVENTID("PLAYER_UNIT_RESCUED")
//! runtextmacro EVENTID("PLAYER_UNIT_DEATH")
//! runtextmacro EVENTID("PLAYER_UNIT_DECAY")
//! runtextmacro EVENTID("PLAYER_UNIT_DETECTED")
//! runtextmacro EVENTID("PLAYER_UNIT_HIDDEN")
//! runtextmacro EVENTID("PLAYER_UNIT_SELECTED")
//! runtextmacro EVENTID("PLAYER_UNIT_DESELECTED")
//! runtextmacro EVENTID("PLAYER_UNIT_CONSTRUCT_START")
//! runtextmacro EVENTID("PLAYER_UNIT_CONSTRUCT_CANCEL")
//! runtextmacro EVENTID("PLAYER_UNIT_CONSTRUCT_FINISH")
//! runtextmacro EVENTID("PLAYER_UNIT_UPGRADE_START")
//! runtextmacro EVENTID("PLAYER_UNIT_UPGRADE_CANCEL")
//! runtextmacro EVENTID("PLAYER_UNIT_UPGRADE_FINISH")
//! runtextmacro EVENTID("PLAYER_UNIT_TRAIN_START")
//! runtextmacro EVENTID("PLAYER_UNIT_TRAIN_CANCEL")
//! runtextmacro EVENTID("PLAYER_UNIT_TRAIN_FINISH")
//! runtextmacro EVENTID("PLAYER_UNIT_RESEARCH_START")
//! runtextmacro EVENTID("PLAYER_UNIT_RESEARCH_CANCEL")
//! runtextmacro EVENTID("PLAYER_UNIT_RESEARCH_FINISH")
//! runtextmacro EVENTID("PLAYER_UNIT_ISSUED_ORDER")
//! runtextmacro EVENTID("PLAYER_UNIT_ISSUED_POINT_ORDER")
//! runtextmacro EVENTID("PLAYER_UNIT_ISSUED_TARGET_ORDER")
//! runtextmacro EVENTID("PLAYER_UNIT_ISSUED_UNIT_ORDER")
//! runtextmacro EVENTID("PLAYER_HERO_LEVEL")
//! runtextmacro EVENTID("PLAYER_HERO_SKILL")
//! runtextmacro EVENTID("PLAYER_HERO_REVIVABLE")
//! runtextmacro EVENTID("PLAYER_HERO_REVIVE_START")
//! runtextmacro EVENTID("PLAYER_HERO_REVIVE_CANCEL")
//! runtextmacro EVENTID("PLAYER_HERO_REVIVE_FINISH")
//! runtextmacro EVENTID("PLAYER_UNIT_SUMMON")
//! runtextmacro EVENTID("PLAYER_UNIT_DROP_ITEM")
//! runtextmacro EVENTID("PLAYER_UNIT_PICKUP_ITEM")
//! runtextmacro EVENTID("PLAYER_UNIT_USE_ITEM")
//! runtextmacro EVENTID("PLAYER_UNIT_LOADED")
//! runtextmacro EVENTID("UNIT_DAMAGED")
//! runtextmacro EVENTID("UNIT_DEATH")
//! runtextmacro EVENTID("UNIT_DECAY")
//! runtextmacro EVENTID("UNIT_DETECTED")
//! runtextmacro EVENTID("UNIT_HIDDEN")
//! runtextmacro EVENTID("UNIT_SELECTED")
//! runtextmacro EVENTID("UNIT_DESELECTED")
//! runtextmacro EVENTID("UNIT_STATE_LIMIT")
//! runtextmacro EVENTID("UNIT_ACQUIRED_TARGET")
//! runtextmacro EVENTID("UNIT_TARGET_IN_RANGE")
//! runtextmacro EVENTID("UNIT_ATTACKED")
//! runtextmacro EVENTID("UNIT_RESCUED")
//! runtextmacro EVENTID("UNIT_CONSTRUCT_CANCEL")
//! runtextmacro EVENTID("UNIT_CONSTRUCT_FINISH")
//! runtextmacro EVENTID("UNIT_UPGRADE_START")
//! runtextmacro EVENTID("UNIT_UPGRADE_CANCEL")
//! runtextmacro EVENTID("UNIT_UPGRADE_FINISH")
//! runtextmacro EVENTID("UNIT_TRAIN_START")
//! runtextmacro EVENTID("UNIT_TRAIN_CANCEL")
//! runtextmacro EVENTID("UNIT_TRAIN_FINISH")
//! runtextmacro EVENTID("UNIT_RESEARCH_START")
//! runtextmacro EVENTID("UNIT_RESEARCH_CANCEL")
//! runtextmacro EVENTID("UNIT_RESEARCH_FINISH")
//! runtextmacro EVENTID("UNIT_ISSUED_ORDER")
//! runtextmacro EVENTID("UNIT_ISSUED_POINT_ORDER")
//! runtextmacro EVENTID("UNIT_ISSUED_TARGET_ORDER")
//! runtextmacro EVENTID("UNIT_HERO_LEVEL")
//! runtextmacro EVENTID("UNIT_HERO_SKILL")
//! runtextmacro EVENTID("UNIT_HERO_REVIVABLE")
//! runtextmacro EVENTID("UNIT_HERO_REVIVE_START")
//! runtextmacro EVENTID("UNIT_HERO_REVIVE_CANCEL")
//! runtextmacro EVENTID("UNIT_HERO_REVIVE_FINISH")
//! runtextmacro EVENTID("UNIT_SUMMON")
//! runtextmacro EVENTID("UNIT_DROP_ITEM")
//! runtextmacro EVENTID("UNIT_PICKUP_ITEM")
//! runtextmacro EVENTID("UNIT_USE_ITEM")
//! runtextmacro EVENTID("UNIT_LOADED")
//! runtextmacro EVENTID("WIDGET_DEATH")
//! runtextmacro EVENTID("DIALOG_BUTTON_CLICK")
//! runtextmacro EVENTID("DIALOG_CLICK")
//! runtextmacro EVENTID("GAME_LOADED")
//! runtextmacro EVENTID("GAME_TOURNAMENT_FINISH_SOON")
//! runtextmacro EVENTID("GAME_TOURNAMENT_FINISH_NOW")
//! runtextmacro EVENTID("GAME_SAVE")
//! runtextmacro EVENTID("PLAYER_ARROW_LEFT_DOWN")
//! runtextmacro EVENTID("PLAYER_ARROW_LEFT_UP")
//! runtextmacro EVENTID("PLAYER_ARROW_RIGHT_DOWN")
//! runtextmacro EVENTID("PLAYER_ARROW_RIGHT_UP")
//! runtextmacro EVENTID("PLAYER_ARROW_DOWN_DOWN")
//! runtextmacro EVENTID("PLAYER_ARROW_DOWN_UP")
//! runtextmacro EVENTID("PLAYER_ARROW_UP_DOWN")
//! runtextmacro EVENTID("PLAYER_ARROW_UP_UP")
//! runtextmacro EVENTID("PLAYER_UNIT_SELL")
//! runtextmacro EVENTID("PLAYER_UNIT_CHANGE_OWNER")
//! runtextmacro EVENTID("PLAYER_UNIT_SELL_ITEM")
//! runtextmacro EVENTID("PLAYER_UNIT_SPELL_CHANNEL")
//! runtextmacro EVENTID("PLAYER_UNIT_SPELL_CAST")
//! runtextmacro EVENTID("PLAYER_UNIT_SPELL_EFFECT")
//! runtextmacro EVENTID("PLAYER_UNIT_SPELL_FINISH")
//! runtextmacro EVENTID("PLAYER_UNIT_SPELL_ENDCAST")
//! runtextmacro EVENTID("PLAYER_UNIT_PAWN_ITEM")
//! runtextmacro EVENTID("UNIT_SELL")
//! runtextmacro EVENTID("UNIT_CHANGE_OWNER")
//! runtextmacro EVENTID("UNIT_SELL_ITEM")
//! runtextmacro EVENTID("UNIT_SPELL_CHANNEL")
//! runtextmacro EVENTID("UNIT_SPELL_CAST")
//! runtextmacro EVENTID("UNIT_SPELL_EFFECT")
//! runtextmacro EVENTID("UNIT_SPELL_FINISH")
//! runtextmacro EVENTID("UNIT_SPELL_ENDCAST")
//! runtextmacro EVENTID("UNIT_PAWN_ITEM")
return null
endfunction
// -------------------------------------------------------------------------------
// getTriggerDisplayName: retrieves the trigger display name, or a default string if if does not exist.
// -------------------------------------------------------------------------------
private function getTriggerDisplayName takes trigger whichTrigger returns string
local integer iTriggerHandleId = GetHandleId(whichTrigger)
local string sTriggerName
set sTriggerName = LoadStr(pData, kDisplayName, iTriggerHandleId)
if (sTriggerName == null) then
set sTriggerName = "Unknown" + I2S(iTriggerHandleId)
endif
return sTriggerName
endfunction
// -------------------------------------------------------------------------------
// dumpTriggerParams: formats a string with the trigger context.
// -------------------------------------------------------------------------------
private function dumpTriggerContext takes nothing returns string
local trigger pTrigger = GetTriggeringTrigger()
local integer iEvalCount = GetTriggerEvalCount(pTrigger)
local integer iExecCount = GetTriggerExecCount(pTrigger)
local string sTriggerName = getTriggerDisplayName(pTrigger)
return "[" + sTriggerName + "(" + I2S(iExecCount) + "/" + I2S(iEvalCount) + ")]"
endfunction
// -------------------------------------------------------------------------------
// dumpTriggerParams: format a string with some of the trigger params.
// -------------------------------------------------------------------------------
private function dumpTriggerParams takes nothing returns string
local string sParams = ""
local eventid pEvent = GetTriggerEventId()
local unit pUnit = GetTriggerUnit()
local player pPlayer = GetTriggerPlayer()
local destructable pDestructable = GetTriggerDestructable()
local string sChatTxt = GetEventPlayerChatString()
local integer iIssuedOrder = GetIssuedOrderIdBJ()
local integer iAbility = GetSpellAbilityId()
set sParams = sParams + " |event=" + getEventName(pEvent)
if (null != pUnit) then
set sParams = sParams + "|unit=" + GetUnitName(pUnit)
if (null == pPlayer) then
set pPlayer = GetOwningPlayer(pUnit)
endif
endif
if (null != pPlayer) then
set sParams = sParams + "|player=" + GetPlayerName(pPlayer)
endif
if (null != pDestructable) then
set sParams = sParams + "|destr=" + GetDestructableName(pDestructable)
endif
if (null != sChatTxt) then
set sParams = sParams + "|chat=" + sChatTxt
endif
if (iIssuedOrder > 0) then
set sParams = sParams + "|order=" + OrderId2StringBJ(iIssuedOrder)
endif
if (iAbility > 0) then
set sParams = sParams + "|ability=" + GetAbilityName(iAbility)
endif
return sParams
endfunction
// -------------------------------------------------------------------------------
// beforeCondition: a fonction registered as first true-condition for logging.
// -------------------------------------------------------------------------------
private function beforeCondition takes nothing returns boolean
local string sDebugText = dumpTriggerContext() + " Checking conditions" + dumpTriggerParams()
call DebugLog(LVL_DEBUG, sDebugText)
//call DisplayTextToForce( GetPlayersAll(), sDebugText )
return true
endfunction
// -------------------------------------------------------------------------------
// ifConditionsTrue: a fonction registered as condition for logging when conditions are validated.
// -------------------------------------------------------------------------------
private function ifConditionsTrue takes nothing returns boolean
local string sDebugText = dumpTriggerContext() + " Conditions TRUE"
call DebugLog(LVL_DEBUG, sDebugText)
//call DisplayTextToForce( GetPlayersAll(), sDebugText )
return true
endfunction
// -------------------------------------------------------------------------------
// ifConditionsFalse: a fonction registered as condition for logging when conditions are invalidated.
// -------------------------------------------------------------------------------
private function ifConditionsFalse takes nothing returns boolean
local string sDebugText = dumpTriggerContext() + " Conditions FALSE"
call DebugLog(LVL_DEBUG, sDebugText)
//call DisplayTextToForce( GetPlayersAll(), sDebugText )
return false
endfunction
// -------------------------------------------------------------------------------
// beforeAction: a fonction registered as first action for logging.
// -------------------------------------------------------------------------------
private function beforeAction takes nothing returns nothing
local string sDebugText = dumpTriggerContext() + " Starts" + dumpTriggerParams()
call DebugLog(LVL_DEBUG, sDebugText)
//call DisplayTextToForce( GetPlayersAll(), sDebugText )
endfunction
// -------------------------------------------------------------------------------
// afterAction: a fonction registered as last action for logging.
// -------------------------------------------------------------------------------
private function afterAction takes nothing returns nothing
local string sDebugText = dumpTriggerContext() + " Done"
call DebugLog(LVL_DEBUG, sDebugText)
//call DisplayTextToForce( GetPlayersAll(), sDebugText )
endfunction
// -------------------------------------------------------------------------------
// saveTriggerDisplayName: declare a display name for trigger logs.
// -------------------------------------------------------------------------------
private function saveTriggerDisplayName takes trigger whichTrigger, string displayName returns nothing
local integer iTriggerHandleId = GetHandleId(whichTrigger)
set aRegisteredTriggers[iLastTriggerIndex] = whichTrigger
call SaveStr(pData, kDisplayName, iTriggerHandleId, displayName)
endfunction
// -------------------------------------------------------------------------------
// onTriggerAddCondition: a hook executed right before TriggerAddCondition() call, allowing to add a first true-condition for logging.
// -------------------------------------------------------------------------------
private function onTriggerAddCondition takes trigger whichTrigger, boolexpr condition returns nothing
local integer iTriggerHandleId = GetHandleId(whichTrigger)
local boolean bHasConditions = false
set bHasConditions = LoadBoolean(pData, kHasConditions, iTriggerHandleId)
if (not bHasConditions) then
// It is the first one
call SaveBoolean(pData, kHasConditions, iTriggerHandleId, true)
call SaveBooleanExprHandle(pData, kCondition, iTriggerHandleId, condition)
endif
endfunction
hook TriggerAddCondition onTriggerAddCondition
// -------------------------------------------------------------------------------
// onTriggerAddAction: a hook executed right before TriggerAddAction() call, allowing to add a first action for logging.
// -------------------------------------------------------------------------------
private function onTriggerAddAction takes trigger whichTrigger, code actionFunc returns nothing
local integer iTriggerHandleId = GetHandleId(whichTrigger)
local boolean bHasActions = false
set bHasActions = LoadBoolean(pData, kHasActions, iTriggerHandleId)
if (not bHasActions) then
// It is the first one
call SaveBoolean(pData, kHasActions, iTriggerHandleId, true)
call TriggerAddAction(whichTrigger, function beforeAction)
endif
endfunction
hook TriggerAddAction onTriggerAddAction
//private function TriggerDebugAutomationOnTime0
//endfunction
// -------------------------------------------------------------------------------
// TriggerIsReady: function to call after the trigger is fully created (actions, conditions).
// -------------------------------------------------------------------------------
public function TriggerIsReady takes trigger whichTrigger, string displayName returns nothing
local integer iTriggerHandleId = GetHandleId(whichTrigger)
local boolexpr pCondition = LoadBooleanExprHandle(pData, kCondition, iTriggerHandleId)
local boolexpr pWrappedCondition
local boolean bTriggerHasConditions
local boolean bTriggerHasActions
if (whichTrigger == null) then
call DebugLog(LVL_DEBUG, displayName + " is not a trigger, is disabled, or is not ready yet")
return
endif
// Save the display name
call saveTriggerDisplayName(whichTrigger, displayName)
set bTriggerHasConditions = false
set bTriggerHasConditions = LoadBoolean(pData, kHasConditions, iTriggerHandleId)
set bTriggerHasActions = false
set bTriggerHasActions = LoadBoolean(pData, kHasActions, iTriggerHandleId)
if (bTriggerHasConditions and bTriggerHasActions) then
// We have logs for beforeCondition, ifConditionsFalse, beforeAction, afterAction
call TriggerClearConditions(whichTrigger)
set pWrappedCondition = Or(And(function beforeCondition, pCondition), function ifConditionsFalse)
call TriggerAddCondition(whichTrigger, pWrappedCondition)
call TriggerAddAction(whichTrigger, function afterAction)
elseif (bTriggerHasConditions) then
// We have logs for beforeCondition, ifConditionsTrue, ifConditionsFalse
call TriggerClearConditions(whichTrigger)
set pWrappedCondition = Or(And(And(function beforeCondition, pCondition), function ifConditionsTrue), function ifConditionsFalse)
call TriggerAddCondition(whichTrigger, pWrappedCondition)
elseif (bTriggerHasActions) then
// We have logs for beforeAction, afterAction
call TriggerAddAction(whichTrigger, function afterAction)
endif
endfunction
// -------------------------------------------------------------------------------
// TriggerDebugAutomationOnMapInit: code executed on map init (the sooner after all GUI triggers are fully created).
// -------------------------------------------------------------------------------
function TriggerDebugAutomationOnMapInit takes nothing returns nothing
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Debug_Logger")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Debug_Log_During_Replay")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Primitive_Debug_Automation")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Trigger_Debug_Automation")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Trigger_Debug_Automation_Initializer")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Trigger_Debug_Automation_Time0")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_ForTestingPurposeOnly")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Dota_Initialization")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Framework")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Avalanche")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_CraggyExterior")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_GrabTree")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Grow")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Toss")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_GameStatus")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_MathsLib")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Logarithm")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_ArmorLib")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_GameClock_VariableCreator")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_TimeUtils")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Item_Cleanup")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ItemTimedLife")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Unit_Event_Config")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Unit_Event")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Damage_Engine_Config")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Damage_Engine")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Open_List")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Events_for_Cancel_List_ESC")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Cancel_List_ESC")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Cancel_List_Item")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_EasyItemStacknSplitGUI_config")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_EasyItemStacknSplitGUI")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_MinionsUpgrade")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Archive_LifeSteal_Configuration")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Archive_LifeSteal_Execute")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DamageReturn_Configuration")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DamageReturn_Execute")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DamageReturn_ApplyArmor")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_InvisibleBuffCorrector")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_BossRegister")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BossUnregister")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Functions")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BossRearFlankAttack")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_DefendAgainstDamage")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Initialization")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Beast_Attack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_KnockbackUnits")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Import_Instructions")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BAmr_Config")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BAmr_Cast")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BAmr_Block")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BAmr_Periodic")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_BoneArmorAOE_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BoneArmorAOE_GUI")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Scream")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CleaveFix")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_TCAFix_Config")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_TCAFix_UnitIndexed")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_TCAFix_UnitLearnsSkill")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_TCAFix_UnitDeindexed")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_EtherealFix_OnDamage")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HealFix_Unblock")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_GrabTreeFix_Unblock")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_PauseAllUnits")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_UnpauseAllUnits")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Init_sequence")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Abilities")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Alliances")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Buildings")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DarkUnitVars")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Flags")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Hashtables")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Items")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_NeutralUnitVars")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Points")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Quests")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Regions")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Times")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DarkUnits")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Music")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_NeutralUnits")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Invulnerables")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Shops")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Check")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Archive_StartGame")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_OptionsGame")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_OptionsDifficulty")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_OptionsHeroes")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_OptionsTimeOut")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Archive_ModeNormal")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Archive_ModeRandom")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ModeExtreme")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ModeEasy")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ReviveTrees")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CastleUnderAttack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_StopAttackCastle")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_MuradinHelp")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_MuradinAttacked")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BuyLevels")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BuyTomes")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CloseWay")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ExtremeLevel")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Fog")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Commands")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Host")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Info")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Kick")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_OpenWay")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Random")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Revive")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Repick")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_RepickTip")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ShortBuffAfterReincarnation")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_FullRestoration")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SuperItems")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_AutoPotionOfGreaterHealing")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_LeaderBoard")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_LeaderBoardAdd")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect1a")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect1b")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect2a")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect2b")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect3a")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect3b")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect4a")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect4b")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect5a")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect5b")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect6a")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect6b")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect7a")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect7b")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect8a")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSelect8b")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSetup")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroDead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ControlSharing")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_EasyPowerUp")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroSetupDual")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroDeadDual")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroChange")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroChangeShared")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroStats")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroLevel")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroMoveItem")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroUseItem")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroAcquireItem")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroLoseItem")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_DualHeroCooldown")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_BoostLevel")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_EnableHeroAbilities")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_EnableItemAbilities")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Level20Abilities")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Armor")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ArtifactCollector")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_AttributeBonusPerLevel")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_AuraOfBlight")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Bomb")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CooldownsReduction")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CriticalStrikeAura")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CursedClaws")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DamageDealtMalus")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DamageTakenMalus")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DeathCoil")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DebilitationAura")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Doom")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Earthquake")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_EarthquakeStop")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Ensnare")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_FanOfKnives_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_FanOfKnives")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_FrostParalysisAura")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Hex_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Hex")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HolyLight")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Immolation")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ImmolationGhost")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_InnerFire")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_KnightsChallenge")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_LifeRegenerationAura")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_LightFrenzy")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_LightningAttackOnOff")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_MagicDefenseOnOff")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_MorphingCopyStats")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_OwlScout")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_PermanentLightning")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_Polymorph_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Polymorph")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_RefreshCooldownChance")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_RuneClockCreate")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_RuneClockDestroy")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_RunicWeaponCreate")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_ShadowStrike_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ShadowStrike")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_ShockWave_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ShockWave")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpiritPig")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpiritualPresence")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_StormBolt_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_StormBolt")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_ThunderClap_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ThunderClap")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_TrueTank")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WardsMastery")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_WarStomp_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WarStomp")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Whirlwind")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_AttackStart")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CreepsKills100")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CreepsKills500")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CreepsKills1000")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_CreepsKills2000")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_AttackCome")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_AttackCheck")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_AttackTower")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_AttackNecropolis")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_ExtremePowerUp")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_DragonAttack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_HeroAttack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_LightningAttack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SilentAssassin")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Rune")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_GameLevel1")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WarnBeforeGameLevel2")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_GameLevel2")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_GameLevel3")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_GameLevel4")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WaveTimer")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WaveAttack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WaveKills20")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WaveKills40")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WaveKills60")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_WaveKills80")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_FinalWaveCheckIfReady")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_FinalWaveStart")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Survival1CheckIfReady")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Survival1Start")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Survival1Attack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Survival2Start")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Survival2Attack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Survival3Start")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Survival3Attack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Teleport")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss1Teleport")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss1Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss2Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss2Leave")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss3Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss4Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss5Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss6Come")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss6Teleport")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss6Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss7Come")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss7Phases")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss7Auras")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Boss7Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special4Agree")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special4Won")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special4Failed")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special5Agree")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special5EnableAbilities")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special5Won")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special5Failed")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8Timer")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8Start")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8Attack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8Stop")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8Teleport")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8Leave")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8AntiHack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special8Move")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_MuradinClap_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_MuradinClap")
////! runtextmacro TRIGGER_IS_READY ("gg_trg_MuradinBolt_JASS")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_MuradinBolt")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special1Timer")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special1Start")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special1Attack")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special1Corpse")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special1Stop")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special1Dead")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special2Start")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special2Stop")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special2Won")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special2TimeOut")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special2Teleport")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special3Start")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special3Stop")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special3Count")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special3Won")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special3TimeOut")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special3Teleport")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special6Agree")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Special7Agree")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpecialXDialog")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpecialXStart")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpecialXDisagree")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpecialXRevive")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpecialXTeleport")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpecialXPauseHeroImage")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpecialXResumeHeroImage")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_SpecialXCancel")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Victory")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_YouLose")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_EndGame")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_Disconnect")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_RemovePlayer")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_EndPitch")
endfunction
// -------------------------------------------------------------------------------
// TriggerDebugAutomationOnTime0: code executed after 0seconds elapsed (the sooner after all library init are fully done).
// -------------------------------------------------------------------------------
function TriggerDebugAutomationOnTime0 takes nothing returns nothing
//! runtextmacro TRIGGER_IS_READY ("gg_trg_EasyItemStacknSplit")
//! runtextmacro TRIGGER_IS_READY ("gg_trg_RealmOfSoul")
//! runtextmacro TRIGGER_IS_READY ("udg_MURefreshTrigger")
//! runtextmacro TRIGGER_IS_READY ("udg_TCAFixUnitAttackedTrigger")
//! runtextmacro TRIGGER_IS_READY ("udg_TCAFixResumeHoldPosTrigger")
endfunction
// ----------------------------------------------------------------------------
// onInit: system initializer
// ----------------------------------------------------------------------------
private module M
private static method onInit takes nothing returns nothing
//local trigger pTime0Trigger = CreateTrigger()
set pData = InitHashtable()
//call TriggerRegisterTimerEventSingle( pTime0Trigger, 0.00 )
//call TriggerAddAction(pTime0Trigger , function TriggerDebugAutomationOnTime0)
endmethod
endmodule
private struct S extends array
implement M
endstruct
endlibrary
// -------------------------------------------------------------------------------
// Conditions - begin ------------------------------------------------------------
function AlwaysTrueCondition takes nothing returns boolean
return true
endfunction
function IsUnitUnknownOrDead takes unit loc_unit01 returns boolean
return GetUnitTypeId(loc_unit01)<1 or IsUnitType(loc_unit01,UNIT_TYPE_DEAD)==true
endfunction
function IsCourrierType takes unit loc_unit01 returns boolean
//return GetUnitTypeId(loc_unit01)=='n00I' or GetUnitTypeId(loc_unit01)=='n022' or GetUnitTypeId(loc_unit01)=='n021' or GetUnitTypeId(loc_unit01)=='n023' or GetUnitTypeId(loc_unit01)=='n024' or GetUnitTypeId(loc_unit01)=='n025' or GetUnitTypeId(loc_unit01)=='e01H' or GetUnitTypeId(loc_unit01)=='e01Z' or GetUnitTypeId(loc_unit01)=='n00M' or GetUnitTypeId(loc_unit01)=='n0HV'
return false
endfunction
function IsInvalidTargetType takes unit loc_unit01 returns boolean
//local integer loc_integer01=GetUnitTypeId(loc_unit01)
//return loc_integer01=='n004' or loc_integer01=='n01G' or loc_integer01=='n01C' or loc_integer01=='n018' or loc_integer01=='e00K' or loc_integer01=='e00I' or loc_integer01=='e00L' or loc_integer01=='e01J'
return false
endfunction
// TODO : update when trees are added
function IsTreeDestructableType takes destructable loc_destructable01 returns boolean
//return GetDestructableTypeId(loc_destructable01)=='NTtc' or GetDestructableTypeId(loc_destructable01)=='NTtw' or GetDestructableTypeId(loc_destructable01)=='ATtr' or GetDestructableTypeId(loc_destructable01)=='B002' or GetDestructableTypeId(loc_destructable01)=='B003' or GetDestructableTypeId(loc_destructable01)=='B005'
return false
endfunction
function TargetFilterNoAncients takes nothing returns boolean
return IsUnitEnemy(udg_TempTossProjectedUnit,GetOwningPlayer(GetFilterUnit()))and(GetUnitAbilityLevel(GetFilterUnit(),'A069')==0 and IsUnitUnknownOrDead(GetFilterUnit())==false)and(IsUnitType(GetFilterUnit(),UNIT_TYPE_ANCIENT)==false or IsInvalidTargetType(GetFilterUnit()))
endfunction
function IsUnitFlying takes unit loc_unit01 returns boolean
//return GetUnitTypeId(loc_unit01)=='H00F' or GetUnitTypeId(loc_unit01)=='H00E' or GetUnitTypeId(loc_unit01)=='H00G' or GetUnitTypeId(loc_unit01)=='O017'
return false
endfunction
// Conditions - end --------------------------------------------------------------
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// Maths - begin -----------------------------------------------------------------
function MathsAtan2 takes real loc_real01,real loc_real02,real loc_real03,real loc_real04 returns real
return bj_RADTODEG*Atan2(loc_real04-loc_real02,loc_real03-loc_real01)
endfunction
// Coord X1, Coord Y1, Coord X2, Coord Y2
function DistanceBetweenPointsByCoord takes real loc_real01,real loc_real02,real loc_real03,real loc_real04 returns real
return SquareRoot(((loc_real01-loc_real03)*(loc_real01-loc_real03))+((loc_real02-loc_real04)*(loc_real02-loc_real04)))
endfunction
function DistanceBetweenUnits takes unit loc_unit01,unit loc_unit02 returns real
local real loc_real01=GetUnitX(loc_unit01)
local real loc_real02=GetUnitY(loc_unit01)
local real loc_real03=GetUnitX(loc_unit02)
local real loc_real04=GetUnitY(loc_unit02)
if loc_unit01==null or loc_unit02==null then
return I2R(2147483648)
else
return SquareRoot((loc_real01-loc_real03)*(loc_real01-loc_real03)+(loc_real02-loc_real04)*(loc_real02-loc_real04))
endif
return 1.0
endfunction
// Maths - end -------------------------------------------------------------------
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// Actions - begin ---------------------------------------------------------------
function TriggerRegisterAllPlayersUnitEvent takes trigger loc_trigger01,playerunitevent loc_playerunitevent01 returns nothing
local integer loc_integer01=0
loop
call TriggerRegisterPlayerUnitEvent(loc_trigger01,Player(loc_integer01),loc_playerunitevent01,Condition(function AlwaysTrueCondition))
set loc_integer01=loc_integer01+1
exitwhen loc_integer01==12
endloop
endfunction
function DamageUnit takes unit loc_unit01,unit loc_unit02,integer loc_integer01,real loc_real01 returns nothing
if loc_integer01==0 then
return
endif
if loc_integer01==1 then
call UnitDamageTarget(loc_unit01,loc_unit02,loc_real01,true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,WEAPON_TYPE_WHOKNOWS)
elseif loc_integer01==2 then
call UnitDamageTarget(loc_unit01,loc_unit02,loc_real01,true,true,ATTACK_TYPE_HERO,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
elseif loc_integer01==3 then
call UnitDamageTarget(loc_unit01,loc_unit02,loc_real01,true,true,ATTACK_TYPE_HERO,DAMAGE_TYPE_MAGIC,WEAPON_TYPE_WHOKNOWS)
elseif loc_integer01==4 then
call UnitDamageTarget(loc_unit01,loc_unit02,loc_real01,true,true,ATTACK_TYPE_PIERCE,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
endif
endfunction
function DisplayError takes player loc_player01,string loc_string01 returns nothing
local sound loc_sound01=CreateSoundFromLabel("InterfaceError",false,false,false,10,10)
if(GetLocalPlayer()==loc_player01)then
call StartSound(loc_sound01)
endif
if(loc_string01!="")and(loc_string01!=null)then
call ClearTextMessages()
call DisplayTimedTextToPlayer(loc_player01,0.50,-1.00,2.00,"|cffffcc00"+loc_string01+"|r")
endif
call KillSoundWhenDone(loc_sound01)
endfunction
function DestroyTreeDestructable takes nothing returns nothing
if IsTreeDestructableType(GetEnumDestructable())then
call KillDestructable(GetEnumDestructable())
endif
endfunction
// Coord X, Coord Y, HalfRangeX, HalfRangeY
function DestroyTreeDestructablesInRegion takes real loc_real01,real loc_real02,real loc_real03 returns nothing
local rect loc_rect01=Rect(loc_real01-loc_real03,loc_real02-loc_real03,loc_real01+loc_real03,loc_real02+loc_real03)
call EnumDestructablesInRectAll(loc_rect01,function DestroyTreeDestructable)
call RemoveRect(loc_rect01)
set loc_rect01=null
endfunction
function StopUnitSilent takes unit loc_unit01 returns nothing
call PauseUnit(loc_unit01,true)
call IssueImmediateOrder(loc_unit01,"stop")
call PauseUnit(loc_unit01,false)
endfunction
function UnitAddPermanentAbility takes unit loc_unit01,integer loc_integer01 returns nothing
call UnitAddAbility(loc_unit01,loc_integer01)
call UnitMakeAbilityPermanent(loc_unit01,true,loc_integer01)
endfunction
function SetAllPlayersAbilityAvailable takes integer loc_integer01 returns nothing
local integer loc_integer02=0
loop
exitwhen loc_integer02>8
call SetPlayerAbilityAvailable(Player(loc_integer02),loc_integer01,false)
set loc_integer02=loc_integer02+1
endloop
endfunction
function SaveUnitInCacheFinish takes nothing returns boolean
local trigger loc_trigger01=GetTriggeringTrigger()
local integer loc_integer01=GetHandleId(loc_trigger01)
call DebugLog(LVL_DEBUG, "SaveUnitInCacheFinish")
call SaveInteger(udg_DotaHashtable,(GetHandleId(((LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(14)))))),(((LoadInteger(udg_DotaHashtable,(loc_integer01),(33))))),(2))
call FlushChildHashtable(udg_DotaHashtable,(loc_integer01))
call DisableTrigger(loc_trigger01)
set loc_trigger01=null
return false
endfunction
function SaveUnitInCacheWithDelay takes unit loc_unit01,integer loc_integer01,real loc_real01 returns nothing
local trigger loc_trigger01=CreateTrigger()
call TriggerAddCondition(loc_trigger01,Condition(function SaveUnitInCacheFinish))
call TriggerRegisterTimerEvent(loc_trigger01,loc_real01,false)
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "SaveUnitInCacheWithDelay_" + I2S(loc_integer01))
call SaveInteger(udg_DotaHashtable,(GetHandleId((loc_unit01))),((loc_integer01)),(1))
call SaveUnitHandle(udg_DotaHashtable,(GetHandleId(loc_trigger01)),(14),(loc_unit01))
call SaveInteger(udg_DotaHashtable,(GetHandleId(loc_trigger01)),(33),(loc_integer01))
set loc_trigger01=null
endfunction
// Actions - end -----------------------------------------------------------------
// -------------------------------------------------------------------------------
function AvalancheAdditionalDamageTossedUnit takes nothing returns boolean
if((LoadInteger(udg_DotaHashtable,(GetHandleId((GetFilterUnit()))),((4268))))==1)and IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(udg_TCAAvalancheCaster))==true then
call DamageUnit(udg_TCAAvalancheCaster,GetFilterUnit(),1,udg_TempAvalancheDamages)
endif
return false
endfunction
function AvalancheAdditionalDamageTossedUnitPeriodic takes nothing returns boolean
local trigger loc_trigger01=GetTriggeringTrigger()
local integer loc_integer01=GetHandleId(loc_trigger01)
local unit loc_unit01=(LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(14)))
local real loc_real01=(LoadReal(udg_DotaHashtable,(loc_integer01),(6)))
local real loc_real02=(LoadReal(udg_DotaHashtable,(loc_integer01),(7)))
local unit loc_unit02=CreateUnit(GetOwningPlayer(loc_unit01),'n00E',loc_real01,loc_real02,0)
local integer loc_integer02=GetUnitAbilityLevel(loc_unit01,'A06E')
local group loc_group01=CreateGroup()
call DebugLog(LVL_DEBUG, "AvalancheAdditionalDamageTossedUnitPeriodic")
call UnitApplyTimedLifeBJ(5.00, 'BTLF', loc_unit02)
call UnitAddAbility(loc_unit02,'A06F')
set udg_TCAAvalancheCaster=loc_unit01
if loc_integer02==1 then
set udg_TempAvalancheDamages=75
elseif loc_integer02==2 then
set udg_TempAvalancheDamages=100
elseif loc_integer02==3 then
set udg_TempAvalancheDamages=210
elseif loc_integer02==4 then
set udg_TempAvalancheDamages=230
endif
call SetUnitAbilityLevel(loc_unit02,'A06F',loc_integer02)
call GroupEnumUnitsInRange(loc_group01,loc_real01,loc_real02,299,Condition(function AvalancheAdditionalDamageTossedUnit))
call DestroyGroup(loc_group01)
if GetTriggerEvalCount(loc_trigger01)>6 then
call FlushChildHashtable(udg_DotaHashtable,(loc_integer01))
call DisableTrigger(loc_trigger01)
endif
set loc_trigger01=null
set loc_unit01=null
set loc_unit02=null
set loc_group01=null
return false
endfunction
function AvalancheEffect takes nothing returns nothing
local unit loc_unit01=GetTriggerUnit()
local trigger loc_trigger01=CreateTrigger()
local integer loc_integer01=GetHandleId(loc_trigger01)
local location loc_location01=GetSpellTargetLoc()
local unit loc_unit02=CreateUnit(GetOwningPlayer(loc_unit01),'n00E',GetUnitX(loc_unit01),GetUnitY(loc_unit01),0)
local integer loc_integer02=GetUnitAbilityLevel(loc_unit01,'A06E')
call DebugLog(LVL_DEBUG, "AvalancheEffect")
call UnitApplyTimedLifeBJ(5.00, 'BTLF', loc_unit02)
call UnitAddAbility(loc_unit02,'A06H')
call SetUnitAbilityLevel(loc_unit02,'A06H',loc_integer02)
call IssuePointOrder(loc_unit02,"clusterrockets",GetLocationX(loc_location01),GetLocationY(loc_location01))
call TriggerRegisterTimerEvent(loc_trigger01,0.25,true)
call TriggerAddCondition(loc_trigger01,Condition(function AvalancheAdditionalDamageTossedUnitPeriodic))
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "AvalancheEffect")
call SaveUnitHandle(udg_DotaHashtable,(loc_integer01),(14),(loc_unit01))
call SaveReal(udg_DotaHashtable,(loc_integer01),(6),((GetLocationX(loc_location01))*1.0))
call SaveReal(udg_DotaHashtable,(loc_integer01),(7),((GetLocationY(loc_location01))*1.0))
call TriggerEvaluate(loc_trigger01)
call RemoveLocation(loc_location01)
set loc_unit01=null
set loc_location01=null
set loc_trigger01=null
set loc_unit02=null
endfunction
function AvalancheSkill takes nothing returns boolean
if GetSpellAbilityId()=='A06E' then
call DebugLog(LVL_DEBUG, "SpellEffect - Avalanche")
call AvalancheEffect()
endif
return false
endfunction
function AvalancheEffectRegister takes nothing returns nothing
local trigger loc_trigger01=CreateTrigger()
call TriggerRegisterAllPlayersUnitEvent(loc_trigger01,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(loc_trigger01,Condition(function AvalancheSkill))
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "AvalancheEffectRegister")
set loc_trigger01=null
endfunction
function CraggyExteriorEffect takes nothing returns boolean
local unit loc_unit01=GetTriggerUnit()
local unit loc_unit02=GetAttacker()
local integer loc_integer01=GetUnitAbilityLevel(loc_unit01,'A066')
local unit loc_unit03
call DebugLog(LVL_DEBUG, "CraggyExteriorEffect")
if GetRandomInt(1,100)<=loc_integer01*6 and DistanceBetweenUnits(loc_unit01,loc_unit02)<325 then
set loc_unit03=CreateUnit(GetOwningPlayer(loc_unit01),'n00E',GetUnitX(loc_unit02),GetUnitY(loc_unit02),0)
call UnitApplyTimedLifeBJ(1.50, 'BTLF', loc_unit03)
call UnitAddAbility(loc_unit03,'A067')
call SetUnitAbilityLevel(loc_unit03,'A067',loc_integer01)
call IssueTargetOrder(loc_unit03,"thunderbolt",loc_unit02)
set loc_unit03=null
endif
set loc_unit01=null
set loc_unit02=null
return false
endfunction
function CraggyExteriorEffectRegister takes nothing returns nothing
local trigger loc_trigger01=CreateTrigger()
local unit loc_unit01=GetTriggerUnit()
call TriggerRegisterUnitEvent(loc_trigger01,loc_unit01,EVENT_UNIT_ATTACKED)
call TriggerAddCondition(loc_trigger01,Condition(function CraggyExteriorEffect))
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "CraggyExteriorEffectRegister")
set loc_trigger01=null
set loc_unit01=null
endfunction
function CraggyExteriorSkill takes nothing returns boolean
if GetLearnedSkill()=='A066' and GetUnitAbilityLevel(GetTriggerUnit(),'A066')==1 and IsUnitIllusion(GetTriggerUnit())==false then
call DebugLog(LVL_DEBUG, "CraggyExteriorSkill")
call CraggyExteriorEffectRegister()
endif
return false
endfunction
function CraggyExteriorSkillRegister takes nothing returns nothing
local trigger loc_trigger01=CreateTrigger()
call TriggerRegisterAllPlayersUnitEvent(loc_trigger01,EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(loc_trigger01,Condition(function CraggyExteriorSkill))
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "CraggyExteriorSkillRegister")
set loc_trigger01=null
endfunction
function SetAllPlayerGrabTreeAvailable takes nothing returns nothing
local unit loc_unit01=GetTriggerUnit()
call SetAllPlayersAbilityAvailable('A2KK')
endfunction
function GrabTreeCountSuccessfulAttacks takes nothing returns boolean
local trigger loc_trigger01=GetTriggeringTrigger()
local integer loc_integer01=GetHandleId(loc_trigger01)
local integer loc_integer02=(LoadInteger(udg_DotaHashtable,(loc_integer01),(375)))
local unit loc_unit01=(LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(2)))
local integer loc_integer03
if GetTriggerEventId()==EVENT_UNIT_DAMAGED then
if GetEventDamageSource()==loc_unit01 and GetEventDamage()>20 then
set loc_integer03=(LoadInteger(udg_DotaHashtable,(GetHandleId(loc_unit01)),(674))) // Get number of attacks already made
if((LoadInteger(udg_DotaHashtable,(GetHandleId((loc_unit01))),((4297))))==1)==false then
call SaveUnitInCacheWithDelay(loc_unit01,4297,0.1)
call SaveInteger(udg_DotaHashtable,(GetHandleId(loc_unit01)),(674),(loc_integer03+1)) // Save number or attacks made
endif
call FlushChildHashtable(udg_DotaHashtable,(loc_integer01))
call DestroyTrigger(loc_trigger01)
endif
else
call FlushChildHashtable(udg_DotaHashtable,(loc_integer01))
call DestroyTrigger(loc_trigger01)
endif
set loc_trigger01=null
set loc_unit01=null
return false
endfunction
function GrabTreeAttackEffect takes nothing returns boolean
local trigger loc_trigger01=GetTriggeringTrigger()
local trigger loc_trigger02
local integer loc_integer01=GetHandleId(loc_trigger01)
local unit loc_unit01=(LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(2)))
local integer loc_integer02=(LoadInteger(udg_DotaHashtable,(GetHandleId(loc_unit01)),(674)))
call DebugLog(LVL_DEBUG, "GrabTreeAttackEffect")
if GetTriggerEventId()==EVENT_PLAYER_UNIT_ATTACKED and loc_integer02<30 then
// A unit is attacked and the max attack count is not reached yet
if GetAttacker()==loc_unit01 and((LoadInteger(udg_DotaHashtable,(GetHandleId((loc_unit01))),((4297))))==1)==false then
// Attacker is Tiny && SaveUnitInCacheWithDelay is finshed
set loc_trigger02=CreateTrigger()
call TriggerRegisterUnitEvent(loc_trigger02,GetTriggerUnit(),EVENT_UNIT_DAMAGED)
call TriggerRegisterTimerEvent(loc_trigger02,1.00,false)
call TriggerAddCondition(loc_trigger02,Condition(function GrabTreeCountSuccessfulAttacks))
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger02, "GrabTreeAttackEffect")
call SaveUnitHandle(udg_DotaHashtable,(GetHandleId(loc_trigger02)),(2),(loc_unit01))
endif
endif
if loc_integer02>29 then
// The max attack count is about to be reached
call SaveInteger(udg_DotaHashtable,(GetHandleId(GetTriggerUnit())),(674),(0))
call UnitRemoveAbility(loc_unit01,'A06M')
if(LoadInteger(udg_DotaHashtable,(loc_integer01),(34)))==1 then
call FlushChildHashtable(udg_DotaHashtable,(loc_integer01))
call DestroyTrigger(loc_trigger01)
endif
endif
if GetTriggerEventId()!=EVENT_PLAYER_UNIT_ATTACKED then
// A unit is attacked
call SaveInteger(udg_DotaHashtable,(loc_integer01),(34),(1))
if loc_integer02>29 then
// The max attack count is about to be reached
call FlushChildHashtable(udg_DotaHashtable,(loc_integer01))
call DestroyTrigger(loc_trigger01)
endif
call UnitRemoveAbility(loc_unit01,'A06K')
call SetPlayerTechResearched(GetOwningPlayer(loc_unit01),'R002',0)
//if(Func0367((loc_unit01),integers084[integer355])!=null)then
if (GetHeroLevel(loc_unit01)>=20)then
// Unit carries Tiny's aghanim scepter
call UnitAddPermanentAbility(loc_unit01,'A06L')
endif
call SetPlayerAbilityAvailable(GetOwningPlayer(loc_unit01),'A06L',true)
endif
set loc_trigger01=null
set loc_unit01=null
return false
endfunction
//Dissused
function Func2388 takes nothing returns boolean
local trigger loc_trigger01=GetTriggeringTrigger()
local integer loc_integer01=GetHandleId(loc_trigger01)
local unit loc_unit01=(LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(2)))
if GetTriggerEvalCount(loc_trigger01)==1 then
call SetUnitTimeScalePercent(loc_unit01,250)
else
call SetUnitTimeScalePercent(loc_unit01,100)
call SetUnitAnimationByIndex(loc_unit01,12)
call FlushChildHashtable(udg_DotaHashtable,(loc_integer01))
call DestroyTrigger(loc_trigger01)
endif
set loc_trigger01=null
return false
endfunction
function GrabTreeEffectRegister takes nothing returns boolean
local trigger loc_trigger01
call DebugLog(LVL_DEBUG, "GrabTreeEffectRegister")
if GetSpellAbilityId()=='A06L' then
// Tiny is casting GrabTree - Current version
call SaveUnitInCacheWithDelay(GetTriggerUnit(),4296,29.9)
call SetPlayerAbilityAvailable(GetOwningPlayer(GetTriggerUnit()),'A06L',false) // Remove dummy grap tree ability
call SetPlayerAbilityAvailable(GetOwningPlayer(GetTriggerUnit()),'A06K',true)
call UnitAddPermanentAbility(GetTriggerUnit(),'A06K') // Add the real grab tree ability
call SetPlayerTechResearched(GetOwningPlayer(GetTriggerUnit()),'R002',1)
call SetPlayerAbilityAvailable(GetOwningPlayer(GetTriggerUnit()),'A06M',false) // Disable to hide the spellbook
call UnitAddPermanentAbility(GetTriggerUnit(),'A06M')
call IssueTargetOrder(GetTriggerUnit(),"grabtree",GetSpellTargetDestructable()) // Cast the real grab tree ability
set loc_trigger01=CreateTrigger()
call TriggerRegisterTimerEvent(loc_trigger01,30,false)
call TriggerRegisterAllPlayersUnitEvent(loc_trigger01,EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(loc_trigger01,Condition(function GrabTreeAttackEffect))
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "GrabTreeEffectRegister")
call SaveUnitHandle(udg_DotaHashtable,(GetHandleId(loc_trigger01)),(2),(GetTriggerUnit()))
call SaveInteger(udg_DotaHashtable,(GetHandleId(loc_trigger01)),(34),(0))
call SaveInteger(udg_DotaHashtable,(GetHandleId(GetTriggerUnit())),(674),(0))
set loc_trigger01=null
elseif GetSpellAbilityId()=='A06C' then
// Tiny is casting GrabTree - Old version
call RemoveDestructable(GetSpellTargetDestructable())
call ShowUnit(GetTriggerUnit(),false)
call ShowUnit(GetTriggerUnit(),true)
call UnitRemoveAbility(GetTriggerUnit(),'Aloc')
call UnitAddAbility(GetTriggerUnit(),'A06D')
call UnitRemoveAbility(GetTriggerUnit(),'A06D')
call UnitAddAbility(GetTriggerUnit(),'A06I')
call UnitRemoveAbility(GetTriggerUnit(),'A06I')
call AddUnitAnimationProperties(GetTriggerUnit(),"upgrade",false)
endif
return false
endfunction
//Dissused
function Func2390 takes nothing returns boolean
local trigger loc_trigger01=GetTriggeringTrigger()
local integer loc_integer01=GetHandleId(loc_trigger01)
local unit loc_unit01=(LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(2)))
//if(Func0367((loc_unit01),integers084[integer355])!=null)then
if(GetHeroLevel(loc_unit01)>=20)then
// Unit carries Tiny's aghanim scepter
if((LoadInteger(udg_DotaHashtable,(GetHandleId((loc_unit01))),((4296))))==1)==false then
if GetUnitAbilityLevel(loc_unit01,'A06L')==0 and GetUnitAbilityLevel(loc_unit01,'A06K')==0 then
call SetPlayerAbilityAvailable(GetOwningPlayer(loc_unit01),'A06L',true)
call UnitAddPermanentAbility(loc_unit01,'A06L')
call SetPlayerTechResearched(GetOwningPlayer(loc_unit01),'R002',0)
endif
endif
else
call UnitRemoveAbility(loc_unit01,'A06L')
call UnitRemoveAbility(loc_unit01,'A06K')
call SetPlayerTechResearched(GetOwningPlayer(loc_unit01),'R002',0)
endif
set loc_trigger01=null
return false
endfunction
// New function
function GrapTreeSkillRegister takes nothing returns nothing
local trigger loc_trigger01=CreateTrigger()
call TriggerRegisterAllPlayersUnitEvent(loc_trigger01,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(loc_trigger01,Condition(function GrabTreeEffectRegister))
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "GrabTreeSkillRegister")
set loc_trigger01=null
endfunction
function GrowSkillLearnConditions takes nothing returns boolean
return GetLearnedSkill()=='A068' and IsUnitIllusion(GetTriggerUnit())==false
endfunction
function GrowUpdateScaleLoop takes nothing returns nothing
local unit loc_unit01=(LoadUnitHandle(udg_DotaHashtable,(GetHandleId(GetTriggeringTrigger())),(298)))
local integer loc_integer01=GetUnitAbilityLevel(loc_unit01,'A068')
if GetUnitState(loc_unit01,UNIT_STATE_LIFE)>1 then
call SetUnitScale(loc_unit01,0.5+0.25*loc_integer01,0.5+0.25*loc_integer01,0.5+0.25*loc_integer01)
endif
endfunction
function GrowUpdateScaleEnterRegion takes nothing returns boolean
local integer loc_integer01
if IsUnitIllusion(GetTriggerUnit())and GetUnitTypeId(GetTriggerUnit())=='U00B' then
set loc_integer01=GetUnitAbilityLevel((LoadUnitHandle(udg_DotaHashtable,(GetHandleId(GetTriggeringTrigger())),(298))),'A068')
call SetUnitScale(GetTriggerUnit(),0.5+0.25*loc_integer01,0.5+0.25*loc_integer01,0.5+0.25*loc_integer01)
endif
return false
endfunction
function GrowSkillLearn takes nothing returns nothing
local trigger loc_trigger01
local unit loc_unit01=GetTriggerUnit()
local integer loc_integer01=GetUnitAbilityLevel(loc_unit01,'A068')
local unit loc_unit03=GetTriggerUnit()
local region loc_region01
call DebugLog(LVL_DEBUG, "GrowSkill")
call SetPlayerTechResearched(GetOwningPlayer(loc_unit01),'R001',loc_integer01)
if loc_integer01==1 then
set loc_trigger01=CreateTrigger()
call TriggerAddAction(loc_trigger01,function GrowUpdateScaleLoop)
call TriggerRegisterTimerEvent(loc_trigger01,2.00,true)
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "GrowSkillLearn")
call SaveUnitHandle(udg_DotaHashtable,(GetHandleId(loc_trigger01)),(298),(loc_unit01))
call SetUnitScale(loc_unit01,0.5+0.25*loc_integer01,0.5+0.25*loc_integer01,0.5+0.25*loc_integer01)
set loc_trigger01=CreateTrigger()
call TriggerRegisterEnterRectSimple( loc_trigger01, GetEntireMapRect() )
call TriggerAddCondition(loc_trigger01,Condition(function GrowUpdateScaleEnterRegion))
call SaveUnitHandle(udg_DotaHashtable,(GetHandleId(loc_trigger01)),(298),(loc_unit01))
set loc_trigger01=null
endif
set loc_unit03=null
endfunction
function GrowSkillRegister takes nothing returns nothing
local trigger loc_trigger01=CreateTrigger()
call TriggerRegisterAllPlayersUnitEvent(loc_trigger01,EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(loc_trigger01,Condition(function GrowSkillLearnConditions))
call TriggerAddAction(loc_trigger01,function GrowSkillLearn)
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "GrowSkillRegister")
//set loc_trigger01=CreateTrigger()
//call TriggerRegisterAllPlayersUnitEvent(loc_trigger01,EVENT_PLAYER_UNIT_SPELL_EFFECT)
//call TriggerAddCondition(loc_trigger01,Condition(function GrabTreeEffectRegister))
//call SetAllPlayerGrabTreeAvailable()
set loc_trigger01=null
endfunction
function TossDamageUnit takes nothing returns nothing
if IsUnitType(GetEnumUnit(),UNIT_TYPE_STRUCTURE)==true then
call DamageUnit(udg_TempTossCaster,GetEnumUnit(),1,udg_TempTossDamages/3)
else
call DamageUnit(udg_TempTossCaster,GetEnumUnit(),1,udg_TempTossDamages)
endif
endfunction
function TossAreaDamage takes unit loc_unit01,real loc_real01,real loc_real02,real loc_real03,real loc_real04 returns nothing
local group loc_group01=CreateGroup()
set udg_TempTossProjectedUnit=loc_unit01
call GroupEnumUnitsInRange(loc_group01,loc_real01,loc_real02,loc_real03,Condition(function TargetFilterNoAncients))
set udg_TempTossCaster=loc_unit01
set udg_TempTossDamages=loc_real04
call ForGroup(loc_group01,function TossDamageUnit)
call DestroyGroup(loc_group01)
set loc_group01=null
endfunction
function TossFilterProjectableUnits takes nothing returns boolean
if((GetUnitAbilityLevel(GetFilterUnit(),'A069')==0 and IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)==false and GetUnitDefaultMoveSpeed(GetFilterUnit())>0.00 and IsUnitUnknownOrDead(GetFilterUnit())==false)and IsUnitVisible(GetFilterUnit(),GetOwningPlayer(GetTriggerUnit())))and IsCourrierType(GetFilterUnit())==false and(IsUnitType(GetFilterUnit(),UNIT_TYPE_ANCIENT)==false or IsInvalidTargetType(GetFilterUnit()))then
return true
endif
return false
endfunction
function TossSelectProjectUnit takes unit loc_unit01 returns unit
local unit loc_unit02=null
local group loc_group01=CreateGroup()
call GroupEnumUnitsInRange(loc_group01,GetUnitX(loc_unit01),GetUnitY(loc_unit01),275,Condition(function TossFilterProjectableUnits))
call GroupRemoveUnit(loc_group01,loc_unit01)
set loc_unit02=GroupPickRandomUnit(loc_group01)
call DestroyGroup(loc_group01)
set udg_TempTossProjectedUnit=loc_unit02
set loc_unit02=null
set loc_group01=null
return udg_TempTossProjectedUnit
endfunction
function TossProjectedUnitFlightPeriodic takes nothing returns boolean
local trigger loc_trigger01=GetTriggeringTrigger()
local integer loc_integer01=GetHandleId(loc_trigger01)
local integer loc_integer02=GetTriggerEvalCount(loc_trigger01)
local unit loc_unit01=(LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(14))) // Caster unit
local unit loc_unit02=(LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(2))) // Projected unit
//local integer loc_integer03=(LoadInteger(udg_DotaHashtable,(loc_integer01),(30)))
//local unit loc_unit03=Func0022(loc_integer03)
local unit loc_unit03=(LoadUnitHandle(udg_DotaHashtable,(loc_integer01),(30))) // Target unit
local real loc_real01=(LoadReal(udg_DotaHashtable,(loc_integer01),(282))) // Target X
local real loc_real02=(LoadReal(udg_DotaHashtable,(loc_integer01),(283))) // Target Y
local real loc_real03=GetUnitX(loc_unit02)
local real loc_real04=GetUnitY(loc_unit02)
local real loc_real05=GetUnitX(loc_unit03)
local real loc_real06=GetUnitY(loc_unit03)
local real loc_real07
local real loc_real08
local real loc_real09
local real loc_real10
local real loc_real11
local real loc_real12
local location loc_location01
call DebugLog(LVL_DEBUG, "TossProjectedUnitFlightPeriodic")
if DistanceBetweenPointsByCoord(loc_real01,loc_real02,loc_real05,loc_real06)>1000 then
set loc_real05=loc_real01
set loc_real06=loc_real02
endif
set loc_real07=MathsAtan2(loc_real03,loc_real04,loc_real05,loc_real06)
set loc_real08=DistanceBetweenPointsByCoord(loc_real03,loc_real04,loc_real05,loc_real06)
set loc_real09=loc_real08/IMaxBJ((51-loc_integer02),1)
set loc_real10=(loc_integer02-25)*(loc_integer02-25)
set loc_real11=loc_real03+loc_real09*Cos(loc_real07*bj_DEGTORAD)
set loc_real12=loc_real04+loc_real09*Sin(loc_real07*bj_DEGTORAD)
if loc_integer02<51 then
if IsUnitFlying(loc_unit02)==false then
call SetUnitFlyHeight(loc_unit02,775-loc_real10,0)
endif
call SetUnitPosition(loc_unit02,loc_real11,loc_real12)
else
if IsUnitFlying(loc_unit02)==false then
call SetUnitFlyHeight(loc_unit02,GetUnitDefaultFlyHeight(loc_unit02),0)
endif
call PauseUnit(loc_unit02,false)
call SetUnitPathing(loc_unit02,true)
call SetUnitPosition(loc_unit02,loc_real05,loc_real06)
call SaveInteger(udg_DotaHashtable,(GetHandleId((loc_unit02))),((4268)),(2))
set loc_location01=Location(loc_real05,loc_real06)
call TerrainDeformationRippleBJ(0.2,true,loc_location01,1.00,300.00,96.00,1,64.00)
call RemoveLocation(loc_location01)
call DestroyEffect((LoadEffectHandle(udg_DotaHashtable,(loc_integer01),(32)))) // Projection effect handle
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl",GetUnitX(loc_unit02),GetUnitY(loc_unit02)))
if IsUnitAlly(loc_unit02,GetOwningPlayer(loc_unit01))then
else
//if(Func0367((loc_unit01),integers084[integer355])!=null)then
if(GetHeroLevel(loc_unit01)>=20)then
// Unit carries Aghanim scepter
call DamageUnit(loc_unit01,loc_unit02,1,(0.2+0.15*GetUnitAbilityLevel(loc_unit01,'A068')+1)*2000*GetUnitAbilityLevel(loc_unit01,'A06G'))
else
call DamageUnit(loc_unit01,loc_unit02,1,(0.2+0.15*GetUnitAbilityLevel(loc_unit01,'A068'))*2000*GetUnitAbilityLevel(loc_unit01,'A06G'))
endif
endif
call DestroyTreeDestructablesInRegion(loc_real05,loc_real06,300)
call TossAreaDamage(loc_unit01,loc_real05,loc_real06,300,550*GetUnitAbilityLevel(loc_unit01,'A06G'))
//call Func0021(loc_integer03)
call FlushChildHashtable(udg_DotaHashtable,(loc_integer01))
call DisableTrigger(loc_trigger01)
endif
set loc_trigger01=null
set loc_unit01=null
set loc_unit02=null
set loc_unit03=null
set loc_location01=null
return false
endfunction
function TossSourceEffect takes nothing returns nothing
local unit loc_unit01=GetTriggerUnit()
local unit loc_unit02=TossSelectProjectUnit(loc_unit01)
local unit loc_unit03=GetSpellTargetUnit()
local trigger loc_trigger01=CreateTrigger()
local integer loc_integer01=GetHandleId(loc_trigger01)
call DebugLog(LVL_DEBUG, "TossSourceEffect")
//call SetUnitAnimationByIndex(loc_unit01,4)
call SetUnitAnimation(loc_unit01, "spell")
call PauseUnit(loc_unit02,true)
call SetUnitPathing(loc_unit02,false)
if IsUnitFlying(loc_unit02)==false then
call UnitAddAbility(loc_unit02,'Amrf')
call UnitRemoveAbility(loc_unit02,'Amrf')
endif
call SaveInteger(udg_DotaHashtable,(GetHandleId((loc_unit02))),((4268)),(1)) // Is unit projected
call SaveUnitHandle(udg_DotaHashtable,(loc_integer01),(14),(loc_unit01)) // Caster unit
call SaveUnitHandle(udg_DotaHashtable,(loc_integer01),(2),(loc_unit02)) // Projected unit
//call SaveInteger(udg_DotaHashtable,(loc_integer01),(30),(Func0024(loc_unit03))) // Target handle
call SaveUnitHandle(udg_DotaHashtable,(loc_integer01),(30),loc_unit03) // Target unit
call SaveEffectHandle(udg_DotaHashtable,(loc_integer01),(32),(AddSpecialEffectTarget("Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl",loc_unit02,"origin"))) // Projection effect handle
call SaveReal(udg_DotaHashtable,(loc_integer01),(282),((GetUnitX(loc_unit03))*1.0)) // Target X
call SaveReal(udg_DotaHashtable,(loc_integer01),(283),((GetUnitY(loc_unit03))*1.0)) // Target Y
call TriggerRegisterTimerEvent(loc_trigger01,0.02,true)
call TriggerAddCondition(loc_trigger01,Condition(function TossProjectedUnitFlightPeriodic))
//call TriggerDebugAutomation_TriggerIsReady(loc_trigger01, "TossSourceEffect")
set loc_unit01=null
set loc_unit02=null
set loc_unit03=null
set loc_trigger01=null
endfunction
function TossSourceCast takes nothing returns nothing
local unit loc_unit01=GetTriggerUnit()
local unit loc_unit02=TossSelectProjectUnit(loc_unit01)
local unit loc_unit03=GetSpellTargetUnit()
call DebugLog(LVL_DEBUG, "TossSourceCast")
if loc_unit02==null then
call StopUnitSilent(loc_unit01)
call DisplayError(GetOwningPlayer(loc_unit01),"No valid unit to Toss")
elseif GetOwningPlayer(loc_unit03)==GetOwningPlayer(loc_unit01)then
call StopUnitSilent(loc_unit01)
call DisplayError(GetOwningPlayer(loc_unit01),"Cannot Toss to your own units")
endif
set loc_unit01=null
set loc_unit02=null
set loc_unit03=null
endfunction
function TossEffect takes nothing returns boolean
if GetSpellAbilityId()=='A06G' then
call DebugLog(LVL_DEBUG, "TossEffect")
if GetTriggerEventId()==EVENT_PLAYER_UNIT_SPELL_CAST then
call TossSourceCast()
else
call TossSourceEffect()
endif
endif
return false
endfunction
function TossEffectRegister takes nothing returns nothing
local trigger loc_trigger01=CreateTrigger()
call TriggerRegisterAllPlayersUnitEvent(loc_trigger01,EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerRegisterAllPlayersUnitEvent(loc_trigger01,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(loc_trigger01,Condition(function TossEffect))
set loc_trigger01=null
endfunction
library GameStatus uses optional PlayerUtils
/***************************************************************
*
* v1.0.0 by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Simple API for detecting if the game is online, offline, or a replay.
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the script to your map and save it (requires JassHelper *or* JNGP)
* _________________________________________________________________________
* 2. API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* This library provides one function
*
* function GetGameStatus takes nothing returns integer
*
* It returns one of the following constants
*
* - GAME_STATUS_OFFLINE
* - GAME_STATUS_ONLINE
* - GAME_STATUS_REPLAY
*
***************************************************************/
// Configuration:
globals
// The dummy unit is only created once, and removed directly after.
private constant integer DUMMY_UNIT_ID = 'hfoo'
endglobals
// (end)
globals
constant integer GAME_STATUS_OFFLINE = 0
constant integer GAME_STATUS_ONLINE = 1
constant integer GAME_STATUS_REPLAY = 2
private integer status = 0
endglobals
function GetGameStatus takes nothing returns integer
return status
endfunction
private module GameStatusInit
private static method onInit takes nothing returns nothing
local player firstPlayer
local unit u
local boolean selected
// find an actual player
static if not (LIBRARY_PlayerUtils) then
set firstPlayer = Player(0)
loop
exitwhen (GetPlayerController(firstPlayer) == MAP_CONTROL_USER and GetPlayerSlotState(firstPlayer) == PLAYER_SLOT_STATE_PLAYING)
set firstPlayer = Player(GetPlayerId(firstPlayer)+1)
endloop
else
set firstPlayer = User.fromPlaying(0).toPlayer()
endif
// force the player to select a dummy unit
set u = CreateUnit(firstPlayer, DUMMY_UNIT_ID, 0, 0, 0)
call SelectUnit(u, true)
set selected = IsUnitSelected(u, firstPlayer)
call RemoveUnit(u)
set u = null
if (selected) then
// detect if replay or offline game
if (ReloadGameCachesFromDisk()) then
set status = GAME_STATUS_OFFLINE
else
set status = GAME_STATUS_REPLAY
endif
else
// if the unit wasn't selected instantly, the game is online
set status = GAME_STATUS_ONLINE
endif
endmethod
endmodule
private struct GameStatus
implement GameStatusInit
endstruct
endlibrary
library Maths
function Floor takes real r returns integer
local integer i = R2I(r)
if r == i then
return i
endif
if r > 0 then
return i
else // if r < 0 then
return i - 1
endif
endfunction
function Ceil takes real r returns integer
local integer i = R2I(r)
if r == i then
return i
endif
if r > 0 then
return i + 1
else // if r < 0 then
return i
endif
endfunction
function Round takes real r returns integer
if r > 0 then
return R2I(r + 0.5)
else // if r < 0 then
return R2I(r - 0.5)
endif
endfunction
endlibrary
library Logarithm
globals
private constant integer ITERATIONS=20
endglobals
function Log takes real x returns real
local real min=-88.0
local real max= 88.0
local real mid
local integer i=ITERATIONS
loop
set mid=(min+max)/2
exitwhen(i<=0)
set i=i-1
if (Pow(bj_E,mid)>=x) then
set max=mid
else
set min=mid
endif
endloop
return mid
endfunction
function Logarithm takes real base, real x returns real
local real min=-88.0
local real max= 88.0
local real mid
local integer i=ITERATIONS
loop
set mid=(min+max)/2
exitwhen(i<=0)
set i=i-1
if (Pow(base,mid)>=x) then
set max=mid
else
set min=mid
endif
endloop
return mid
endfunction
endlibrary
/*
Armor lib v1.0.1 (28/11/2019) by Ricola3D
API:
### General ###
function ArmorToDamageFactor takes real whichArmor returns real
> Converts armor amount to damage factor.
function DamageFactorToArmor takes real whichDamageFactor returns real
> Converts damage factor to armor amount.
### Unit ###
function ModifyArmor takes unit whichUnit, real armorDiff returns nothing
> Adds/removes the given difference to unit's code armor.
function AdjustArmorToWhite takes unit whichUnit, real desiredWhiteArmor returns nothing
> Modifies unit's code armor to obtain the desired amount of white armor.
function AdjustArmorToWhitePercent takes unit whichUnit, real desiredWhiteArmorPercent returns nothing
> Modifies unit's code armor to obtain the desired percent of current white armor.
function AdjustArmorToTotal takes unit whichUnit, real desiredTotalArmor returns nothing
> Modifies unit's code armor to obtain the desired amount of total armor.
function AdjustArmorToTotalPercent takes unit whichUnit, real desiredTotalArmorPercent returns nothing
> Modifies unit's code armor to obtain the desired percent of current total armor.
function SetCodeArmor takes unit whichUnit, real desiredCodeArmor returns nothing
> Sets unit's code armor to the given value.
function GetWhiteArmor takes unit whichUnit returns real
> Gets white (base + agi + code) armor of the unit.
function GetGreenArmor takes unit whichUnit returns real
> Gets green armor of the unit.
function GetTotalArmor takes unit whichUnit returns real
> Gets total (white + green) armor of the unit.
function GetDamageFactor takes unit whichUnit returns real
> Gets the damage factor corresponding to the total armor of the unit.
function GetAgiArmor takes unit whichUnit, boolean includeBonuses returns real
> Gets agility armor of the unit.
function GetCodeArmor takes unit whichUnit returns real
> Gets code armor of the unit (0 by default, modified by setters).
function GetBaseArmor takes unit whichUnit returns real
> Worthless. Gets base (value from Object Editor) armor of the unit.
*/
native UnitAlive takes unit id returns boolean
library Armor requires Logarithm /*, TriggerDebugAutomation*/
globals
// ----------------------------------------------------------------------------
// BASE SETTINGS
// ----------------------------------------------------------------------------
// Values shall be taken from gameplay constants
constant real AGI_DEFENSE_BASE = 5.00 // "Hero Attributes - Defense Base Value (before Agility Bonus)"
constant real AGI_DEFENSE_BONUS = 0.1 // "Hero Attributes - Defense Bonus per Agility Point"
constant real DEFENSE_ARMOR = 0.06 // "Combat - Armor Damage Reduction Multiplier"
// An ability derivated from Devotion Aura that nullifies what Blizzard considers "base" armor.
private constant integer NULLIFY_BASE_ARMOR_ABILITY = 'A05K'
private constant integer NULLIFY_BASE_ARMOR_BUFF = 'B01J'
// System cleaning - Ability to detect unit removal from game
// If you already have a Unit Event system (GUI Unit Event by Bribe, Unit Event by Nestharus, or AutoEvents by grim001) you can reuse the same ability.
private constant integer DETECT_REMOVE_ABILITY = 'A06D'
// ----------------------------------------------------------------------------
// ADVANCED SETTINGS (you may leave them default)
// ----------------------------------------------------------------------------
// Set to true if you want the getter/setters to work for hidden units.
// Notes:
// - Works fine except in one case: during "[ANef] Storm, Earth, And Fire" ability cast.
constant boolean ENABLE_UNHIDE = true
// Set to true to enable last ressort action (if nothing else worked to compute armor values).
// Notes:
// - No known case for the moment, it's just a safe fallback measure.
// - Will temporary remove items, and remove all buffs (only aura/passive buffs will be re-activate after, not active ones)
constant boolean ENABLE_BONUS_REMOVAL = true
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// Internal storage for code armor value
private hashtable unitData
endglobals
// ----------------------------------------------------------------------------
// SaveCode.
// Notes: since there is no native to access internal "code armor" value from Blizzard. This function stores a copy of it.
// ----------------------------------------------------------------------------
private function SaveCode takes unit whichUnit, real codeArmor returns nothing
if ( null != whichUnit ) then
// In case there is no Unit Event system, add the remove detect ability manually.
if ( 0 == GetUnitAbilityLevel(whichUnit, DETECT_REMOVE_ABILITY) ) then
call UnitAddAbility(whichUnit, DETECT_REMOVE_ABILITY)
call UnitMakeAbilityPermanent(whichUnit, true, DETECT_REMOVE_ABILITY)
endif
call SaveReal( unitData, GetHandleId(whichUnit), StringHash("code_armor"), codeArmor )
endif
endfunction
// ----------------------------------------------------------------------------
// ArmorToDamageFactor: get damage modifier corresponding to a armor value.
// Notes:
// - "0.02" means damages are reduced by 98%.
// - "1.25" means damages are amplified by 25%.
// - For the moment Blizzard displays -71% as min reduction
// ----------------------------------------------------------------------------
function ArmorToDamageFactor takes real whichArmor returns real
local real factor = 0.0
if (whichArmor >= 0) then
// Damage reduction - factor is inferior or equal to 1
set factor = 1 - ( whichArmor * DEFENSE_ARMOR ) / ( 1 + whichArmor * DEFENSE_ARMOR )
else
// DamageAmplification - factor is superior to 1
set factor = 2 - Pow( 1 - DEFENSE_ARMOR , -1 * whichArmor )
endif
return factor
endfunction
// ----------------------------------------------------------------------------
// DamageFactorToArmor: computes the armor necessary to reach this damage factor.
// Notes:
// - Does not work for 0 walue (= dodge, nullify damage).
// - Does not work for negative values (=heal).
// ----------------------------------------------------------------------------
function DamageFactorToArmor takes real whichDamageFactor returns real
local real armor = 0.0
if (whichDamageFactor <= 0.0) then
// Negative factor does not work - infinite armor or healing
elseif (whichDamageFactor <= 1.0) then
// Damage reduction - armor is positive or null
set armor = ( 1 - whichDamageFactor ) / ( whichDamageFactor * DEFENSE_ARMOR )
else
// Damage amplification - armor is negative
set armor = -1.0 * Logarithm( 1.0 - DEFENSE_ARMOR, 2.0 - whichDamageFactor )
endif
return armor
endfunction
// ----------------------------------------------------------------------------
// GetAgiArmor: get armor bonus from agility. Returns 0 for non-hero units.
// ----------------------------------------------------------------------------
function GetAgiArmor takes unit whichUnit, boolean includeBonuses returns real
local real agiArmor = 0.0
local integer agi = 0
local boolean isHero = false
if ( null != whichUnit ) then
set isHero = IsUnitIdType(GetUnitTypeId(whichUnit), UNIT_TYPE_HERO)
if (isHero) then
// Unit is hero, illusion of hero, ...
set agi = GetHeroAgi(whichUnit, includeBonuses)
set agiArmor = AGI_DEFENSE_BASE + AGI_DEFENSE_BONUS * I2R(agi)
endif
endif
return agiArmor
endfunction
// ----------------------------------------------------------------------------
// GetCodeArmor: get code armor. This is the only armor you can modify directly (with BlzSetUnitArmor primitive).
// ----------------------------------------------------------------------------
function GetCodeArmor takes unit whichUnit returns real
local real codeArmor = 0.0
if ( null != whichUnit ) then
set codeArmor = LoadReal( unitData, GetHandleId(whichUnit), StringHash("code_armor") )
endif
return codeArmor
endfunction
// ----------------------------------------------------------------------------
// GetBaseArmor: get what Blizzard considers "base" armor.
// If no upgrades, equal to the number in World Editor > Object Editor > Unit > "[def] Combat - Defense base".
// Otherwise also includes upgrades count x "[defUp] Defense Upgrade Bonus"
// Notes:
// - Meaningless in 99% of cases. Equal to white armor ONLY for non-hero units whose armor have not been modified by code...)
// - This is the value taken as base by Devotion Aura with "[DataB1] Data - Percent Bonus" set to true.
// ----------------------------------------------------------------------------
function GetBaseArmor takes unit whichUnit returns real
local real totalArmorBefore = 0.0
local real totalArmorAfter = 0.0
local real baseArmor = 0.0
static if ENABLE_BONUS_REMOVAL then
local integer inventorySize = 0
local integer tempInventorySlot = 0
local item array unitItems
endif
local boolean abilityAdded = false
local boolean buffAdded = false
local boolean isDead = false
static if ENABLE_UNHIDE then
local boolean isHidden = false
endif
if ( null != whichUnit ) then
static if ENABLE_UNHIDE then
set isHidden = IsUnitHidden(whichUnit)
if (isHidden) then
call ShowUnit(whichUnit, true)
endif
endif
// Try to compute base armor using the test ability (that nullifies base armor).
set totalArmorBefore = BlzGetUnitArmor(whichUnit)
call UnitAddAbility(whichUnit, NULLIFY_BASE_ARMOR_ABILITY)
set abilityAdded = ( GetUnitAbilityLevel(whichUnit, NULLIFY_BASE_ARMOR_ABILITY) > 0 )
set buffAdded = ( GetUnitAbilityLevel(whichUnit, NULLIFY_BASE_ARMOR_BUFF) > 0 )
if (abilityAdded and buffAdded) then
// Test worked, base armor has been nullified properly.
set totalArmorAfter = BlzGetUnitArmor(whichUnit)
set baseArmor = totalArmorBefore - totalArmorAfter
else
// Test failed
set isDead = ( not UnitAlive(whichUnit) )
if (isDead) then
// Case A: unit is dead. Thus its green armor is 0, and base = total - agi - code
set baseArmor = BlzGetUnitArmor(whichUnit) - GetAgiArmor(whichUnit, false) - GetCodeArmor(whichUnit)
static if ENABLE_BONUS_REMOVAL then
else
// Case Z: last resort is to remove all armor bonuses
// Temporary remove all items
set inventorySize = UnitInventorySizeBJ(whichUnit)
set tempInventorySlot = 0
loop
set tempInventorySlot = tempInventorySlot + 1
exitwhen tempInventorySlot > inventorySize
set unitItems[tempInventorySlot] = UnitItemInSlotBJ(whichUnit, tempInventorySlot)
if ( null != unitItems[tempInventorySlot] ) then
call UnitRemoveItemFromSlot(whichUnit, tempInventorySlot)
endif
endlop
// Remove all buffs (unfortunately there is no way to restore the active buffs)
call UnitRemoveBuffsEx(whichUnit, true, true, true, true, false, true, true) // Remove all buffs but timed life
set armor = BlzGetUnitArmor(whichUnit) - GetAgiArmor(whichUnit, false) - GetCodeArmor(whichUnit)
// Restore all items
set tempInventorySlot = 0
loop
set tempInventorySlot = tempInventorySlot + 1
exitwhen tempInventorySlot > inventorySize
if ( null != unitItems[tempInventorySlot] ) then
call UnitDropItemSlotBJ( whichUnit, unitItems[tempInventorySlot], tempInventorySlot )
endif
endlop
endif
endif
endif
if (abilityAdded) then
call UnitRemoveAbility(whichUnit, NULLIFY_BASE_ARMOR_ABILITY)
endif
if (buffAdded) then
call UnitRemoveBuffBJ(NULLIFY_BASE_ARMOR_BUFF, whichUnit)
endif
static if ENABLE_UNHIDE then
if (isHidden) then
call ShowUnit(whichUnit, false)
endif
endif
endif
return baseArmor
endfunction
// ----------------------------------------------------------------------------
// GetWhiteArmor: get armor before bonus (= number displayed in white in unit status).
// ----------------------------------------------------------------------------
function GetWhiteArmor takes unit whichUnit returns real
local real whiteArmor = 0.0
local integer baseAgi = 0
if ( null != whichUnit ) then
set whiteArmor = GetBaseArmor(whichUnit) + GetAgiArmor(whichUnit, false) + GetCodeArmor(whichUnit)
endif
return whiteArmor
endfunction
// ----------------------------------------------------------------------------
// GetTotal: get total armor. Same as BlzGetUnitArmor.
// ----------------------------------------------------------------------------
function GetTotalArmor takes unit whichUnit returns real
local real armor = 0.0
if ( null != whichUnit ) then
set armor = BlzGetUnitArmor(whichUnit)
endif
return armor
endfunction
// ----------------------------------------------------------------------------
// GetGreenArmor: get current armor bonus (= number displayed in green in unit status.
// ----------------------------------------------------------------------------
function GetGreenArmor takes unit whichUnit returns real
return ( GetTotalArmor(whichUnit) - GetWhiteArmor(whichUnit) )
endfunction
// ----------------------------------------------------------------------------
// GetDamageFactor: get current damage factor taking into account total armor. Does not consider attack type, damage type and armor type!
// Notes:
// - If factor is <1, then damage reduction
// - If factor >1, then damage amplification
// - If factor ==1, damages unchanged, null armor.
// - If factor ==0, then error.
// ----------------------------------------------------------------------------
function GetDamageFactor takes unit whichUnit returns real
local real damageFactor = 0.0
local real totalArmor = 0.0
if ( null != whichUnit ) then
set totalArmor = GetTotalArmor(whichUnit)
set damageFactor = DamageFactorToArmor(totalArmor)
endif
return damageFactor
endfunction
// ----------------------------------------------------------------------------
// ModifyArmor: apply difference to unit code armor (and thus to white armor). Difference can be positive or negative.
// Also modifies white armor and total armor of the same difference.
// ----------------------------------------------------------------------------
function ModifyArmor takes unit whichUnit, real armorDiff returns nothing
local real currentTotalArmor = 0.0
if ( null != whichUnit ) then
set currentTotalArmor = BlzGetUnitArmor(whichUnit)
call BlzSetUnitArmor(whichUnit, currentTotalArmor + armorDiff )
endif
endfunction
// ----------------------------------------------------------------------------
// SetCode: set unit code armor to desired value.
// ----------------------------------------------------------------------------
function SetCodeArmor takes unit whichUnit, real desiredCodeArmor returns nothing
local real currentCodeArmor = 0.0
local real armorDiff = 0.0
if ( null != whichUnit ) then
set currentCodeArmor = GetCodeArmor(whichUnit)
set armorDiff = desiredCodeArmor - currentCodeArmor
call ModifyArmor(whichUnit, armorDiff)
endif
endfunction
// ----------------------------------------------------------------------------
// AdjustArmorToWhite: set unit base armor such as white armor reaches the desired amount.
// ----------------------------------------------------------------------------
function AdjustArmorToWhite takes unit whichUnit, real desiredWhiteArmor returns nothing
local real currentWhiteArmor = 0.0
local real armorDiff = 0.0
if ( null != whichUnit ) then
set currentWhiteArmor = GetWhiteArmor(whichUnit)
set armorDiff = desiredWhiteArmor - currentWhiteArmor
call ModifyArmor(whichUnit, armorDiff)
endif
endfunction
// ----------------------------------------------------------------------------
// AdjustArmorToWhitePercent: set unit base armor such as white armor changes of the desirated percent.
// Ex: 1.50 will increase white armor of 50%
// ----------------------------------------------------------------------------
function AdjustArmorToWhitePercent takes unit whichUnit, real desiredWhiteArmorPercent returns nothing
local real currentWhiteArmor = 0.0
local real armorDiff = 0.0
if ( null != whichUnit ) then
set currentWhiteArmor = GetWhiteArmor(whichUnit)
set armorDiff = currentWhiteArmor * (desiredWhiteArmorPercent - 1.0)
call ModifyArmor(whichUnit, armorDiff)
endif
endfunction
// ----------------------------------------------------------------------------
// AdjustArmorToTotal: set unit base armor such as total reaches the desired amount.
// Same as BlzSetUnitArmor.
// ----------------------------------------------------------------------------
function AdjustArmorToTotal takes unit whichUnit, real desiredTotalArmor returns nothing
if ( null != whichUnit ) then
call BlzSetUnitArmor(whichUnit, desiredTotalArmor)
endif
endfunction
// ----------------------------------------------------------------------------
// AdjustArmorToTotalPercent: set unit base armor such as total armor changes of the desirated percent.
// Ex: 1.50 will increase total armor of 50%
// ----------------------------------------------------------------------------
function AdjustArmorToTotalPercent takes unit whichUnit, real desiredTotalArmorPercent returns nothing
local real currentTotalArmor = 0.0
if ( null != whichUnit ) then
set currentTotalArmor = BlzGetUnitArmor(whichUnit)
call BlzSetUnitArmor( whichUnit, currentTotalArmor * desiredTotalArmorPercent )
endif
endfunction
// ----------------------------------------------------------------------------
// onBlzSetUnitArmor: called just before everytime code sets unit armor with "BlzSetUnitArmor" native.
// Notes: since there is no native to access internal "code armor" value from Blizzard. This function stores a copy of it.
// ----------------------------------------------------------------------------
private function onBlzSetUnitArmor takes unit whichUnit, real armorAmount returns nothing
local real previousCodeArmor = 0.0
local real nextCodeArmor = 0.0
local real previousArmorAmount = 0.0
local real armorDiff = 0.0
if ( null != whichUnit ) then
// Detect armor difference
set previousArmorAmount = BlzGetUnitArmor(whichUnit)
set armorDiff = armorAmount - previousArmorAmount
// Get previous code armor (0 if not existing), update it, and save-it back
set previousCodeArmor = GetCodeArmor(whichUnit)
set nextCodeArmor = previousCodeArmor + armorDiff
call SaveCode(whichUnit, nextCodeArmor)
endif
endfunction
hook BlzSetUnitArmor onBlzSetUnitArmor
// ----------------------------------------------------------------------------
// onBlzSetUnitRealField: called just before everytime code sets unit armor with "BlzSetUnitRealField" native for UNIT_RF_DEFENSE field.
// Notes: intenally just calls onBlzSetUnitArmor function.
// ----------------------------------------------------------------------------
private function onBlzSetUnitRealField takes unit whichUnit, unitrealfield whichField, real value returns nothing
if ( UNIT_RF_DEFENSE == whichField ) then
call onBlzSetUnitArmor(whichUnit, value)
endif
endfunction
hook BlzSetUnitRealField onBlzSetUnitRealField
// ----------------------------------------------------------------------------
// onUnitRemoved: called just before a unit is removed from the game. Cleans internal storage.
// ----------------------------------------------------------------------------
private function onUnitRemoved takes nothing returns nothing
local unit whichUnit = GetTriggerUnit()
local boolean unitBeingRemoved
// Because unit has DETECT_REMOVE_ABILITY, it will fire a "undefend" event just before being removed from game.
if ( OrderId2StringBJ(GetIssuedOrderIdBJ()) == "undefend" ) then
set unitBeingRemoved = ( 0 == GetUnitAbilityLevel(whichUnit, DETECT_REMOVE_ABILITY) )
if (unitBeingRemoved) then
call FlushChildHashtable( unitData, GetHandleId(whichUnit) )
endif
endif
endfunction
// ----------------------------------------------------------------------------
// onInit: system initializer
// ----------------------------------------------------------------------------
private module M
private static method onInit takes nothing returns nothing
local trigger cleanTrigger = CreateTrigger() // trigger to detect unit removal (and clean hashtable)
call TriggerRegisterAnyUnitEventBJ( cleanTrigger, EVENT_PLAYER_UNIT_ISSUED_ORDER )
call TriggerAddAction( cleanTrigger, function onUnitRemoved )
//call TriggerDebugAutomation_TriggerIsReady(cleanTrigger, "cleanTrigger")
set unitData = InitHashtable() // Init hashtable (avoid adding dependency to an indexer)
endmethod
endmodule
private struct S extends array
implement M
endstruct
endlibrary
library TimeUtils
globals
private constant string FIELDS_DELIMITER = ":"
endglobals
function FormatTime takes real seconds returns string
// Compute seconds, minutes and hours
local real s = ModuloReal( seconds, 60.0 )
local integer m = R2I(ModuloReal( seconds, 3600 ) / 60.0)
local integer h = R2I(seconds / 3600.0)
local string ss
local string ms
local string hs
local string formatedTime
// Format seconds as real with 3 decimals string
set ss = R2SW(s, 2, 3)
// Format minutes as a 2-character number string (3 => 03)
set ms = I2S(m)
if ( 1 == StringLength(ms) ) then
set ms = "0" + ms
endif
// Format hours as a 2-character number string (3 => 03)
set hs = I2S(h)
if ( 1 == StringLength(hs) ) then
set hs = "0" + hs
endif
// Contatenate to obtenate a time under the form: HH:MM:SS.sss
set formatedTime = hs + FIELDS_DELIMITER + ms + FIELDS_DELIMITER + ss
return formatedTime
endfunction
endlibrary
//===========================================================================
function UnitEventInAction takes nothing returns nothing
if (not udg_UnitInAction[udg_UDex]) then
set udg_UnitInAction[udg_UDex] = true
set udg_UnitInActionEvent = 0.00
set udg_UnitInActionEvent = 1.00
set udg_UnitInActionEvent = 0.00
endif
endfunction
function UnitEventInaction takes nothing returns nothing
local integer i = bj_MAX_PLAYER_SLOTS
local unit u = udg_UDexUnits[udg_UDex]
local player p
local player p2
local boolean b
if udg_IsUnitAlive[udg_UDex] and udg_CargoTransportUnit[udg_UDex] != null then
set p2 = GetOwningPlayer(udg_CargoTransportUnit[udg_UDex])
endif
if (udg_UnitInAction[udg_UDex]) then
set udg_UnitInAction[udg_UDex] = false
set udg_UnitInActionEvent = 0.00
set udg_UnitInActionEvent = 2.00
set udg_UnitInActionEvent = 0.00
endif
// Check if selected by some players
loop
set i = i - 1
set p = Player(i)
if IsUnitInGroup(u, udg_PlayerSelection[i]) then
call GroupRemoveUnit(udg_PlayerSelection[i], u)
set udg_PDex = i + 1
set udg_PlayerSelectionEvent = 0.00
set udg_PlayerSelectionEvent = 2.00
set udg_PlayerSelectionEvent = 0.00
// Check if selected unit is loaded in a controlable transporter
set b = (p == p2) or GetPlayerAlliance(p2, p, ALLIANCE_SHARED_CONTROL) or GetPlayerAlliance(p2, p, ALLIANCE_SHARED_ADVANCED_CONTROL)
if (b) then
call GroupAddUnit(udg_PlayerSelection[i], udg_CargoTransportUnit[udg_UDex])
set udg_UDex = GetUnitUserData(udg_CargoTransportUnit[udg_UDex])
set udg_PDex = i + 1
set udg_PlayerSelectionEvent = 0.00
set udg_PlayerSelectionEvent = 1.00
set udg_PlayerSelectionEvent = 0.00
endif
endif
exitwhen i == 0
endloop
endfunction
function UnitEventOnSelect takes nothing returns boolean
local unit u = GetTriggerUnit()
local player p = GetTriggerPlayer()
local integer i = GetPlayerId(p)
local boolean b = IsUnitInGroup(u, udg_PlayerSelection[i])
if (not b) then
call GroupAddUnit(udg_PlayerSelection[i], u)
set udg_UDex = GetUnitUserData(u)
set udg_PDex = i + 1
set udg_PlayerSelectionEvent = 0.00
set udg_PlayerSelectionEvent = 1.00
set udg_PlayerSelectionEvent = 0.00
endif
return false
endfunction
function UnitEventOnDeselect takes nothing returns boolean
local unit u = GetTriggerUnit()
local player p = GetTriggerPlayer()
local integer i = GetPlayerId(p)
local boolean b = IsUnitInGroup(u, udg_PlayerSelection[i])
if (b) then
call GroupRemoveUnit(udg_PlayerSelection[i], u)
set udg_UDex = GetUnitUserData(u)
set udg_PDex = i + 1
set udg_PlayerSelectionEvent = 0.00
set udg_PlayerSelectionEvent = 2.00
set udg_PlayerSelectionEvent = 0.00
endif
return false
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 id = 0
local unit u
loop
set id = udg_CheckDeathList[id]
exitwhen id == 0
set udg_UDex = id
set u = udg_UDexUnits[id]
if udg_IsUnitNew[id] then
//The unit was just created.
set udg_IsUnitNew[id] = false
set udg_UnitIndexEvent = 1.50 //New event requested by SpellBound to detect when unit fully enters scope.
set udg_UnitIndexEvent = 0.00
elseif udg_IsUnitTransforming[id] then
//Added 21 July 2017 to fix the issue re-adding this ability in the same instant
set udg_UnitTypeEvent = 0.00
set udg_UnitTypeEvent = 1.00
set udg_UnitTypeOf[id] = GetUnitTypeId(u) //Set this afterward as otherwise the user won't know what the previous unittype was.
set udg_IsUnitTransforming[id] = false
call UnitAddAbility(u, udg_DetectTransformAbility)
elseif udg_IsUnitAlive[id] then
//The unit has started reincarnating (=is a tombstone).
set udg_IsUnitReincarnating[id] = true
set udg_IsUnitAlive[id] = false
set udg_DeathEvent = 0.50
set udg_DeathEvent = 0.00
call UnitEventInaction()
elseif GetUnitTypeId(u) != 0 and not IsUnitType(u, UNIT_TYPE_DEAD) then //with vJass, could just use UnitAlive instead of both of these values.
//Moved this code to fire after a 0 second timer instead.
set udg_IsUnitAlive[id] = true
set udg_DeathEvent = 2.00
set udg_DeathEvent = 0.00
call UnitEventInAction()
set udg_IsUnitReincarnating[id] = false
endif
set udg_CheckDeathInList[id] = false
endloop
set u = null
//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
call UnitEventInAction()
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
call UnitEventInaction()
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 an inaction event for UDex
call UnitEventInaction()
//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
call UnitEventCheckAfterProxy(i) //modified 22 Oct 2022 to ensure the unit is fully revived before firing the event.
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
call UnitEventInaction()
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
call UnitEventInAction()
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
call UnitEventInaction()
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
// Fire a in action event
call UnitEventInAction()
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 trigger select = CreateTrigger()
local trigger deselect = 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))
call TriggerAddCondition(select, Filter(function UnitEventOnSelect))
call TriggerAddCondition(deselect, Filter(function UnitEventOnDeselect))
loop
set i = i - 1
set p = Player(i)
set udg_PlayerSelection[i] = CreateGroup()
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 TriggerRegisterPlayerUnitEvent( select, p, EVENT_PLAYER_UNIT_SELECTED, null)
call TriggerRegisterPlayerUnitEvent( deselect, p, EVENT_PLAYER_UNIT_DESELECTED, null)
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, enterB)
exitwhen i == 0
endloop
// TODO: just in case, a 2nd loop for units already selected by the init struct of another library ?
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
/*
vJass Damage Engine 5.A.0.0
This update enables compatibility with AttackIndexer.
*/
/*
JASS API:
struct Damage extends array
readonly static unit source // udg_DamageEventSource in real-time
readonly static unit target // udg_DamageEventTarget in real-time
static real amount // udg_DamageEventAmount in real-time
readonly unit sourceUnit // udg_DamageEventSource by index
readonly unit targetUnit // udg_DamageEventTarget by index
real damage // udg_DamageEventAmount by index
readonly real prevAmt // udg_DamageEventPrevAmt by index
attacktype attackType // udg_DamageEventAttackT by index
damagetype damageType // udg_DamageEventDamageT by index
weapontype weaponType // udg_DamageEventWeaponT by index
integer userType // udg_DamageEventType by index
readonly boolean isAttack // udg_IsDamageAttack by index
readonly boolean isCode // udg_IsDamageCode by index
readonly boolean isMelee // udg_IsDamageMelee by index
readonly boolean isRanged // udg_IsDamageRanged by index
readonly boolean isSpell // udg_IsDamageSpell by index
real armorPierced // udg_DamageEventArmorPierced by index
integer armorType // udg_DamageEventArmorT by index
integer defenseType // udg_DamageEventDefenseT by index
readonly integer eFilter
Set to false to disable the damage event triggers or to true to reverse that:
static boolean operator enabled
Same arguments as "UnitDamageTarget" but has the benefit of being performance-friendly during recursive events.
Will automatically cause the damage to be registered as Code damage.
static method apply takes
unit source,
unit target,
real amount,
boolean isAttack,
boolean isRanged,
attacktype at,
damagetype dt,
weapontype wt
returns Damage
A simplified version of the above function that autofills each boolean, attacktype and weapontype.
static method applySpell takes
unit src,
unit tgt,
real amt,
damagetype dt
returns Damage
A different variation of the above which autofills the "isAttack" boolean
and populates damagetype as DAMAGE_TYPE_NORMAL.
static method applyAttack takes
unit src,
unit tgt,
real amt,
boolean ranged,
attacktype at,
weapontype wt
returns Damage
struct DamageTrigger extends array
method operator filter= takes integer filter returns nothing
// Apply primary filters such as DamageEngine_FILTER_MELEE/RANGED/SPELL which are based off of limitop handles to enable easier access for GUI folks
// Full filter list:
- integer DamageEngine_FILTER_ATTACK
- integer DamageEngine_FILTER_MELEE
- integer DamageEngine_FILTER_OTHER
- integer DamageEngine_FILTER_RANGED
- integer DamageEngine_FILTER_SPELL
- integer DamageEngine_FILTER_CODE
boolean configured //set to True after configuring any filters listed below.
Apply custom filters after setting any desired udg_DamageFilter variables (for GUI).
Alternatively, vJass users can set these instead. Just be mindful to set the variable
"configured" to true after settings these:
unit source
unit target
integer sourceType
integer targetType
integer sourceBuff
integer targetBuff
real damageMin
integer attackType
integer damageType
integer userType
method configure takes nothing returns nothing
The string in the aruments below requires the following API:
"" for standard damage event
"Modifier(or Mod if you prefer)/After/Lethal/AOE" for the others
static method registerTrigger takes
trigger whichTrig,
string var,
real value
returns nothing
static method unregister takes
trigger whichTrig,
string eventName,
real value,
boolean reset
returns boolean
static method getIndex takes
trigger fromTrigger,
string eventName,
real value
returns integer
If you already have the index of the trigger you want to unregister:
method unregisterByIndex takes
boolean reset
returns boolean
Converts a code argument to a trigger, while checking if the same code had already been registered before.
Use it via DamageTrigger[function MyCallbackFunction]
static method operator [] takes
code callback
returns trigger
The accepted strings here use the same criteria as DamageTrigger.getIndex/registerTrigger/unregister:
function TriggerRegisterDamageEngineEx takes
trigger whichTrig,
string eventName,
real value,
integer opId
returns nothing
function TriggerRegisterDamageEngine takes
trigger whichTrig,
string eventName,
real value
returns nothing
function RegisterDamageEngineEx takes
code callback,
string eventName,
real value,
integer opId
returns nothing
function RegisterDamageEngine takes
code callback,
string eventName,
real value
returns nothing
*/
//===========================================================================
library DamageEngine requires optional AttackIndexer /*, TriggerDebugAutomation*/
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 boolean USE_SCALING = USE_GUI //If you don't need or want to use DamageScalingUser/WC3 then set this to false
private constant boolean USE_EXTRA = true //If you don't use DamageEventLevel or SourceDamageEvent, set this to false
private constant boolean USE_ARMOR_MOD = true //If you do not modify nor detect armor/defense, set this to false
private constant boolean USE_MELEE_RANGE = true //If you do not detect melee nor ranged damage, set this to false
private constant boolean USE_LETHAL = true //If you do not use LethalDamageEvent nor negative damage (explosive) types, set this to false
/*
When manually-enabled recursion is enabled via DamageEngine_inception,
the engine will never go deeper than MAX_RECURSIVE_TOLERANCE:
*/
private constant integer MAX_RECURSIVE_TOLERANCE = 16
public constant integer TYPE_CODE = 1 //Must be the same as udg_DamageTypeCode, or 0 if you prefer to disable the automatic flag.
public constant integer TYPE_PURE = 2 //Must be the same as udg_DamageTypePure
private constant real DEATH_VAL = 0.405 //In case M$ ever changes this, it'll be a quick fix here.
private timer async = null
private boolean timerStarted = false
//Values to track the original pre-spirit Link/defensive damage values
private Damage lastInstance = 0
private boolean isNotNativeRecursiveDamage = true
private boolean waitingForDamageEventToRun = false
private boolean array attacksImmune
private boolean array damagesImmune
//Primary triggers used to handle all damage events.
private trigger damagingTrigger = null
private trigger damagedTrigger = null
private trigger recursiveTrigger = null //Catches, stores recursive events
/*
These variables coincide with Blizzard's "limitop" type definitions
so as to enable GUI users with some performance perks - however,
these optimizations need to be tested
*/
public constant integer FILTER_ATTACK = 0 //LESS_THAN
public constant integer FILTER_MELEE = 1 //LESS_THAN_OR_EQUAL
public constant integer FILTER_OTHER = 2 //EQUAL
public constant integer FILTER_RANGED = 3 //GREATER_THAN_OR_EQUAL
public constant integer FILTER_SPELL = 4 //GREATER_THAN
public constant integer FILTER_CODE = 5 //NOT_EQUAL
public constant integer FILTER_MAX = 6
private integer eventFilter = FILTER_OTHER
/*
When true, it allows your trigger to go recursively up to
MAX_RECURSIVE_TOLERANCE (if needed). It must be set before dealing damage.
*/
public boolean inception = false
private boolean callbacksInProgress = false
private integer recursiveCallbackDepth = 0
private group recursionSources = null
private group recursionTargets = null
private boolean recursiveCallbaksInProgress = false
private boolean nativeEventsCompleted = false
private boolean atLeastOneLethalDamageEventRegistered = false
// Struct members made private to this library.
private keyword run
private keyword trigFrozen
private keyword ownRecursiveDepth
private keyword manualRecursionRequested
endglobals
native UnitAlive takes unit u returns boolean
//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
//Added in 5.7 to enable efficient, built-in filtering (see the below "checkConfig" method - I recommend commenting-out anything you don't need in your map)
integer udg_DamageFilterAttackT
integer udg_DamageFilterDamageT //filter for a specific attack/damage type
unit udg_DamageFilterSource
unit udg_DamageFilterTarget //filter for a specific source/target
integer udg_DamageFilterSourceT
integer udg_DamageFilterTargetT //unit type of source/target
integer udg_DamageFilterType //which DamageEventType was used
integer udg_DamageFilterSourceB
integer udg_DamageFilterTargetB //if source/target has a buff
real udg_DamageFilterMinAmount //only allow a minimum damage threshold
//Added in 5.8:
boolean udg_RemoveDamageEvent //Allow GUI users to more fully unregister a damage event trigger. Can only be used from within a damage event (of any kind).
integer udg_DamageFilterSourceA
integer udg_DamageFilterTargetA //Check if a source or target have a specific ability (will overwrite any source or target buff check, I need to use this because GUI differentiates ability ID and buff ID)
integer udg_DamageFilterSourceI
integer udg_DamageFilterTargetI //Check if a source or target have a specific type of item
integer udg_DamageFilterSourceC
integer udg_DamageFilterTargetC //Classification of source/target (e.g. hero, treant, ward)
//Added in 5.9
real udg_SourceDamageEvent //Like AOEDamageEvent, fires each time the source unit has finished dealing damage, but doesn't care if the damage hit multiple units.
real udg_PreDamageEvent //Like DamageModifierEvent 3.99 or less, except can be any real value.
real udg_ArmorDamageEvent //Like DamageModifierEvent 4.00 or more, except can be any real value.
real udg_OnDamageEvent //Like DamageEvent equal to 1.00 or some non-zero/non-2 value, except can be any real value.
real udg_ZeroDamageEvent //Like DamageEvent equal to 0.00 or 2.00, except can be any real value.
*/
struct DamageTrigger extends array
static method checkItem takes unit u, integer id returns boolean
local integer i
if IsUnitType(u, UNIT_TYPE_HERO) then
set i = UnitInventorySize(u)
loop
exitwhen i <= 0
set i = i - 1
if GetItemTypeId(UnitItemInSlot(u, i)) == id then
return true
endif
endloop
endif
return false
endmethod
/*
Map makers should probably not use these filters,
unless someone tests performance to see
if such an ugly hack is even worth it.
*/
method checkConfig takes nothing returns boolean
//call BJDebugMsg("Checking configuration")
if this.sourceType != 0 and GetUnitTypeId(udg_DamageEventSource) != this.sourceType then
elseif this.targetType != 0 and GetUnitTypeId(udg_DamageEventTarget) != this.targetType then
elseif this.sourceBuff != 0 and GetUnitAbilityLevel(udg_DamageEventSource, this.sourceBuff) == 0 then
elseif this.targetBuff != 0 and GetUnitAbilityLevel(udg_DamageEventTarget, this.targetBuff) == 0 then
elseif this.failChance > 0.00 and GetRandomReal(0.00, 1.00) <= this.failChance then
elseif this.userType != 0 and udg_DamageEventType != this.userType then
elseif this.source != null and this.source != udg_DamageEventSource then
elseif this.target != null and this.target != udg_DamageEventTarget then
elseif this.attackType >= 0 and this.attackType != udg_DamageEventAttackT then
elseif this.damageType >= 0 and this.damageType != udg_DamageEventDamageT then
elseif this.sourceItem != 0 and not .checkItem(udg_DamageEventSource, this.sourceItem) then
elseif this.targetItem != 0 and not .checkItem(udg_DamageEventTarget, this.targetItem) then
elseif this.sourceClass >= 0 and not IsUnitType(udg_DamageEventSource, ConvertUnitType(this.sourceClass)) then
elseif this.targetClass >= 0 and not IsUnitType(udg_DamageEventTarget, ConvertUnitType(this.targetClass)) then
elseif udg_DamageEventAmount >= this.damageMin then
//call BJDebugMsg("Configuration passed")
return true
endif
//call BJDebugMsg("Checking failed")
return false
endmethod
//The below variables are to be treated as constant
readonly static thistype MOD = 1
readonly static thistype SHIELD = 4
readonly static thistype DAMAGE = 5
readonly static thistype ZERO = 6
readonly static thistype AFTER = 7
readonly static thistype LETHAL = 8
readonly static thistype AOE = 9
private static integer count = 9
static thistype lastRegistered = 0
private static thistype array trigIndexStack
static thistype eventIndex = 0
static boolean array filters
readonly string eventStr
readonly real weight
boolean usingGUI
private thistype next
private trigger rootTrig
//The below variables are to be treated as private
boolean trigFrozen //Whether the trigger is currently disabled due to recursion
integer ownRecursiveDepth //How deep the user recursion currently is.
boolean manualRecursionRequested //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent triggers.
//configuration variables:
boolean configured
unit source
unit target
integer sourceType
integer targetType
integer sourceBuff
integer targetBuff
integer sourceItem
integer targetItem
integer sourceClass
integer targetClass
real damageMin
real failChance
integer attackType
integer damageType
integer userType
// getter:
method operator runChance takes nothing returns real
return 1.00 - this.failChance
endmethod
// setter:
method operator runChance= takes real chance returns nothing
set this.failChance = 1.00 - chance
endmethod
method configure takes nothing returns nothing
set this.attackType = udg_DamageFilterAttackT
set this.damageType = udg_DamageFilterDamageT
set this.source = udg_DamageFilterSource
set this.target = udg_DamageFilterTarget
set this.sourceType = udg_DamageFilterSourceT
set this.targetType = udg_DamageFilterTargetT
set this.sourceItem = udg_DamageFilterSourceI
set this.targetItem = udg_DamageFilterTargetI
set this.sourceClass = udg_DamageFilterSourceC
set this.targetClass = udg_DamageFilterTargetC
set this.userType = udg_DamageFilterType
set this.damageMin = udg_DamageFilterMinAmount
set this.failChance = 1.00 - (udg_DamageFilterRunChance - udg_DamageFilterFailChance)
if udg_DamageFilterSourceA > 0 then
set this.sourceBuff = udg_DamageFilterSourceA
set udg_DamageFilterSourceA = 0
else
set this.sourceBuff = udg_DamageFilterSourceB
endif
if udg_DamageFilterTargetA > 0 then
set this.targetBuff = udg_DamageFilterTargetA
set udg_DamageFilterTargetA = 0
else
set this.targetBuff = udg_DamageFilterTargetB
endif
set udg_DamageFilterSource = null
set udg_DamageFilterTarget = null
//These handles can have a valid value of 0, so we need to distinguish them.
set udg_DamageFilterAttackT = -1
set udg_DamageFilterDamageT = -1
set udg_DamageFilterSourceC = -1
set udg_DamageFilterTargetC = -1
set udg_DamageFilterSourceT = 0
set udg_DamageFilterTargetT = 0
set udg_DamageFilterType = 0
set udg_DamageFilterSourceB = 0
set udg_DamageFilterTargetB = 0
set udg_DamageFilterSourceI = 0
set udg_DamageFilterTargetI = 0
set udg_DamageFilterMinAmount = 0.00
set udg_DamageFilterFailChance = 0.00
set udg_DamageFilterRunChance = 1.00
set this.configured = true
endmethod
static method setGUIFromStruct takes boolean full returns nothing
set udg_DamageEventAmount = Damage.index.damage
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
static if USE_ARMOR_MOD then
set udg_DamageEventArmorPierced = Damage.index.armorPierced
set udg_DamageEventArmorT = Damage.index.armorType
set udg_DamageEventDefenseT = Damage.index.defenseType
endif
if full then
set udg_DamageEventSource = Damage.index.sourceUnit
set udg_DamageEventTarget = Damage.index.targetUnit
set udg_DamageEventPrevAmt = Damage.index.prevAmt
set udg_IsDamageAttack = Damage.index.isAttack
set udg_IsDamageCode = Damage.index.isCode
set udg_IsDamageSpell = Damage.index.isSpell
//! runtextmacro optional ATTACK_INDEXER_GUI_VARS()
static if USE_MELEE_RANGE then
set udg_IsDamageMelee = Damage.index.isMelee
set udg_IsDamageRanged = Damage.index.isRanged
endif
endif
endmethod
static method setStructFromGUI takes nothing returns nothing
set Damage.index.damage = 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
static if USE_ARMOR_MOD then
set Damage.index.armorPierced = udg_DamageEventArmorPierced
set Damage.index.armorType = udg_DamageEventArmorT
set Damage.index.defenseType = udg_DamageEventDefenseT
endif
endmethod
static method getVerboseStr takes string eventName returns string
if eventName == "Modifier" or eventName == "Mod" then
return "udg_DamageModifierEvent"
endif
return "udg_" + eventName + "DamageEvent"
endmethod
private static method getStrIndex takes string var, real lbs returns thistype
local integer root = R2I(lbs)
if (var == "udg_DamageModifierEvent" and root < 4) or var == "udg_PreDamageEvent" then
set root = MOD
elseif var == "udg_DamageModifierEvent" or var == "udg_ArmorDamageEvent" then
set root = SHIELD
elseif (var == "udg_DamageEvent" and root == 2 or root == 0) or var == "udg_ZeroDamageEvent" then
set root = ZERO
elseif var == "udg_DamageEvent" or var == "udg_OnDamageEvent" then
set root = DAMAGE
elseif var == "udg_AfterDamageEvent" then
set root = AFTER
elseif var == "udg_LethalDamageEvent" then
set root = LETHAL
elseif var == "udg_AOEDamageEvent" or var == "udg_SourceDamageEvent" then
set root = AOE
else
set root = 0
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_05()
endif
return root
endmethod
private method toggleAllFilters takes boolean flag returns nothing
set filters[this + FILTER_ATTACK] = flag
set filters[this + FILTER_MELEE] = flag
set filters[this + FILTER_OTHER] = flag
set filters[this + FILTER_RANGED] = flag
set filters[this + FILTER_SPELL] = flag
set filters[this + FILTER_CODE] = flag
endmethod
method operator filter= takes integer opId returns nothing
set this = this * FILTER_MAX
if opId == FILTER_OTHER then
call this.toggleAllFilters(true)
else
if opId == FILTER_ATTACK then
set filters[this + FILTER_ATTACK] = true
set filters[this + FILTER_MELEE] = true
set filters[this + FILTER_RANGED] = true
else
set filters[this + opId] = true
endif
endif
endmethod
static method registerVerbose takes /*
*/ trigger whichTrig, /*
*/ string var, /*
*/ real lbs, /*
*/ boolean GUI, /*
*/ integer filt /*
*/ returns thistype
local thistype index = getStrIndex(var, lbs)
local thistype i = 0
local thistype id = 0
if index == 0 then
return 0
elseif lastRegistered.rootTrig == whichTrig and lastRegistered.usingGUI then
//allows GUI to register multiple different types of Damage filters to the same trigger
set filters[lastRegistered*FILTER_MAX + filt] = true
return 0
endif
set atLeastOneLethalDamageEventRegistered = /*
*/ atLeastOneLethalDamageEventRegistered or index == LETHAL
if trigIndexStack[0] == 0 then
set count = count + 1 //List runs from index 10 and up
set id = count
else
set id = trigIndexStack[0]
set trigIndexStack[0] = trigIndexStack[id]
endif
set lastRegistered = id
set id.filter = filt
set id.rootTrig = whichTrig
set id.usingGUI = GUI
set id.weight = lbs
set id.eventStr = var
//Next 2 lines added to fix a bug when using manual vJass configuration,
//discovered and solved by lolreported
set id.attackType = -1
set id.damageType = -1
//they will probably bug out with class types as well, so I should add them, just in case:
set id.sourceClass = -1
set id.targetClass = -1
loop
set i = index.next
exitwhen i == 0 or lbs < i.weight
set index = i
endloop
set index.next = id
set id.next = i
//call BJDebugMsg("Registered " + I2S(id) + " to " + I2S(index) + " and before " + I2S(i))
return lastRegistered
endmethod
static method registerTrigger takes trigger t, string var, real lbs returns thistype
return registerVerbose(t, DamageTrigger.getVerboseStr(var), lbs, false, FILTER_OTHER)
endmethod
private static thistype prev = 0
static method getIndex takes /*
*/ trigger t, /*
*/ string eventName, /*
*/ real lbs /*
*/ returns thistype
local thistype index = getStrIndex(getVerboseStr(eventName), lbs)
loop
set prev = index
set index = index.next
exitwhen index == 0 or index.rootTrig == t
endloop
return index
endmethod
method unregisterByIndex takes boolean reset returns boolean
if this == 0 then
return false
endif
set prev.next = this.next
set trigIndexStack[this] = trigIndexStack[0]
set trigIndexStack[0] = this
if reset then
call this.configure()
set this.configured = false
call thistype(this*FILTER_MAX).toggleAllFilters(false)
endif
return true
endmethod
static method unregister takes /*
*/ trigger t, /*
*/ string eventName, /*
*/ real lbs, /*
*/ boolean reset /*
*/ returns boolean
return getIndex(t, eventName, lbs).unregisterByIndex(reset)
endmethod
method run takes nothing returns nothing
local integer cat = this
local Damage d = Damage.index
static if USE_GUI then
local boolean structUnset = false
local boolean guiUnset = false
local boolean mod = cat <= DAMAGE
endif
if callbacksInProgress then
return
endif
set callbacksInProgress = true
call DisableTrigger(damagingTrigger)
call DisableTrigger(damagedTrigger)
call EnableTrigger(recursiveTrigger)
//call BJDebugMsg("Start of event running")
loop
set this = this.next
exitwhen this == 0
exitwhen cat == MOD and (udg_DamageEventOverride or udg_DamageEventType == TYPE_PURE)
exitwhen cat == SHIELD and udg_DamageEventAmount <= 0.00
static if USE_LETHAL then
exitwhen cat == LETHAL and udg_LethalDamageHP > DEATH_VAL
endif
set eventIndex = this
if (not this.trigFrozen) and /*
*/ filters[this*FILTER_MAX + d.eFilter] and /*
*/ IsTriggerEnabled(this.rootTrig) and /*
*/ ((not this.configured) or (this.checkConfig())) and /*
*/ (cat != AOE or udg_DamageEventAOE > 1 or this.eventStr == "udg_SourceDamageEvent") /*
*/ then
static if USE_GUI then
if mod then
if this.usingGUI then
if guiUnset then
set guiUnset = false
call setGUIFromStruct(false)
endif
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_PDD()
elseif structUnset then
set structUnset = false
call setStructFromGUI()
endif
endif
endif
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_05()
//JASS users who do not use actions can modify the below block to just evaluate.
//It should not make any perceptable difference in terms of performance.
if TriggerEvaluate(this.rootTrig) then
call TriggerExecute(this.rootTrig)
endif
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_05()
static if USE_GUI then
if mod then
if this.usingGUI then
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_PDD()
if cat != MOD then
set d.damage = udg_DamageEventAmount
else
set structUnset = true
endif
elseif cat != MOD then
set udg_DamageEventAmount = d.damage
else
set guiUnset = true
endif
endif
if udg_RemoveDamageEvent then
set udg_RemoveDamageEvent = false
call this.unregisterByIndex(true)
endif
endif
endif
endloop
static if USE_GUI then
if structUnset then
call setStructFromGUI()
endif
if guiUnset then
call setGUIFromStruct(false)
endif
else
call setGUIFromStruct(false)
endif
//call BJDebugMsg("End of event running")
call DisableTrigger(recursiveTrigger)
call EnableTrigger(damagingTrigger)
call EnableTrigger(damagedTrigger)
set callbacksInProgress = false
endmethod
/*
Used by RegisterDamageEngineEx to create triggers behind-the-scenes,
allowing the user to simply pass the function they want to execute.
*/
static trigger array autoTriggers
static boolexpr array autoFuncs
static integer autoN = 0
static method operator [] takes code callback returns trigger
local integer i = 0
local boolexpr b = Filter(callback)
loop
if i == autoN then
set autoTriggers[i] = CreateTrigger()
set autoFuncs[i] = b
call TriggerAddCondition(autoTriggers[i], b)
//call TriggerDebugAutomation_TriggerIsReady(autoTriggers[i], "autoTriggers_" + I2S(i))
exitwhen true
endif
set i = i + 1
exitwhen b == autoFuncs[i]
endloop
return autoTriggers[i]
endmethod
endstruct
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_05()
struct Damage extends array
readonly unit sourceUnit
readonly unit targetUnit
real damage
readonly real prevAmt
attacktype attackType
damagetype damageType
weapontype weaponType
integer userType
readonly boolean isAttack
readonly boolean isCode
readonly boolean isSpell
static if USE_MELEE_RANGE then
readonly boolean isMelee //stores udg_IsDamageMelee
endif
readonly boolean isRanged //stores udg_IsDamageRanged
readonly integer eFilter //stores the previous eventFilter variable
static if USE_ARMOR_MOD then
real armorPierced //stores udg_DamageEventArmorPierced
integer armorType //stores udg_DamageEventArmorT
integer defenseType //stores udg_DamageEventDefenseT
endif
readonly static Damage index = 0
private static Damage damageStack = 0
private static Damage prepped = 0
private static integer count = 0 //The number of currently-running queued or sequential damage instances
private Damage stackRef
private DamageTrigger recursiveTrig
private integer prevArmorT
private integer prevDefenseT
static method operator source takes nothing returns unit
return udg_DamageEventSource
endmethod
static method operator target takes nothing returns unit
return udg_DamageEventTarget
endmethod
static method operator amount takes nothing returns real
return Damage.index.damage
endmethod
static method operator amount= takes real r returns nothing
set Damage.index.damage = r
endmethod
static if USE_ARMOR_MOD then
private method setArmor takes boolean reset returns nothing
local real pierce
local integer at
local integer dt
if reset then
set pierce = udg_DamageEventArmorPierced
set at = Damage.index.prevArmorT
set dt = Damage.index.prevDefenseT
set udg_DamageEventArmorPierced = 0.00
set this.armorPierced = 0.00
else
set pierce = -udg_DamageEventArmorPierced
set at = udg_DamageEventArmorT
set dt = udg_DamageEventDefenseT
endif
if not (pierce == 0.00) then //Changed from != to not == due to bug reported by BLOKKADE
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)
endif
if Damage.index.prevDefenseT != udg_DamageEventDefenseT then
call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, dt)
endif
endmethod
endif
static if USE_EXTRA then
private static method onAOEEnd takes nothing returns nothing
call DamageTrigger.AOE.run()
set udg_DamageEventAOE = 1
set udg_DamageEventLevel = 1
set udg_EnhancedDamageTarget = null
set udg_AOEDamageSource = null
call GroupClear(udg_DamageEventAOEGroup)
endmethod
endif
private static method afterDamage takes nothing returns nothing
if udg_DamageEventDamageT != 0 and not (udg_DamageEventPrevAmt == 0.00) then
call DamageTrigger.AFTER.run()
set udg_DamageEventDamageT = 0
set udg_DamageEventPrevAmt = 0.00
endif
endmethod
private method runDamagingEvents takes boolean natural returns boolean
static if USE_ARMOR_MOD then
set this.armorType = BlzGetUnitIntegerField(this.targetUnit, UNIT_IF_ARMOR_TYPE)
set this.defenseType = BlzGetUnitIntegerField(this.targetUnit, UNIT_IF_DEFENSE_TYPE)
set this.prevArmorT = this.armorType
set this.prevDefenseT = this.defenseType
set this.armorPierced = 0.00
endif
set Damage.index = this
call DamageTrigger.setGUIFromStruct(true)
call GroupAddUnit(recursionSources, udg_DamageEventSource)
call GroupAddUnit(recursionTargets, udg_DamageEventTarget)
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_05()
// Using not == instead of !=; the idea is to eliminate floating point bugs when two numbers are very close to 0,
// because JASS uses a less-strict comparison for checking if a number is equal than when it is unequal.
if not (udg_DamageEventAmount == 0.00) then
set udg_DamageEventOverride = udg_DamageEventDamageT == 0
call DamageTrigger.MOD.run()
static if not USE_GUI then
call DamageTrigger.setGUIFromStruct(false)
endif
if natural then
call BlzSetEventAttackType(this.attackType)
call BlzSetEventDamageType(this.damageType)
call BlzSetEventWeaponType(this.weaponType)
call BlzSetEventDamage(udg_DamageEventAmount)
endif
static if USE_ARMOR_MOD then
call this.setArmor(false)
endif
return false
endif
return true //return value is based on whether the event is a 0 damage event (true) or not (false).
endmethod
private static method unfreeze takes nothing returns nothing
local Damage i = damageStack
loop
exitwhen i == 0
set i = i - 1
set i.stackRef.recursiveTrig.trigFrozen = false
set i.stackRef.recursiveTrig.ownRecursiveDepth = 0
endloop
call EnableTrigger(damagingTrigger)
call EnableTrigger(damagedTrigger)
set recursiveCallbaksInProgress = false
set damageStack = 0
set prepped = 0
set callbacksInProgress = false
set recursiveCallbackDepth = 0
call GroupClear(recursionSources)
call GroupClear(recursionTargets)
//call BJDebugMsg("Cleared up the groups")
endmethod
static method runAfterDamageEvents takes nothing returns nothing
local Damage i = 0
local integer exit
if nativeEventsCompleted then
set nativeEventsCompleted = false
call afterDamage()
endif
if isNotNativeRecursiveDamage and not recursiveCallbaksInProgress then
if damageStack != 0 then
set recursiveCallbaksInProgress = true
loop
/*
Use two loops. The outer loop handles all normal event
execution, while the inner loop intelligently handles
recursive execution (when it's used).
*/
set recursiveCallbackDepth = recursiveCallbackDepth + 1
set exit = damageStack
loop
set prepped = i.stackRef
if UnitAlive(prepped.targetUnit) then
// We don't need to trigger `damagingTrigger` itself, so just call its handler directly.
call prepped.runDamagingEvents(false)
if prepped.damage > 0.00 then
call DisableTrigger(damagingTrigger) // Disallow `damagingTrigger` because we only want `damageTrigger` to run.
call EnableTrigger(damagedTrigger) // Re-enable `damagedTrigger` in case the user forgot to do so.
set waitingForDamageEventToRun = true
call UnitDamageTarget( /*
*/ prepped.sourceUnit, /*
*/ prepped.targetUnit, /*
*/ prepped.damage, /*
*/ prepped.isAttack, /*
*/ prepped.isRanged, /*
*/ prepped.attackType, /*
*/ prepped.damageType, /*
*/ prepped.weaponType /*
*/ )
else
if udg_DamageEventDamageT != 0 then
//No native events run at all in this case
call DamageTrigger.DAMAGE.run()
endif
if prepped.damage < 0.00 then
/*
No need for BlzSetEventDamage/UnitDamageTarget here,
because we can safely adjust the unit's life instead.
*/
call SetWidgetLife( /*
*/ prepped.targetUnit, /*
*/ GetWidgetLife(prepped.targetUnit) - prepped.damage /*
*/ )
endif
static if USE_ARMOR_MOD then
call prepped.setArmor(true)
endif
endif
call afterDamage()
endif
set i = i + 1
exitwhen i == exit
endloop
exitwhen i == damageStack
endloop
endif
call unfreeze()
endif
endmethod
private static method failsafeClear takes nothing returns nothing
static if USE_ARMOR_MOD then
call Damage.index.setArmor(true)
endif
set isNotNativeRecursiveDamage = true
set recursiveCallbaksInProgress = false
set waitingForDamageEventToRun = false
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
set nativeEventsCompleted = true
endif
call runAfterDamageEvents()
endmethod
static method operator enabled= takes boolean b returns nothing
if b then
if callbacksInProgress then
call EnableTrigger(recursiveTrigger)
else
call EnableTrigger(damagingTrigger)
call EnableTrigger(damagedTrigger)
endif
else
if callbacksInProgress then
call DisableTrigger(recursiveTrigger)
else
call DisableTrigger(damagingTrigger)
call DisableTrigger(damagedTrigger)
endif
endif
endmethod
static method operator enabled takes nothing returns boolean
return IsTriggerEnabled(damagingTrigger)
endmethod
private static boolean threadCompleted = false
private static method asyncCallbackSafeCallback takes nothing returns nothing
if waitingForDamageEventToRun then
/*
This means that WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
*/
call failsafeClear()
else
set isNotNativeRecursiveDamage = true
set recursiveCallbaksInProgress = false
call runAfterDamageEvents()
endif
static if USE_EXTRA then
call onAOEEnd()
endif
set threadCompleted = true
endmethod
private static method asyncCallback takes nothing returns nothing
set callbacksInProgress = false
set Damage.enabled = true
/*
Open a new thread in case of a thread crash during callback.
*/
call ForForce(bj_FORCE_PLAYER[0], function thistype.asyncCallbackSafeCallback)
if not threadCompleted then
//call BJDebugMsg("DamageEngine issue: thread crashed!")
call unfreeze()
else
set threadCompleted = false
endif
set Damage.count = 0
set Damage.index = 0
set timerStarted = false
//call BJDebugMsg("Timer wrapped up")
endmethod
private method addRecursive takes nothing returns nothing
local DamageTrigger currentIndex
if not (this.damage == 0.00) then
set currentIndex = DamageTrigger.eventIndex
set this.recursiveTrig = currentIndex
if not this.isCode then
/*
If the recursive damage trigger is executed, this can only
mean that the user has manually dealt damage from a trigger.
Hence flag the damage as being 'code' if they didn't already
manually do this.
*/
set this.isCode = true
set this.userType = TYPE_CODE
endif
set inception = inception or /*
*/ currentIndex.manualRecursionRequested
if recursiveCallbaksInProgress and /*
*/ IsUnitInGroup(this.sourceUnit, recursionSources) and /*
*/ IsUnitInGroup(this.targetUnit, recursionTargets) /*
*/ then
if not inception then
set currentIndex.trigFrozen = true
elseif not currentIndex.trigFrozen then
set currentIndex.manualRecursionRequested = true
if currentIndex.ownRecursiveDepth < recursiveCallbackDepth then
set currentIndex.ownRecursiveDepth = /*
*/ currentIndex.ownRecursiveDepth + 1
if currentIndex.ownRecursiveDepth >= MAX_RECURSIVE_TOLERANCE then
set currentIndex.trigFrozen = true
endif
endif
endif
endif
// push the reference to the top of the damage stack.
set damageStack.stackRef = this
set damageStack = damageStack + 1
//call BJDebugMsg("damageStack: " + I2S(damageStack) + " ownRecursiveDepth: " + I2S(currentIndex.ownRecursiveDepth) + " recursiveCallbackDepth: " + I2S(recursiveCallbackDepth))
endif
set inception = false
endmethod
private static method clearNexts takes nothing returns nothing
set udg_NextDamageIsAttack = false
set udg_NextDamageType = 0
set udg_NextDamageWeaponT = 0
static if USE_MELEE_RANGE then
set udg_NextDamageIsMelee = false
set udg_NextDamageIsRanged = false
endif
endmethod
static method create takes /*
*/ unit src, /*
*/ unit tgt, /*
*/ real amt, /*
*/ boolean isAttack, /*
*/ attacktype at, /*
*/ damagetype dt, /*
*/ weapontype wt /*
*/ returns Damage
local Damage d = Damage.count + 1
set Damage.count = d
set d.sourceUnit = src
set d.targetUnit = tgt
set d.damage = amt
set d.prevAmt = amt
set d.damageType = dt
set d.attackType = at
set d.weaponType = wt
set d.isAttack = udg_NextDamageIsAttack or isAttack
set d.isSpell = d.attackType == null and not d.isAttack
return d
endmethod
private static method createFromEvent takes nothing returns Damage
local Damage d = thistype.create( /*
*/ GetEventDamageSource(), /*
*/ GetTriggerUnit(), /*
*/ GetEventDamage(), /*
*/ BlzGetEventIsAttack(), /*
*/ BlzGetEventAttackType(), /*
*/ BlzGetEventDamageType(), /*
*/ BlzGetEventWeaponType() /*
*/ )
set d.isCode = udg_NextDamageType != 0 or /*
*/ udg_NextDamageIsAttack or /*
*/ udg_NextDamageIsRanged or /*
*/ udg_NextDamageIsMelee or /*
*/ d.damageType == DAMAGE_TYPE_MIND or /*
*/ udg_NextDamageWeaponT != 0 or /*
*/ (d.damageType == DAMAGE_TYPE_UNKNOWN and not (d.damage == 0.00))
if d.isCode then
if udg_NextDamageType != 0 then
set d.userType = udg_NextDamageType
else
set d.userType = TYPE_CODE
endif
static if USE_MELEE_RANGE then
set d.isMelee = udg_NextDamageIsMelee
set d.isRanged = udg_NextDamageIsRanged
endif
set d.eFilter = FILTER_CODE
if udg_NextDamageWeaponT != 0 then
set d.weaponType = ConvertWeaponType(udg_NextDamageWeaponT)
set udg_NextDamageWeaponT = 0
endif
else
set d.userType = 0
if d.damageType == DAMAGE_TYPE_NORMAL and d.isAttack then
// Added in version 5.A in order to allow an optional external
// Attack Indexer system to reset the event weapon type to normal.
//! runtextmacro optional ATTACK_INDEXER_ADJUSTMENTS()
static if USE_MELEE_RANGE then
set d.isMelee = IsUnitType(d.sourceUnit, UNIT_TYPE_MELEE_ATTACKER)
set d.isRanged = IsUnitType(d.sourceUnit, UNIT_TYPE_RANGED_ATTACKER)
if d.isMelee and d.isRanged then
// Melee units always play a sound when damaging in WC3,
// so this is an easy check.
set d.isMelee = d.weaponType != null
// In the case where a unit is both ranged and melee,
// the ranged attack plays no sound.
set d.isRanged = not d.isMelee
endif
if d.isMelee then
set d.eFilter = FILTER_MELEE
elseif d.isRanged then
set d.eFilter = FILTER_RANGED
else
set d.eFilter = FILTER_ATTACK
endif
else
set d.eFilter = FILTER_ATTACK
endif
else
if d.isSpell then
set d.eFilter = FILTER_SPELL
else
set d.eFilter = FILTER_OTHER
endif
static if USE_MELEE_RANGE then
// Spells are neither melee nor ranged.
set d.isMelee = false
set d.isRanged = false
endif
endif
endif
call clearNexts()
return d
endmethod
private static method onRecursiveDamageCallback takes nothing returns boolean
local Damage d = Damage.createFromEvent()
call d.addRecursive()
call BlzSetEventDamage(0.00)
return false
endmethod
private static method onDamagingCallback takes nothing returns boolean
local Damage d = Damage.createFromEvent()
//call BJDebugMsg("Pre-damage event running for " + GetUnitName(GetTriggerUnit()))
if timerStarted then
if waitingForDamageEventToRun then
//WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
if d.damageType == DAMAGE_TYPE_SPIRIT_LINK or /*
*/ d.damageType == DAMAGE_TYPE_DEFENSIVE or /*
*/ d.damageType == DAMAGE_TYPE_PLANT /*
*/ then
set waitingForDamageEventToRun = false
set lastInstance = Damage.index
set isNotNativeRecursiveDamage = false
else
call failsafeClear() //Not an overlapping event - just wrap it up
endif
else
call runAfterDamageEvents() //wrap up any previous damage index
endif
static if USE_EXTRA then
if d.sourceUnit != udg_AOEDamageSource then
call onAOEEnd()
set udg_AOEDamageSource = d.sourceUnit
set udg_EnhancedDamageTarget = d.targetUnit
elseif d.targetUnit == udg_EnhancedDamageTarget then
set udg_DamageEventLevel= udg_DamageEventLevel + 1
elseif not IsUnitInGroup(d.targetUnit, udg_DamageEventAOEGroup) then
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
endif
else
call TimerStart(async, 0.00, false, function Damage.asyncCallback)
set timerStarted = true
static if USE_EXTRA then
set udg_AOEDamageSource = d.sourceUnit
set udg_EnhancedDamageTarget= d.targetUnit
endif
endif
static if USE_EXTRA then
call GroupAddUnit(udg_DamageEventAOEGroup, d.targetUnit)
endif
if d.runDamagingEvents(true) then
call DamageTrigger.ZERO.run()
set isNotNativeRecursiveDamage = true
call runAfterDamageEvents()
endif
set waitingForDamageEventToRun = lastInstance == 0 or /*
*/ attacksImmune[udg_DamageEventAttackT] or /*
*/ damagesImmune[udg_DamageEventDamageT] or /*
*/ not IsUnitType(udg_DamageEventTarget, UNIT_TYPE_MAGIC_IMMUNE)
return false
endmethod
private static method onDamagedCallback takes nothing returns boolean
local real r = GetEventDamage()
local Damage d = Damage.index
//call BJDebugMsg("Second damage event running for " + GetUnitName(GetTriggerUnit()))
if prepped > 0 then
set prepped = 0
elseif callbacksInProgress or d.prevAmt == 0.00 then
return false
elseif waitingForDamageEventToRun then
set waitingForDamageEventToRun = false
else
/*
This should only happen for native recursive WarCraft 3 damage
such as Spirit Link, Thorns Aura, or Spiked Carapace / Barricades.
*/
call afterDamage()
set Damage.index = lastInstance
set lastInstance = 0
set d = Damage.index
/*
Since the native recursive damage has now wrapped up, we can resume
handling events as normal at this point. This means that the original
target that the DAMAGING event was triggered for is now finally getting
its DAMAGED event.
*/
set isNotNativeRecursiveDamage = true
call DamageTrigger.setGUIFromStruct(true)
endif
static if USE_ARMOR_MOD then
call d.setArmor(true)
endif
static if USE_SCALING then
if not (udg_DamageEventAmount == 0.00) and not (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
if udg_DamageEventPrevAmt == 0.00 then
set udg_DamageScalingUser = 0.00
else
set udg_DamageScalingUser = /*
*/ udg_DamageEventAmount / udg_DamageEventPrevAmt
endif
endif
endif
set udg_DamageEventAmount = r
set d.damage = r
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_05()
if udg_DamageEventAmount > 0.00 then
call DamageTrigger.SHIELD.run()
static if not USE_GUI then
set udg_DamageEventAmount = d.damage
endif
static if USE_LETHAL then
if atLeastOneLethalDamageEventRegistered or udg_DamageEventType < 0 then
set udg_LethalDamageHP = /*
*/ GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
if udg_LethalDamageHP <= DEATH_VAL then
if atLeastOneLethalDamageEventRegistered then
call DamageTrigger.LETHAL.run()
set udg_DamageEventAmount = /*
*/ GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
set d.damage = udg_DamageEventAmount
endif
if udg_DamageEventType < 0 and /*
*/ udg_LethalDamageHP <= DEATH_VAL /*
*/ then
call SetUnitExploded(udg_DamageEventTarget, true)
endif
endif
endif
endif
static if USE_SCALING then
if udg_DamageEventPrevAmt == 0.00 or /*
*/ udg_DamageScalingWC3 == 0.00 /*
*/ then
set udg_DamageScalingUser = 0.00
else
set udg_DamageScalingUser = /*
*/ udg_DamageEventAmount / udg_DamageEventPrevAmt / udg_DamageScalingWC3
endif
endif
endif
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
endif
call BlzSetEventDamage(udg_DamageEventAmount)
set nativeEventsCompleted = true
if udg_DamageEventAmount == 0.00 then
call runAfterDamageEvents()
endif
// This return statement was needed years ago to avoid potential crashes on Mac.
// I am not sure if that's still a thing.
return false
endmethod
static method apply takes /*
*/ unit src, /*
*/ unit tgt, /*
*/ real amt, /*
*/ boolean a, /*
*/ boolean r, /*
*/ attacktype at, /*
*/ damagetype dt, /*
*/ weapontype wt /*
*/ returns Damage
local Damage d
if udg_NextDamageType == 0 then
set udg_NextDamageType = TYPE_CODE
endif
if callbacksInProgress then
set d = create(src, tgt, amt, a, at, dt, wt)
set d.isCode = true
set d.eFilter = FILTER_CODE
set d.userType = udg_NextDamageType
static if USE_MELEE_RANGE then
if not d.isSpell then
set d.isRanged = udg_NextDamageIsRanged or r
set d.isMelee = not d.isRanged
endif
endif
call d.addRecursive()
else
call UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
set d = Damage.index
call runAfterDamageEvents()
endif
call clearNexts()
return d
endmethod
static method applySpell takes /*
*/ unit src, /*
*/ unit tgt, /*
*/ real amt, /*
*/ damagetype dt /*
*/ returns Damage
return apply(src, tgt, amt, false, false, null, dt, null)
endmethod
static method applyAttack takes /*
*/ unit src, /*
*/ unit tgt, /*
*/ real amt, /*
*/ boolean ranged, /*
*/ attacktype at, /*
*/ weapontype wt /*
*/ returns Damage
return apply(src, tgt, amt, true, ranged, at, DAMAGE_TYPE_NORMAL, wt)
endmethod
/*
This part is the most critical to get things kicked off. All the code we've seen up until now
is related to event handling, trigger assignment, edge cases, etc. But it's the following that
is really quite esesntial for any damage engine - not just this one.
*/
private static method onInit takes nothing returns nothing
set async = CreateTimer()
set recursionSources = CreateGroup()
set recursionTargets = CreateGroup()
set damagingTrigger = CreateTrigger()
set damagedTrigger = CreateTrigger()
set recursiveTrigger = CreateTrigger() //Moved from globals block as per request of user Ricola3D
call TriggerRegisterAnyUnitEventBJ(damagingTrigger, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(damagingTrigger, Filter(function Damage.onDamagingCallback))
//call TriggerDebugAutomation_TriggerIsReady(damagingTrigger, "damagingTrigger")
call TriggerRegisterAnyUnitEventBJ(damagedTrigger, EVENT_PLAYER_UNIT_DAMAGED)
call TriggerAddCondition(damagedTrigger, Filter(function Damage.onDamagedCallback))
//call TriggerDebugAutomation_TriggerIsReady(damagedTrigger, "damagedTrigger")
//For recursion
call TriggerRegisterAnyUnitEventBJ(recursiveTrigger, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(recursiveTrigger, Filter(function Damage.onRecursiveDamageCallback))
call DisableTrigger(recursiveTrigger) //starts disabled. Will be enabled during recursive event handling.
//call TriggerDebugAutomation_TriggerIsReady(recursiveTrigger, "recursiveTrigger")
/*
For preventing Thorns/Defensive glitch.
Data gathered from https://www.hiveworkshop.com/threads/repo-in-progress-mapping-damage-types-to-their-abilities.316271/
*/
set attacksImmune[0] = false //ATTACK_TYPE_NORMAL
set attacksImmune[1] = true //ATTACK_TYPE_MELEE
set attacksImmune[2] = true //ATTACK_TYPE_PIERCE
set attacksImmune[3] = true //ATTACK_TYPE_SIEGE
set attacksImmune[4] = false //ATTACK_TYPE_MAGIC
set attacksImmune[5] = true //ATTACK_TYPE_CHAOS
set attacksImmune[6] = true //ATTACK_TYPE_HERO
set damagesImmune[0] = true //DAMAGE_TYPE_UNKNOWN
set damagesImmune[4] = true //DAMAGE_TYPE_NORMAL
set damagesImmune[5] = true //DAMAGE_TYPE_ENHANCED
set damagesImmune[8] = false //DAMAGE_TYPE_FIRE
set damagesImmune[9] = false //DAMAGE_TYPE_COLD
set damagesImmune[10] = false //DAMAGE_TYPE_LIGHTNING
set damagesImmune[11] = true //DAMAGE_TYPE_POISON
set damagesImmune[12] = true //DAMAGE_TYPE_DISEASE
set damagesImmune[13] = false //DAMAGE_TYPE_DIVINE
set damagesImmune[14] = false //DAMAGE_TYPE_MAGIC
set damagesImmune[15] = false //DAMAGE_TYPE_SONIC
set damagesImmune[16] = true //DAMAGE_TYPE_ACID
set damagesImmune[17] = false //DAMAGE_TYPE_FORCE
set damagesImmune[18] = false //DAMAGE_TYPE_DEATH
set damagesImmune[19] = false //DAMAGE_TYPE_MIND
set damagesImmune[20] = false //DAMAGE_TYPE_PLANT
set damagesImmune[21] = false //DAMAGE_TYPE_DEFENSIVE
set damagesImmune[22] = true //DAMAGE_TYPE_DEMOLITION
set damagesImmune[23] = true //DAMAGE_TYPE_SLOW_POISON
set damagesImmune[24] = false //DAMAGE_TYPE_SPIRIT_LINK
set damagesImmune[25] = false //DAMAGE_TYPE_SHADOW_STRIKE
set damagesImmune[26] = true //DAMAGE_TYPE_UNIVERSAL
endmethod
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_05()
endstruct
// Called from the GUI configuration trigger once the assignments are in place.
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"
// Added 25 July 2017 to allow detection of things like Bash or Pulverize or AOE spread
set udg_DamageEventAOE = 1
set udg_DamageEventLevel = 1
/*
In-game World Editor doesn't allow Attack Type and Damage Type comparisons.
Therefore, I need to code them as integers into GUI
*/
set udg_ATTACK_TYPE_SPELLS = 0
set udg_ATTACK_TYPE_NORMAL = 1
set udg_ATTACK_TYPE_PIERCE = 2
set udg_ATTACK_TYPE_SIEGE = 3
set udg_ATTACK_TYPE_MAGIC = 4
set udg_ATTACK_TYPE_CHAOS = 5
set udg_ATTACK_TYPE_HERO = 6
// -
set udg_DAMAGE_TYPE_UNKNOWN = 0
set udg_DAMAGE_TYPE_NORMAL = 4
set udg_DAMAGE_TYPE_ENHANCED = 5
set udg_DAMAGE_TYPE_FIRE = 8
set udg_DAMAGE_TYPE_COLD = 9
set udg_DAMAGE_TYPE_LIGHTNING = 10
set udg_DAMAGE_TYPE_POISON = 11
set udg_DAMAGE_TYPE_DISEASE = 12
set udg_DAMAGE_TYPE_DIVINE = 13
set udg_DAMAGE_TYPE_MAGIC = 14
set udg_DAMAGE_TYPE_SONIC = 15
set udg_DAMAGE_TYPE_ACID = 16
set udg_DAMAGE_TYPE_FORCE = 17
set udg_DAMAGE_TYPE_DEATH = 18
set udg_DAMAGE_TYPE_MIND = 19
set udg_DAMAGE_TYPE_PLANT = 20
set udg_DAMAGE_TYPE_DEFENSIVE = 21
set udg_DAMAGE_TYPE_DEMOLITION = 22
set udg_DAMAGE_TYPE_SLOW_POISON = 23
set udg_DAMAGE_TYPE_SPIRIT_LINK = 24
set udg_DAMAGE_TYPE_SHADOW_STRIKE = 25
set udg_DAMAGE_TYPE_UNIVERSAL = 26
/*
The below variables don't affect damage amount, but do affect the sound played
They also give important information about the type of attack used.
They can differentiate between ranged and melee for units who are both
*/
set udg_WEAPON_TYPE_NONE = 0
// Metal Light/Medium/Heavy
set udg_WEAPON_TYPE_ML_CHOP = 1
set udg_WEAPON_TYPE_MM_CHOP = 2
set udg_WEAPON_TYPE_MH_CHOP = 3
set udg_WEAPON_TYPE_ML_SLICE = 4
set udg_WEAPON_TYPE_MM_SLICE = 5
set udg_WEAPON_TYPE_MH_SLICE = 6
set udg_WEAPON_TYPE_MM_BASH = 7
set udg_WEAPON_TYPE_MH_BASH = 8
set udg_WEAPON_TYPE_MM_STAB = 9
set udg_WEAPON_TYPE_MH_STAB = 10
// Wood Light/Medium/Heavy
set udg_WEAPON_TYPE_WL_SLICE = 11
set udg_WEAPON_TYPE_WM_SLICE = 12
set udg_WEAPON_TYPE_WH_SLICE = 13
set udg_WEAPON_TYPE_WL_BASH = 14
set udg_WEAPON_TYPE_WM_BASH = 15
set udg_WEAPON_TYPE_WH_BASH = 16
set udg_WEAPON_TYPE_WL_STAB = 17
set udg_WEAPON_TYPE_WM_STAB = 18
// Claw Light/Medium/Heavy
set udg_WEAPON_TYPE_CL_SLICE = 19
set udg_WEAPON_TYPE_CM_SLICE = 20
set udg_WEAPON_TYPE_CH_SLICE = 21
// Axe Medium
set udg_WEAPON_TYPE_AM_CHOP = 22
// Rock Heavy
set udg_WEAPON_TYPE_RH_BASH = 23
/*
Since GUI still doesn't provide Defense Type and Armor Types,
I needed to include the below:
*/
set udg_ARMOR_TYPE_NONE = 0
set udg_ARMOR_TYPE_FLESH = 1
set udg_ARMOR_TYPE_METAL = 2
set udg_ARMOR_TYPE_WOOD = 3
set udg_ARMOR_TYPE_ETHEREAL = 4
set udg_ARMOR_TYPE_STONE = 5
set udg_DEFENSE_TYPE_LIGHT = 0
set udg_DEFENSE_TYPE_MEDIUM = 1
set udg_DEFENSE_TYPE_HEAVY = 2
set udg_DEFENSE_TYPE_FORTIFIED = 3
set udg_DEFENSE_TYPE_NORMAL = 4
set udg_DEFENSE_TYPE_HERO = 5
set udg_DEFENSE_TYPE_DIVINE = 6
set udg_DEFENSE_TYPE_UNARMORED = 7
/*
The remaining stuff is an ugly 'optimization' that I did a long
time ago, thinking that it would improve performance for GUI users
by not having so many different triggerconditions evaluating per
damage event. I am not sure if it even worked; in Lua it might
perform worse, but in vJass it remains to be tested.
*/
set udg_UNIT_CLASS_HERO = 0
set udg_UNIT_CLASS_DEAD = 1
set udg_UNIT_CLASS_STRUCTURE = 2
set udg_UNIT_CLASS_FLYING = 3
set udg_UNIT_CLASS_GROUND = 4
set udg_UNIT_CLASS_ATTACKS_FLYING = 5
set udg_UNIT_CLASS_ATTACKS_GROUND = 6
set udg_UNIT_CLASS_MELEE = 7
set udg_UNIT_CLASS_RANGED = 8
set udg_UNIT_CLASS_GIANT = 9
set udg_UNIT_CLASS_SUMMONED = 10
set udg_UNIT_CLASS_STUNNED = 11
set udg_UNIT_CLASS_PLAGUED = 12
set udg_UNIT_CLASS_SNARED = 13
set udg_UNIT_CLASS_UNDEAD = 14
set udg_UNIT_CLASS_MECHANICAL = 15
set udg_UNIT_CLASS_PEON = 16
set udg_UNIT_CLASS_SAPPER = 17
set udg_UNIT_CLASS_TOWNHALL = 18
set udg_UNIT_CLASS_ANCIENT = 19
set udg_UNIT_CLASS_TAUREN = 20
set udg_UNIT_CLASS_POISONED = 21
set udg_UNIT_CLASS_POLYMORPHED = 22
set udg_UNIT_CLASS_SLEEPING = 23
set udg_UNIT_CLASS_RESISTANT = 24
set udg_UNIT_CLASS_ETHEREAL = 25
set udg_UNIT_CLASS_MAGIC_IMMUNE = 26
set udg_DamageFilterAttackT = -1
set udg_DamageFilterDamageT = -1
set udg_DamageFilterSourceC = -1
set udg_DamageFilterTargetC = -1
set udg_DamageFilterRunChance = 1.00
endfunction
public function RegisterFromHook takes /*
*/ trigger whichTrig, /*
*/ string var, /*
*/ limitop op, /*
*/ real value /*
*/ returns nothing
call DamageTrigger.registerVerbose(whichTrig, var, value, true, GetHandleId(op))
endfunction
hook TriggerRegisterVariableEvent RegisterFromHook
function TriggerRegisterDamageEngineEx takes /*
*/ trigger whichTrig, /*
*/ string eventName, /*
*/ real value, /*
*/ integer opId /*
*/ returns DamageTrigger
return DamageTrigger.registerVerbose( /*
*/ whichTrig, /*
*/ DamageTrigger.getVerboseStr(eventName), /*
*/ value, /*
*/ false, /*
*/ opId /*
*/ )
endfunction
function TriggerRegisterDamageEngine takes /*
*/ trigger whichTrig, /*
*/ string eventName, /*
*/ real value /*
*/ returns DamageTrigger
return DamageTrigger.registerTrigger(whichTrig, eventName, value)
endfunction
function RegisterDamageEngineEx takes /*
*/ code callback, /*
*/ string eventName, /*
*/ real value, /*
*/ integer opId /*
*/ returns DamageTrigger
return TriggerRegisterDamageEngineEx(DamageTrigger[callback], eventName, value, opId)
endfunction
//Similar to TriggerRegisterDamageEvent, but takes code instead of trigger as the first argument.
function RegisterDamageEngine takes /*
*/ code callback, /*
*/ string eventName, /*
*/ real value /*
*/ returns DamageTrigger
return RegisterDamageEngineEx(callback, eventName, value, FILTER_OTHER)
endfunction
/*
The below macros are for GUI to tap into more powerful vJass event filtering:
*/
//! textmacro DAMAGE_TRIGGER_CONFIG
if not DamageTrigger.eventIndex.configured then
//! endtextmacro
//! textmacro DAMAGE_TRIGGER_CONFIG_END
call DamageTrigger.eventIndex.configure()
endif
if not DamageTrigger.eventIndex.checkConfig() then
return
endif
//! endtextmacro
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//=====================================================================================
// Easy Item Stack 'n Split v2.7.4
// by Dangerb0y
//=====================================================================================
library EasyItemStacknSplit initializer onInit /*requires TriggerDebugAutomation*/
//=====================================================================================
//
// This system adds some much needed item stacking, item splitting, and full inventory
// functionality to Warcraft III.
//
// A couple of useful functions are also included...
//
// - UnitInventoryFull( unit )
// Returns true if all of a unit's inventory slots are occupied, else false.
//
// - UnitStackItem( unit, item )
// Works like UnitAddItem(), but will try to stack items even if inventory is full.
// If excess items from an item stack are dropped, returns the item, else null.
//
//=====================================================================================
// SYSTEM PARAMETERS
//=====================================================================================
globals
// Allow item splitting with double right-click?
private boolean SPLIT = true
// Amount to split from stack... (0 = half)
private integer SPLIT_SIZE = 1
// Allow consecutively split items to stack together?
private boolean SPLIT_STACK = true
private real SPLIT_STACK_DELAY = 2.00
// Allow split items to be dropped if no inventory slots are open?
private boolean SPLIT_DROP = true
// Use item levels to determine stack limit? (false = unlimited stacks)
private boolean USE_ITEM_LEVEL = true
// Full inventory error sound filename... (null = disabled)
private string ERROR_SOUND = "Sound\\Interface\\Error.wav"
private string ERROR_MESSAGE = "Inventory is full."
private integer array DUMMY_ITEM_TYPES
private integer array REAL_ITEM_TYPES
endglobals
function InitDummyItemPairs takes nothing returns nothing
// Since BlzSetItemBooleanField does not work, to allow buying items with full inventory we are forced to do this
// For each item with charges, we created a tome-like dummy copy with "use automatically on acquired" = true.
// Such dummy items can be clicked-bought even with full inventory, contrary to other items.
set DUMMY_ITEM_TYPES[0] = 'I00P' // 1st dummy item bought from shop
set REAL_ITEM_TYPES[0] = 'wild' // 1st real item heroes can use
set DUMMY_ITEM_TYPES[1] = 'I00Q'
set REAL_ITEM_TYPES[1] = 'ankh'
set DUMMY_ITEM_TYPES[2] = 'I00O'
set REAL_ITEM_TYPES[2] = 'pams'
set DUMMY_ITEM_TYPES[3] = 'I00W'
set REAL_ITEM_TYPES[3] = 'I002'
set DUMMY_ITEM_TYPES[4] = 'I00S'
set REAL_ITEM_TYPES[4] = 'whwd'
set DUMMY_ITEM_TYPES[5] = 'I00T'
set REAL_ITEM_TYPES[5] = 'pghe'
set DUMMY_ITEM_TYPES[6] = 'I00U'
set REAL_ITEM_TYPES[6] = 'pgma'
set DUMMY_ITEM_TYPES[7] = 'I00V'
set REAL_ITEM_TYPES[7] = 'pnvu'
set DUMMY_ITEM_TYPES[8] = 'I00X'
set REAL_ITEM_TYPES[8] = 'stwp'
set DUMMY_ITEM_TYPES[9] = 'I00K'
set REAL_ITEM_TYPES[9] = 'I00L'
set DUMMY_ITEM_TYPES[10] = 'I00N'
set REAL_ITEM_TYPES[10] = 'I00Z'
endfunction
//=====================================================================================
// DO NOT EDIT BELOW THIS LINE
//=====================================================================================
globals
private unit array goPickButFullUnits
private unit array splitStackUnits
private item array goPickButFullItems
private item array splitStackItem1s
private item array splitStackItem2s
private real array splitStackDelays
private integer goPickButFullCount = 0
private integer splitStackCount = 0
private timer t = CreateTimer()
endglobals
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// PUBLIC FUNCTION : UnitInventoryFull( unit )
// Checks if all the inventory slots of a unit are occupied.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
function UnitInventoryFull takes unit u returns boolean
local integer inventorySize = UnitInventorySize( u )
local integer slot = 0
loop
exitwhen slot >= inventorySize
if UnitItemInSlot(u, slot) == null then
return false
endif
set slot = slot + 1
endloop
return true
endfunction
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// PUBLIC FUNCTION : FireStackChangedEvent( unit, item )
// Fire an event to signal that the stack size on an item carriet has changed.
// The solution decided for the moment is to trigger a "Unit - is issued an order targeting item" to move it in-place event.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
function FireStackChangedEvent takes unit u, item i returns nothing
local integer inventorySize = UnitInventorySize(u)
local integer slot = 0
// Retrieve the item's slot
loop
if ( UnitItemInSlot(u, slot) == i ) then
exitwhen true // item's slot found
endif
set slot = slot + 1
exitwhen slot >= inventorySize
endloop
if ( slot < inventorySize ) then
call DisableTrigger( gg_trg_EasyItemStacknSplit )
call UnitDropItemSlot( u, i, slot )
call EnableTrigger( gg_trg_EasyItemStacknSplit )
endif
endfunction
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// PUBLIC FUNCTION : UnitStackItem( unit, item )
// Works like UnitAddItem() with full inventory functionality.
// Returns true if excess items are dropped. Otherwise false.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
function UnitStackItem takes unit u, item item1 returns boolean
local integer item1Charges = GetItemCharges( item1 )
local integer inventorySize
local integer item1Level
local integer item1TypeId
local item item2
local integer item2Charges
local integer item2Level
local integer slot
local real posX
local real posY
local real unitAngle
// Make sure the item has charges
if item1Charges <= 0 then
// If not we just give it to the unit
call DisableTrigger( gg_trg_EasyItemStacknSplit )
call UnitAddItem( u, item1 )
call EnableTrigger( gg_trg_EasyItemStacknSplit )
else
// Item has charges
set inventorySize = UnitInventorySize( u )
set item1Level = GetItemLevel( item1 )
set item1TypeId = GetItemTypeId( item1 )
// We can remove the item, we have all the data we need from it
call RemoveItem( item1 )
// Look for items of the same type and try stack onto them
set slot = 0
loop
set item2 = UnitItemInSlot( u, slot )
set item2Charges = GetItemCharges( item2 )
set item2Level = GetItemLevel( item2 )
if item2 != null and (not USE_ITEM_LEVEL or item2Level == 0 or item2Charges < item2Level) and GetItemTypeId(item2) == item1TypeId then
// Found an item with same type and room for some charges
if USE_ITEM_LEVEL and item2Level > 0 and item2Charges + item1Charges > item2Level then
// Not all charges can be stacked onto this item, stack as many as possible and keep the rest
call SetItemCharges( item2, item2Level )
call FireStackChangedEvent(u,item2)
set item1Charges = item2Charges + item1Charges - item2Level
else
// All charges can be stacked onto this item
call SetItemCharges( item2, item2Charges + item1Charges )
call FireStackChangedEvent(u,item2)
set item1Charges = 0
endif
endif
set slot = slot + 1
exitwhen item1Charges <= 0 or slot >= inventorySize
endloop
// If there are any charges left over, look for open slots
if item1Charges > 0 then
// There are charges left
set posX = GetUnitX( u )
set posY = GetUnitY( u )
set slot = 0
loop
// Create as many items as necessary and possible in the unit inventory
set item2 = UnitItemInSlot( u, slot )
if item2 == null then
// There is a free slot: create a new item there for the remaining charges
set item2 = CreateItem( item1TypeId, posX, posY )
if USE_ITEM_LEVEL and item1Level > 0 and item1Charges > item1Level then
// Not all charges can fit in a single item, just put as many as possible
call SetItemCharges( item2, item1Level )
set item1Charges = item1Charges - item1Level
else
// All charges fit in this new item
call SetItemCharges( item2, item1Charges )
set item1Charges = 0
endif
call DisableTrigger( gg_trg_EasyItemStacknSplit )
call UnitAddItem( u, item2 )
call EnableTrigger( gg_trg_EasyItemStacknSplit )
endif
set slot = slot + 1
exitwhen item1Charges <= 0 or slot >= inventorySize
endloop
// If there are still charges left over, drop them on the ground
if item1Charges > 0 then
// There are some charges left that cannot be carried
set unitAngle = GetUnitFacing( u )
set posX = GetUnitX( u ) + 100 * Cos( unitAngle * bj_DEGTORAD )
set posY = GetUnitY( u ) + 100 * Sin( unitAngle * bj_DEGTORAD )
loop
// Create a many items as necessary on the floor
if item1Charges > item1Level then
// Not all charges can find in a single
set item2Charges = item1Level
set item1Charges = item1Charges - item1Level
else
// All charges can fit in a single item
set item2Charges = item1Charges
set item1Charges = 0
endif
set item2 = CreateItem( item1TypeId, posX, posY )
call SetItemCharges( item2, item2Charges )
exitwhen item1Charges <= 0
endloop
return true
endif
endif
endif
// Nothing dropped
return false
endfunction
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// TEXTMACRO : EasyItemStacknSplit_PLAYITEMSOUND( soundname, unitvar )
// Plays item sound for player if the triggering unit is nearby.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! textmacro EasyItemStacknSplit_PLAYITEMSOUND takes FILENAME
set str = "Sound\\Interface\\$FILENAME$.wav"
set snd = CreateSound( str, false, true, false, 12700, 12700, "" )
call AttachSoundToUnit( snd, u )
call SetSoundVolume( snd, 75 )
call SetSoundDistances( snd, 600.0, 1024.0 )
call SetSoundDistanceCutoff( snd, 1536.0 )
if GetLocalPlayer() != p then
call StartSound( snd )
endif
call KillSoundWhenDone( snd )
//! endtextmacro
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// CONTROLLER : TimerController
// EVENTS : Global Timer (t) expires (periodically, 0.05)
// Runs through full-stack and split-stack queues, and works its magic.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private function TimerController takes nothing returns nothing
local unit u
local item item1
local item item2
local integer index
local integer orderId
local real deltax
local real deltay
local real delay
local player p
local string str
local sound snd
// Run through the go-pick-but-full actions stack queue
if goPickButFullCount > 0 then
set index = 0
loop
set u = goPickButFullUnits[index]
set item1 = goPickButFullItems[index]
if u != null and item1 != null and not IsItemOwned(item1) and GetWidgetLife(item1) > 0 and GetWidgetLife(u) > 0 then
// Unit and items are still untouched
set orderId = GetUnitCurrentOrder( u )
set deltax = GetItemX( item1 ) - GetUnitX( u )
set deltay = GetItemY( item1 ) - GetUnitY( u )
if deltax * deltax + deltay * deltay <= 22500 or orderId != 851986 then
// Unit has reached the item, or unit is not currently moving
if orderId == 851986 then
// Unit is moving - get the item and try to stack it
set p = GetOwningPlayer( u )
// Play the "Item Get" sound
//! runtextmacro EasyItemStacknSplit_PLAYITEMSOUND( "PickUpItem" )
call IssueImmediateOrder( u, "stop" )
call SetUnitFacing( u, bj_RADTODEG * Atan2(GetItemY(item1) - GetUnitY(u), GetItemX(item1) - GetUnitX(u)) )
if UnitStackItem(u, item1) then
// Couldn't stack all the charges, somes have been left on the floor, play the "Item Drop" sound
//! runtextmacro EasyItemStacknSplit_PLAYITEMSOUND( "HeroDropItem1" )
endif
endif
set goPickButFullCount = goPickButFullCount - 1
if goPickButFullCount > 0 then
// To fill the hole in the queue, move-in the last action
set goPickButFullUnits[index] = goPickButFullUnits[goPickButFullCount]
set goPickButFullItems[index] = goPickButFullItems[goPickButFullCount]
set index = index - 1
endif
endif
elseif u != null or item1 != null then
// The item has been destroyed or picked by someone else
call IssueImmediateOrder( u, "stop" )
set goPickButFullCount = goPickButFullCount - 1
if goPickButFullCount > 0 then
// To fill the hole in the queue, move-in the last action
set goPickButFullUnits[index] = goPickButFullUnits[goPickButFullCount]
set goPickButFullItems[index] = goPickButFullItems[goPickButFullCount]
set index = index - 1
endif
endif
set index = index + 1
exitwhen index >= goPickButFullCount
endloop
endif
// Run through split-stack actions queue
if SPLIT_STACK and splitStackCount > 0 then
set index = 0
loop
set u = splitStackUnits[index]
set item1 = splitStackItem1s[index]
set item2 = splitStackItem2s[index]
set delay = splitStackDelays[index]
if u != null and item1 != null and item2 != null and delay > 0 and UnitHasItem(u, item1) and UnitHasItem(u, item2) then
// Unit still carries both items - split&stack may have not been resolved yet
set splitStackDelays[index] = delay - 0.05
else
// Split-stack has been finished somehow
set splitStackCount = splitStackCount - 1
if splitStackCount > 0 then
// To fill the hole in the queue, move-in the last action
set splitStackUnits[index] = splitStackUnits[splitStackCount]
set splitStackItem1s[index] = splitStackItem1s[splitStackCount]
set splitStackItem2s[index] = splitStackItem2s[splitStackCount]
set splitStackDelays[index] = splitStackDelays[splitStackCount]
set index = index - 1
endif
endif
set index = index + 1
exitwhen index >= splitStackCount
endloop
endif
// Pause timer if not needed
if goPickButFullCount <= 0 and (not SPLIT_STACK or splitStackCount <= 0) then
// If all timed events have been resolved, pause the timer
call PauseTimer( t )
endif
set u = null
set item1 = null
set p = null
set snd = null
endfunction
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// CONTROLLER : CancelController
// EVENTS : Unit Targets Point
// Flushes trigger-unit and target-item from timer queue.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private function CancelController takes nothing returns boolean
local integer index = 0
if goPickButFullCount > 0 then
loop
if goPickButFullUnits[index] == GetTriggerUnit() and (GetOrderPointX() != GetItemX(goPickButFullItems[index]) or GetOrderPointY() != GetItemY(goPickButFullItems[index])) then
set goPickButFullCount = goPickButFullCount - 1
if goPickButFullCount > 0 then
set goPickButFullUnits[index] = goPickButFullUnits[goPickButFullCount]
set goPickButFullItems[index] = goPickButFullItems[goPickButFullCount]
set index = index - 1
elseif not SPLIT_STACK or splitStackCount <= 0 then
call PauseTimer( t )
endif
endif
set index = index + 1
exitwhen index >= goPickButFullCount
endloop
endif
return false
endfunction
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// CONTROLLER : ActionController
// EVENTS : Unit Acquires Item, Unit Targets Object
// Main system controller. Determines unit order and runs actions accordingly.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private function ActionController takes nothing returns boolean
local eventid eventId = GetTriggerEventId()
local unit u = GetTriggerUnit()
local integer inventorySize = UnitInventorySize(u)
local player p
local integer orderId
local item item1 // Item being manipulated
local integer item1TypeId
local integer item1Charges
local integer item1Level
local item item2 // Either target or newly created item
local integer item2Charges
local integer item2Level
local integer item2Slot
local integer index
local integer splitChargeCount
local location pos
local boolean full
local real unitAngle
local string str
local sound snd
// Only units with not-null inventory are relevant
if (inventorySize == 0) then
return false
endif
// Detect if triggering event is "unit acquires an item" or "unit is issued an order targeting item"
if (eventId == EVENT_PLAYER_UNIT_PICKUP_ITEM) then
// Unit acquired an item
set item1 = GetManipulatedItem()
if (item1 != null) then
// Could it be a dummy item (allow buying items with full inventory)
if ( BlzGetItemBooleanField(item1, ITEM_BF_USE_AUTOMATICALLY_WHEN_ACQUIRED) == true) then
// Compare to the dummy item list
set index = 0
loop
exitwhen (DUMMY_ITEM_TYPES[index] == null or REAL_ITEM_TYPES[index] == null)
if (GetItemTypeId(item1) == DUMMY_ITEM_TYPES[index] ) then
// Dummy item found > replace with the real one
call RemoveItem(item1)
set pos = GetUnitLoc(u)
set item1 = CreateItemLoc( REAL_ITEM_TYPES[index], pos )
call RemoveLocation(pos)
exitwhen true
endif
set index = index + 1
endloop
endif
if ( BlzGetItemBooleanField(item1, ITEM_BF_USE_AUTOMATICALLY_WHEN_ACQUIRED) == true and GetItemCharges(item1) == 1 ) then
// Nothing to add in inventory, all charges will be used on acquisition
else
// The item is being acquired so we stack it
if UnitStackItem(u, item1) then
// Couldnot stack all charges in inventory, some remain on the floor. Play the "Item Drop" sound
set p = GetOwningPlayer( u )
//! runtextmacro EasyItemStacknSplit_PLAYITEMSOUND( "HeroDropItem1.wav" )
endif
endif
endif
elseif (eventId == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER) then
// Unit is issued an order targeting an object: attack, attack once, move to, right click, moveslot<1-6>
set item1 = GetOrderTargetItem()
if (item1 != null) then
if ( BlzGetItemBooleanField(item1, ITEM_BF_USE_AUTOMATICALLY_WHEN_ACQUIRED) == true and GetItemCharges(item1) == 1 ) then
// Nothing to add in inventory, all charges will be used on acquisition
else
set orderId = GetIssuedOrderId()
if (orderId == 851971) then
// The item is on the floor and left clicked (going to pick it)
if UnitInventoryFull(u) then
// The item is being targeted with a full inventory so we add it to the timer queue
set item1Charges = GetItemCharges( item1 )
set index = 0
if ( item1Charges > 0 ) then
// Item with charges, look up if there is an item available to receive those charges in unit inventory
set item1Level = GetItemLevel( item1 )
set item1TypeId = GetItemTypeId( item1 )
set index = 0
loop
set item2 = UnitItemInSlot( u, index )
set item2Level = GetItemLevel( item2 )
if item2 != item1 and GetItemTypeId(item2) == item1TypeId and (not USE_ITEM_LEVEL or item2Level == 0 or GetItemCharges(item2) < item2Level) then
// Found item with same type
set index = inventorySize + 1
else
set index = index + 1
endif
exitwhen index >= inventorySize
endloop
endif
// Is it possible to pick some charges or not ?
if index > inventorySize then
// Inventory has room to receive the charges
set index = 0
if goPickButFullCount > 0 then
// Check if unit is already moving to pick other charges
loop
if goPickButFullUnits[index] == u then
set goPickButFullItems[index] = item1 // Update the parameters
set index = -1
else
set index = index + 1
endif
exitwhen index >= goPickButFullCount or index == -1
endloop
endif
if index >= 0 then
// No previous go-pick-but-full action, just check if the timer should be started and append this action
if goPickButFullCount == 0 then
call TimerStart( t, 0.05, true, function TimerController )
endif
set goPickButFullUnits[goPickButFullCount] = u
set goPickButFullItems[goPickButFullCount] = item1
set goPickButFullCount = goPickButFullCount + 1
endif
// Order the unit to move to the item position
call IssuePointOrder( u, "move", GetItemX(item1), GetItemY(item1) )
else
// Full inventory error
call IssueImmediateOrder( u, "stop" )
set p = GetOwningPlayer( u )
// Play error sound
if ERROR_SOUND != null and ERROR_SOUND != "" then
set str = ERROR_SOUND
set snd = CreateSound( str, false, false, false, 12700, 12700, "" )
call SetSoundVolume( snd, 127 )
if GetLocalPlayer() != p then
call StartSound( snd )
endif
call KillSoundWhenDone( snd )
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetOwningPlayer(udg_EasyItem_unit),0.50,-1.00,2.00,"|cffffcc00"+ERROR_MESSAGE+"|r")
endif
endif
else
// The item will be picked normally when the unit reaches it (a unit acquire item event will be fired by W3)
endif
elseif (orderId > 852001 and orderId < 852008) then
// The unit is issued a moveslot<1-6> order: an item is being moved to another inventory slot
if UnitHasItem(u, item1) then
// Safety check: item comes from the unit inventory
set item1Charges = GetItemCharges( item1 )
if (item1Charges > 0) then
// The moved item has charges
set item2Slot = orderId - 852002 // target slot index (0 to 5)
set item2 = UnitItemInSlot( u, item2Slot )
if GetItemTypeId(item2) == GetItemTypeId(item1) then
// The moved item and target item have the same type or are identical
if item2 == item1 then
// The item is moved on itself - split charges
if SPLIT then
// Split by double right-click is enabled in settings
set full = UnitInventoryFull( u )
if item1Charges > 1 and (SPLIT_DROP or not full) then
// Split is possible (there is room in inventory or drop is enabled in settings)
if SPLIT_SIZE > 0 then
// Split is set to a fixed size in settings
if SPLIT_SIZE >= item1Charges then
// Not enough charges: splitting total quantity minus 1
set splitChargeCount = item1Charges - 1
else
// Enough charges for full-split
set splitChargeCount = SPLIT_SIZE
endif
else
// Splits in half (bottom-rounded)
set splitChargeCount = item1Charges / 2
endif
call SetItemCharges( item1, item1Charges - splitChargeCount )
call FireStackChangedEvent(u, item1)
if SPLIT_STACK then
// Splitted charged stacked on other items is enabled in settings
set item2 = null
if splitStackCount > 0 then
// Cancel timer is on
set index = 0
loop
if u == splitStackUnits[index] then
// This unit already is in the splitstack/cancel timer input
set item2 = splitStackItem2s[index]
set item2Charges = GetItemCharges( item2 )
set item1Charges = GetItemLevel( item2 )
exitwhen true
endif
set index = index + 1
exitwhen index >= splitStackCount
endloop
endif
endif
if SPLIT_STACK and item2 != null and item2 != item1 and splitStackItem1s[index] == item1 and (not USE_ITEM_LEVEL or item1Charges == 0 or item2Charges < item1Charges) and UnitHasItem(u, item2) and GetItemTypeId(item2) == GetItemTypeId(item1) then
// Merge this split-stack with the other entry in the splitstack/cancel timer input
call SetItemCharges( item2, item2Charges + splitChargeCount )
call FireStackChangedEvent(u, item2)
set splitStackDelays[index] = SPLIT_STACK_DELAY
else
// Create a new item with the splitted charges
set unitAngle = GetUnitFacing( u )
set item2 = CreateItem( GetItemTypeId(item1), GetUnitX(u) + 100 * Cos(unitAngle * bj_DEGTORAD), GetUnitY(u) + 100 * Sin(unitAngle * bj_DEGTORAD) )
call SetItemCharges( item2, splitChargeCount )
if not full then
// There is room, give the new item to the unit
call DisableTrigger( gg_trg_EasyItemStacknSplit )
call UnitAddItem( u, item2 )
call EnableTrigger( gg_trg_EasyItemStacknSplit )
if SPLIT_STACK then
// Splitted charged stacked on other items is enabled in settings
set index = 0
if splitStackCount > 0 then
// Cancel timer is on
loop
if splitStackUnits[index] == u then
// This unit already is in the splitstack/cancel timer input - update the previous parameters
set splitStackItem1s[index] = item1
set splitStackItem2s[index] = item2
set splitStackDelays[index] = SPLIT_STACK_DELAY
set index = -1
else
set index = index + 1
endif
exitwhen index >= splitStackCount or index == -1
endloop
endif
if index >= 0 then
// This unit is not already in the cancel timer input
if splitStackCount == 0 then
// If not started, start the timer
call TimerStart( t, 0.05, true, function TimerController )
endif
// Push back this unit to the splitstack/cancel timer input
set splitStackUnits[splitStackCount] = u
set splitStackItem1s[splitStackCount] = item1
set splitStackItem2s[splitStackCount] = item2
set splitStackDelays[splitStackCount] = SPLIT_STACK_DELAY
set splitStackCount = splitStackCount + 1
endif
endif
else
// There is no room for the new item, leave it on the floor and play the "Item Drop" sound
set p = GetOwningPlayer( u )
//! runtextmacro EasyItemStacknSplit_PLAYITEMSOUND( "HeroDropItem1" )
endif
endif
endif
endif
else
// The item is moved on another item of same type - stack them
set item1Level = GetItemLevel( item1 )
set item2Charges = GetItemCharges( item2 )
if USE_ITEM_LEVEL and item1Level > 0 and item2Charges + item1Charges > item1Level then
// Total charges of destination item is limited and that limit is exceeded
if item1Charges < GetItemLevel(item2) and item2Charges < GetItemLevel(item2) then
// Safety check: both items had less than max charge. Stack a max of charges in the destination item, leave the rest in the source item
call SetItemCharges( item2, item2Charges + item1Charges - item1Level )
call FireStackChangedEvent(u, item2)
call SetItemCharges( item1, item1Level )
call FireStackChangedEvent(u, item1)
endif
else
// All the charges can be stacked, source item disapears
call SetItemCharges( item2, item2Charges + item1Charges )
call FireStackChangedEvent(u, item2)
call RemoveItem( item1 )
endif
endif
endif
endif
endif
else
// Other irrelevant orders targeting an item (attack, ...)
endif
endif
endif
else
// OTHER TYPE OF TRIGGERING EVENT - SHOULD NEVER HAPPEN
endif
set u = null
set p = null
set item1 = null
set item2 = null
set snd = null
return false
endfunction
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// CONTROLLER : PreloadController
// EVENTS : Game Time Elapsed = 0.00
// Preloads sound files so that they play the first time around.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private function PreloadController takes nothing returns boolean
local string array str
local sound snd
local integer x = 0
set str[0] = "Sound\\Interface\\PickUpItem.wav"
set str[1] = "Sound\\Interface\\HeroDropItem1.wav"
if ERROR_SOUND != null and ERROR_SOUND != "" then
set str[2] = ERROR_SOUND
endif
loop
exitwhen str[x] == null
set snd = CreateSound( str[x], false, false, false, 12700, 12700, "" )
call SetSoundVolume( snd, 0 )
call StartSound( snd )
call KillSoundWhenDone( snd )
set x = x + 1
endloop
set snd = null
call DestroyTrigger( GetTriggeringTrigger() )
return false
endfunction
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// TRIGGER INITIALIZER
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private function onInit takes nothing returns nothing
local trigger CancelTrigger = CreateTrigger()
local trigger PreloadTrigger = CreateTrigger()
local integer index = 0
call InitDummyItemPairs()
set gg_trg_EasyItemStacknSplit = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent( gg_trg_EasyItemStacknSplit, Player(index), EVENT_PLAYER_UNIT_PICKUP_ITEM, null )
call TriggerRegisterPlayerUnitEvent( gg_trg_EasyItemStacknSplit, Player(index), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null )
call TriggerRegisterPlayerUnitEvent( CancelTrigger, Player(index), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null )
set index = index + 1
exitwhen index >= bj_MAX_PLAYER_SLOTS
endloop
call TriggerRegisterTimerEvent( PreloadTrigger, 0.00, false )
call TriggerAddCondition( gg_trg_EasyItemStacknSplit, function ActionController )
call TriggerAddCondition( CancelTrigger, function CancelController )
call TriggerAddCondition( PreloadTrigger, function PreloadController )
//call TriggerDebugAutomation_TriggerIsReady(CancelTrigger, "CancelTrigger")
//call TriggerDebugAutomation_TriggerIsReady(PreloadTrigger, "PreloadTrigger")
endfunction
endlibrary
library LifeSteal initializer init
// lf = Life Steal => Regen on auto attack damage
// sv = Spell Vamp => Regen on spell damage
// omni => Omni Vamp => Regen on all damage
globals
// ------------------- //
private real array unit_lf
private real array unit_sv
private real array unit_omni
private real array unit_checktime
// ------------------- //
private real array lf_aura_percentage
private real lf_item_percentage
private real lf_skill_percentage
private real array sv_aura_percentage
private real sv_item_percentage
private real sv_skill_percentage
private real array omni_aura_percentage
private real omni_item_percentage
private real omni_skill_percentage
endglobals
private function init takes nothing returns nothing
set lf_aura_percentage[1] = 0.25
set lf_aura_percentage[2] = 0.50
set lf_aura_percentage[3] = 0.75
set lf_aura_percentage[4] = 1.00
set lf_item_percentage = 0.33
set lf_skill_percentage = 0.33
set sv_aura_percentage[1] = 0.40
set sv_aura_percentage[2] = 0.70
set sv_aura_percentage[3] = 1.00
set sv_aura_percentage[4] = 1.00
set sv_item_percentage = 0.55
set sv_skill_percentage = 0.00
set omni_aura_percentage[1] = 0.20
set omni_aura_percentage[2] = 0.40
set omni_aura_percentage[3] = 0.60
set omni_aura_percentage[4] = 0.80
set omni_item_percentage = 0.25
set omni_skill_percentage = 0.00
endfunction
private function ComputeLifeStealPercentage takes unit u returns real
local real lf_percent = 0.00
// ---------------------------------- //
// ----- Muradin's Hammer Aura ------ //
// ---------------------------------- //
if UnitHasBuffBJ(u, 'B01A') then
set lf_percent = lf_percent + lf_aura_percentage[1]
endif
if UnitHasBuffBJ(u, 'B01B') then
set lf_percent = lf_percent + lf_aura_percentage[2]
endif
if UnitHasBuffBJ(u, 'B01C') then
set lf_percent = lf_percent + lf_aura_percentage[3]
endif
if UnitHasBuffBJ(u, 'B01D') then
set lf_percent = lf_percent + lf_aura_percentage[4]
endif
// ---------------------------------- //
// --------- Vampiric Aura ---------- //
// ---------------------------------- //
if UnitHasBuffBJ(u, 'B016') then
set lf_percent = lf_percent + lf_aura_percentage[1]
endif
if UnitHasBuffBJ(u, 'B017') then
set lf_percent = lf_percent + lf_aura_percentage[2]
endif
if UnitHasBuffBJ(u, 'B018') then
set lf_percent = lf_percent + lf_aura_percentage[3]
endif
if UnitHasBuffBJ(u, 'B019') then
set lf_percent = lf_percent + lf_aura_percentage[4]
endif
// ---------------------------------- //
// ---------- Dummy Skill ----------- //
// ---------------------------------- //
if GetUnitAbilityLevelSwapped('A07A', u) > 0 then
set lf_percent = lf_percent + lf_skill_percentage
endif
// ---------------------------------- //
// ------------- Items -------------- //
// ---------------------------------- //
// Mask of Death //
if GetItemOfTypeFromUnitBJ(u, 'modt') != null then
set lf_percent = lf_percent + lf_item_percentage
endif
// Lightning Sword //
if GetItemOfTypeFromUnitBJ(u, 'I00R') != null then
set lf_percent = lf_percent + lf_item_percentage
endif
return lf_percent
endfunction
private function ComputeSpellVampPercentage takes unit u returns real
local real sv_percent = 0.00
// ---------------------------------- //
// ----- Muradin's Hammer Aura ------ //
// ---------------------------------- //
// if UnitHasBuffBJ(u, 'B01A') then
// set sv_percent = sv_percent + sv_aura_percentage[1]
// endif
// if UnitHasBuffBJ(u, 'B01B') then
// set sv_percent = sv_percent + sv_aura_percentage[2]
// endif
// if UnitHasBuffBJ(u, 'B01C') then
// set sv_percent = sv_percent + sv_aura_percentage[3]
// endif
// if UnitHasBuffBJ(u, 'B01D') then
// set sv_percent = sv_percent + sv_aura_percentage[4]
// endif
// ---------------------------------- //
// ---------- Dummy Skill ----------- //
// ---------------------------------- //
// if GetUnitAbilityLevelSwapped('A07A', GetEnumUnit()) > 0 then
// set sv_percent = sv_percent + sv_skill_percentage
// endif
// ---------------------------------- //
// ------------- Items -------------- //
// ---------------------------------- //
// Icy Staff //
if GetItemOfTypeFromUnitBJ(u, 'I01J') != null then
set sv_percent = sv_percent + sv_item_percentage
endif
return sv_percent
endfunction
private function ComputeOmniVampPercentage takes unit u returns real
local real omni_percent = 0.00
// ---------------------------------- //
// ----- Muradin's Hammer Aura ------ //
// ---------------------------------- //
// if UnitHasBuffBJ(u, 'B01A') then
// set omni_percent = omni_percent + omni_aura_percentage[1]
// endif
// if UnitHasBuffBJ(u, 'B01B') then
// set omni_percent = omni_percent + omni_aura_percentage[2]
// endif
// if UnitHasBuffBJ(u, 'B01C') then
// set omni_percent = omni_percent + omni_aura_percentage[3]
// endif
// if UnitHasBuffBJ(u, 'B01D') then
// set omni_percent = omni_percent + omni_aura_percentage[4]
// endif
// ---------------------------------- //
// ---------- Dummy Skill ----------- //
// ---------------------------------- //
// if GetUnitAbilityLevelSwapped('A07A', GetEnumUnit()) > 0 then
// set omni_percent = omni_percent + omni_skill_percentage
// endif
// ---------------------------------- //
// ------------- Items -------------- //
// ---------------------------------- //
// Mask of Death //
// if GetItemOfTypeFromUnitBJ(u, 'modt') != null then
// set omni_percent = omni_percent + omni_item_percentage
// endif
// Nature Saff
if UnitHasBuffBJ(u, 'B029') then
set omni_percent = omni_percent + omni_item_percentage
endif
return omni_percent
endfunction
function ComputeRegenOnDamage takes unit u, real dmg_amount, boolean IsDamageAttack, boolean IsDamageSpell returns real
local real percent = 0.00
local integer unitIndex = GetUnitUserData(u)
local real elapsed_time = TimerGetElapsed(udg_ClockTimer)
// Save unit percentage, and update max once every second, to avoid lag spikes
if elapsed_time - unit_checktime[unitIndex] >= 1.00 then
set unit_lf[unitIndex] = ComputeLifeStealPercentage(u)
set unit_sv[unitIndex] = ComputeSpellVampPercentage(u)
set unit_omni[unitIndex] = ComputeOmniVampPercentage(u)
set unit_checktime[unitIndex] = elapsed_time
endif
set percent = percent + unit_omni[unitIndex]
if IsDamageAttack then
set percent = percent + unit_lf[unitIndex]
endif
if IsDamageSpell then
set percent = percent + unit_sv[unitIndex]
endif
return dmg_amount * percent
endfunction
endlibrary
function RegisterBossUnit takes unit bossUnit returns nothing
call GroupAddUnit(udg_BossUnits, bossUnit)
call EnableTrigger(gg_trg_BossUnregister)
call EnableTrigger(gg_trg_BossRearFlankAttack)
endfunction
library BossTuning initializer init requires Armor
globals
private integer numberOfBossTypes = 0
private string array bossTypes
private integer array baseStat
private real array scalingPerDiffStat
private real array scalingPerPlayerStat
private real array multiplicatorStat
private real array baseArmor
private real array scalingPerDiffArmor
private real array scalingPerPlayerArmor
private real array multiplicatorArmor
endglobals
// Takes a string, returns the index of associated stats
private function getIndex takes string bossType returns integer
local integer i = 0
loop
set i = i +1
exitwhen i > numberOfBossTypes
if (bossType == bossTypes[i]) then
return i
endif
endloop
return 0
endfunction
// Register a new boss type, taking variables at index 0
private function registerBossType takes nothing returns nothing
local integer existing = getIndex(bossTypes[0])
// Check if the boss type already exist
if existing != 0 then
call BJDebugMsg("This boss type already exist")
return
endif
// Register new type
set numberOfBossTypes = numberOfBossTypes +1
set bossTypes[numberOfBossTypes] = bossTypes[0]
set baseStat[numberOfBossTypes] = baseStat[0]
set scalingPerDiffStat[numberOfBossTypes] = scalingPerDiffStat[0]
set scalingPerPlayerStat[numberOfBossTypes] = scalingPerPlayerStat[0]
set multiplicatorStat[numberOfBossTypes] = multiplicatorStat[0]
set baseArmor[numberOfBossTypes] = baseArmor[0]
set scalingPerDiffArmor[numberOfBossTypes] = scalingPerDiffArmor[0]
set scalingPerPlayerArmor[numberOfBossTypes] = scalingPerPlayerArmor[0]
set multiplicatorArmor[numberOfBossTypes] = multiplicatorArmor[0]
endfunction
private function init takes nothing returns nothing
// Stuff at init.
// Register your boss types there
// Wave heroes
set bossTypes[0] = "Wave Hero"
set baseStat[0] = 0
set scalingPerDiffStat[0] = 75.0
set scalingPerPlayerStat[0] = 0.0
set multiplicatorStat[0] = 1.0
set baseArmor[0] = 0
set scalingPerDiffArmor[0] = 10.0
set scalingPerPlayerArmor[0] = 0.0
set multiplicatorArmor[0] = 1.0
call registerBossType()
// Kill Events => Ramero & Baristol (Ring of Superiority & Lightning Sword)
set bossTypes[0] = "Kill Event"
set baseStat[0] = 500
set scalingPerDiffStat[0] = 250.0
set scalingPerPlayerStat[0] = 0.0
set multiplicatorStat[0] = 0.0
set baseArmor[0] = 25.0
set scalingPerDiffArmor[0] = 12.5
set scalingPerPlayerArmor[0] = 0.0
set multiplicatorArmor[0] = 0.0
call registerBossType()
// Circles Event => Frost Infernal & Spirit Beast (Key of Three Moons & Shield of invincibility)
set bossTypes[0] = "Circle Event"
set baseStat[0] = 2000
set scalingPerDiffStat[0] = 1000.0
set scalingPerPlayerStat[0] = 0.0
set multiplicatorStat[0] = 0.0
set baseArmor[0] = 500.0
set scalingPerDiffArmor[0] = 50
set scalingPerPlayerArmor[0] = 0.0
set multiplicatorArmor[0] = 0.0
call registerBossType()
// Bosses on Final Wave
set bossTypes[0] = "Final Wave"
set baseStat[0] = 500
set scalingPerDiffStat[0] = 500.0
set scalingPerPlayerStat[0] = 0.0
set multiplicatorStat[0] = 0.0
set baseArmor[0] = 300.0
set scalingPerDiffArmor[0] = 50.0
set scalingPerPlayerArmor[0] = 0.0
set multiplicatorArmor[0] = 0.0
call registerBossType()
// 4 Captains + Maghteridon
set bossTypes[0] = "Captains"
set baseStat[0] = 2000
set scalingPerDiffStat[0] = 1500.0
set scalingPerPlayerStat[0] = 1500.0
set multiplicatorStat[0] = 0.0
set baseArmor[0] = 500.0
set scalingPerDiffArmor[0] = 50.0
set scalingPerPlayerArmor[0] = 0.0
set multiplicatorArmor[0] = 0.0
call registerBossType()
// Final Bosses => Arthas & Banehallow
set bossTypes[0] = "Final Bosses"
set baseStat[0] = 2000
set scalingPerDiffStat[0] = 1500.0
set scalingPerPlayerStat[0] = 2000.0
set multiplicatorStat[0] = 0.0
set baseArmor[0] = 500.0
set scalingPerDiffArmor[0] = 50.0
set scalingPerPlayerArmor[0] = 0.0
set multiplicatorArmor[0] = 0.0
call registerBossType()
endfunction
// Takes a Boss unit (hero) and set its stats depending on bosstype
// Used globals: game difficulty, game level and number of players
private function SetAttributes takes unit boss, string bossType returns nothing
local integer bossIndex
local integer total_stat
set bossIndex = getIndex(bossType)
if bossIndex == 0 then
call BJDebugMsg("This boss type is invalid")
return
endif
// Compute total stat
set total_stat = baseStat[bossIndex]
set total_stat = total_stat + R2I(scalingPerDiffStat[bossIndex] * Pow(I2R(udg_Difficulty) * (1 + multiplicatorArmor[bossIndex] * I2R(udg_GameLevel[1] -1)), 0.50))
set total_stat = total_stat + R2I(scalingPerPlayerStat[bossIndex] * udg_PlayerCount)
// Set stats
call ModifyHeroStat(bj_HEROSTAT_STR, boss, bj_MODIFYMETHOD_SET, total_stat)
call ModifyHeroStat(bj_HEROSTAT_AGI, boss, bj_MODIFYMETHOD_SET, total_stat)
call ModifyHeroStat(bj_HEROSTAT_INT, boss, bj_MODIFYMETHOD_SET, total_stat)
endfunction
// Takes a Boss unit (hero) and set its stats depending on bosstype
// Used globals: game difficulty, game level and number of players
private function SetArmor takes unit boss, string bossType returns nothing
local integer bossIndex
local real total_armor
set bossIndex = getIndex(bossType)
if bossIndex == 0 then
call BJDebugMsg("This boss type is invalid")
return
endif
// Compute total armor
set total_armor = baseArmor[bossIndex]
set total_armor = total_armor + scalingPerDiffArmor[bossIndex] * Pow(I2R(udg_Difficulty) * (1 + multiplicatorArmor[bossIndex] * udg_GameLevel[1]), 0.50)
set total_armor = total_armor + scalingPerPlayerArmor[bossIndex] * udg_PlayerCount
// Set armor/defense takes into account total armor (bonuses included), so remove buffs for calculation
call AdjustArmorToWhite(boss, total_armor)
endfunction
// Takes a Boss unit (hero), calls SetAttributes & SetArmor
function TuneBoss takes unit boss, string bosstype returns nothing
call SetAttributes(boss, bosstype)
call SetArmor(boss, bosstype)
endfunction
endlibrary
function GetAttackAngle takes unit attackerUnit, unit targetUnit returns real
local location attackerLocation = GetUnitLoc(attackerUnit)
local location targetLocation = GetUnitLoc(targetUnit)
local real targetToAttackerAngle = AngleBetweenPoints(targetLocation, attackerLocation)
local real targetFacingAngle = GetUnitFacing(targetUnit)
call RemoveLocation(attackerLocation)
call RemoveLocation(targetLocation)
return (targetFacingAngle - targetToAttackerAngle)
endfunction
function IsFrontAttacking takes unit attackerUnit, unit targetUnit returns boolean
local real attackAngle = GetAttackAngle(attackerUnit, targetUnit)
if ( CosBJ(attackAngle) >= CosBJ(45.0) ) then
return true
endif
return false
endfunction
function IsRearAttacking takes unit attackerUnit, unit targetUnit returns boolean
local real attackAngle = GetAttackAngle(attackerUnit, targetUnit)
if ( CosBJ(attackAngle) <= -CosBJ(45.0) ) then
return true
endif
return false
endfunction
function IsFlankAttacking takes unit attackerUnit, unit targetUnit returns boolean
local real attackAngle = GetAttackAngle(attackerUnit, targetUnit)
// Right
if ( SinBJ(attackAngle) < -SinBJ(45.0) ) then
return true
endif
// Left
if ( SinBJ(attackAngle) > SinBJ(45.0) ) then
return true
endif
return false
endfunction
//TESH.scrollpos=205
//TESH.alwaysfold=0
function K2DItemCheckXY takes real x, real y returns boolean
call SetItemPosition(udg_K2DItem, x, y)
return GetWidgetX(udg_K2DItem) == x and GetWidgetY(udg_K2DItem) == y
endfunction
function K2DItemCheckAxis takes real x, real y returns boolean
local real x2 = x*udg_K2DRadius[udg_UDex]
local real y2 = y*udg_K2DRadius[udg_UDex]
set x = udg_K2DX + x2
set y = udg_K2DY + y2
if K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
set x = udg_K2DX - x2
set y = udg_K2DY - y2
return K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endif
return false
endfunction
function K2DItemCheck takes nothing returns boolean
local boolean result = K2DItemCheckXY(udg_K2DX, udg_K2DY)
//Only perform additional pathing checks if the unit has a larger collision.
if result and udg_Knockback2DRobustPathing > 0 and udg_K2DRadius[udg_UDex] > 0 then
//Check horizontal axis of unit to make sure nothing is going to collide
set result = K2DItemCheckAxis(udg_K2DCosH[udg_UDex], udg_K2DSinH[udg_UDex])
//Check vertical axis of unit to ensure nothing will collide
set result = result and K2DItemCheckAxis(udg_K2DCos[udg_UDex], udg_K2DSin[udg_UDex])
if result and udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
//Check diagonal axis of unit if more thorough pathing is desired
set result = K2DItemCheckAxis(udg_K2DCosD1[udg_UDex], udg_K2DSinD1[udg_UDex])
set result = result and K2DItemCheckAxis(udg_K2DCosD2[udg_UDex], udg_K2DSinD2[udg_UDex])
endif
endif
//Reset item so it won't interfere with the map
call SetItemPosition(udg_K2DItem, udg_K2DMaxX, udg_K2DMaxY)
call SetItemVisible(udg_K2DItem, false)
return result
endfunction
function K2DItemFilter takes nothing returns boolean
//Check for visible items, temporarily hide them and add them to the filter.
if IsItemVisible(GetFilterItem()) then
call SetItemVisible(GetFilterItem(), false)
return true
endif
return false
endfunction
function K2DItemCode takes nothing returns nothing
//Perform the item-pathing check only once, then unhide those filtered items
if not udg_K2DItemsFound then
set udg_K2DItemsFound = true
set udg_K2DItemOffset = K2DItemCheck()
endif
call SetItemVisible(GetEnumItem(), true)
endfunction
function K2DKillDest takes nothing returns nothing
local real x
local real y
//Handle destruction of debris
set bj_destRandomCurrentPick = GetEnumDestructable()
if GetWidgetLife(bj_destRandomCurrentPick) > 0.405 and IssueTargetOrder(udg_K2DDebrisKiller, udg_Knockback2DTreeOrDebris, bj_destRandomCurrentPick) then
set x = GetWidgetX(bj_destRandomCurrentPick) - udg_K2DX
set y = GetWidgetY(bj_destRandomCurrentPick) - udg_K2DY
if x*x + y*y <= udg_K2DDestRadius[udg_UDex] then
call KillDestructable(bj_destRandomCurrentPick)
endif
endif
endfunction
function K2DEnumDests takes nothing returns nothing
call MoveRectTo(udg_K2DRegion, udg_K2DX, udg_K2DY)
if udg_K2DKillTrees[udg_UDex] then
call SetUnitX(udg_K2DDebrisKiller, udg_K2DX)
call SetUnitY(udg_K2DDebrisKiller, udg_K2DY)
call EnumDestructablesInRect(udg_K2DRegion, null, function K2DKillDest)
endif
endfunction
function Knockback2DCheckXY takes real x, real y returns boolean
set udg_K2DX = x + udg_K2DVelocity[udg_UDex]*udg_K2DCos[udg_UDex]
set udg_K2DY = y + udg_K2DVelocity[udg_UDex]*udg_K2DSin[udg_UDex]
if udg_K2DSimple[udg_UDex] then
//A "pull" effect or a missile system does not require complex pathing.
if udg_K2DX <= udg_K2DMaxX and udg_K2DX >= udg_K2DMinX and udg_K2DY <= udg_K2DMaxY and udg_K2DY >= udg_K2DMinY then
call K2DEnumDests()
return true
endif
return false
elseif udg_K2DFlying[udg_UDex] then
return not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLYABILITY)
elseif not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_WALKABILITY) then
call K2DEnumDests()
set udg_K2DItemOffset = false
call EnumItemsInRect(udg_K2DRegion, Filter(function K2DItemFilter), function K2DItemCode)
if udg_K2DItemsFound then
//If items were found, the check was already performed.
set udg_K2DItemsFound = false
else
//Otherwise, perform the check right now.
set udg_K2DItemOffset = K2DItemCheck()
endif
return udg_K2DItemOffset
endif
return udg_K2DAmphibious[udg_UDex] and not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLOATABILITY)
endfunction
function Knockback2DApplyAngle takes real angle returns nothing
set angle = ModuloReal(angle, udg_K2DRadians_Turn)
set udg_K2DCos[udg_UDex] = Cos(angle)
set udg_K2DSin[udg_UDex] = Sin(angle)
set udg_K2DAngle[udg_UDex] = angle
if udg_Knockback2DRobustPathing > 0 then
set angle = ModuloReal(angle + udg_K2DRadians_QuarterTurn, udg_K2DRadians_Turn)
set udg_K2DCosH[udg_UDex] = Cos(angle)
set udg_K2DSinH[udg_UDex] = Sin(angle)
if udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
set angle = ModuloReal(angle + udg_K2DRadians_QuarterPi, udg_K2DRadians_Turn)
set udg_K2DCosD1[udg_UDex] = Cos(angle)
set udg_K2DSinD1[udg_UDex] = Sin(angle)
set angle = ModuloReal(angle + udg_K2DRadians_QuarterTurn, udg_K2DRadians_Turn)
set udg_K2DCosD2[udg_UDex] = Cos(angle)
set udg_K2DSinD2[udg_UDex] = Sin(angle)
endif
endif
endfunction
function Knockback2DLooper takes nothing returns nothing
local integer i = 0
local unit u
local real x
local real y
call PauseUnit(udg_K2DDebrisKiller, false)
loop
set i = udg_K2DNext[i]
exitwhen i == 0
set udg_UDex = i
set udg_K2DTimeLeft[i] = udg_K2DTimeLeft[i] - udg_K2DTimeout
set udg_K2DDistanceLeft[i] = udg_K2DDistanceLeft[i] - udg_K2DVelocity[i]
set u = udg_UDexUnits[i]
if udg_K2DTimeLeft[i] > 0.00 then
if udg_K2DTimeLeft[i] < udg_K2DHeightThreshold[i] and udg_K2DHeightThreshold[i] != 0.00 then
call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u), GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)/udg_K2DHeightThreshold[i])
set udg_K2DHeightThreshold[i] = 0.00
endif
if udg_K2DPause[i] then
set x = udg_K2DLastX[i]
set y = udg_K2DLastY[i]
else
set x = GetUnitX(u)
set y = GetUnitY(u)
endif
if not Knockback2DCheckXY(x, y) then
if not udg_K2DFreeze[i] and IsTriggerEnabled(udg_K2DImpact[i]) and TriggerEvaluate(udg_K2DImpact[i]) then
call TriggerExecute(udg_K2DImpact[i])
endif
if udg_K2DBounce[i] then
call Knockback2DApplyAngle(udg_K2DRadians_Turn - udg_K2DAngle[i])
if not Knockback2DCheckXY(x, y) then
call Knockback2DApplyAngle(udg_K2DAngle[i] + bj_PI)
if not Knockback2DCheckXY(x, y) then
call Knockback2DApplyAngle(udg_K2DRadians_Turn - udg_K2DAngle[i])
set udg_K2DX = x
set udg_K2DY = y
endif
endif
else
set udg_K2DX = x
set udg_K2DY = y
set udg_K2DFreeze[i] = true
endif
endif
call SetUnitX(u, udg_K2DX)
call SetUnitY(u, udg_K2DY)
set udg_K2DLastX[i] = udg_K2DX
set udg_K2DLastY[i] = udg_K2DY
if udg_K2DFXModel[i] != "" then
set udg_K2DFXTimeLeft[i] = udg_K2DFXTimeLeft[i] - udg_K2DTimeout
if udg_K2DFXTimeLeft[i] <= 0.00 then
set udg_K2DFXTimeLeft[i] = udg_K2DFXRate[i]
if udg_K2DFlying[i] then
call DestroyEffect(AddSpecialEffectTarget(udg_K2DFXModel[i], u, "origin"))
else
call DestroyEffect(AddSpecialEffect(udg_K2DFXModel[i], udg_K2DX, udg_K2DY))
endif
endif
endif
if udg_K2DCollision[i] >= 0.00 then
set udg_Knockback2DSource = u
call GroupEnumUnitsInRange(bj_lastCreatedGroup, udg_K2DX, udg_K2DY, 200.00, null)
call GroupRemoveUnit(bj_lastCreatedGroup, u)
loop
set udg_Knockback2DUnit = FirstOfGroup(bj_lastCreatedGroup)
exitwhen udg_Knockback2DUnit == null
call GroupRemoveUnit(bj_lastCreatedGroup, udg_Knockback2DUnit)
if IsUnitInRange(udg_Knockback2DUnit, u, udg_K2DCollision[i]) and udg_K2DFlying[i] == IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_FLYING) and (not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_STRUCTURE)) and not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_DEAD) and (udg_K2DUnbiasedCollision[i] or IsUnitAlly(udg_Knockback2DUnit, GetOwningPlayer(u))) and TriggerEvaluate(gg_trg_Knockback_2D) then
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(GetUnitY(udg_Knockback2DUnit) - udg_K2DY, GetUnitX(udg_Knockback2DUnit) - udg_K2DX)
set udg_Knockback2DDistance = udg_K2DDistanceLeft[i]
set udg_Knockback2DBounces = udg_K2DBounce[i]
set udg_Knockback2DCollision = udg_K2DCollision[i]
if udg_K2DHeight[i] != 0.00 then
set udg_Knockback2DHeight = GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)
endif
set udg_Knockback2DLoopFX = udg_K2DFXModel[i]
set udg_Knockback2DTime = udg_K2DTimeLeft[i]
set udg_Knockback2DUnbiasedCollision = udg_K2DUnbiasedCollision[i]
call TriggerExecute(gg_trg_Knockback_2D)
set udg_Knockback2DSource = u //in case of a recursive knockback
endif
endloop
endif
set udg_K2DVelocity[i] = udg_K2DVelocity[i] - udg_K2DFriction[i]
else
call TriggerExecute(gg_trg_Knockback_2D_Destroy)
endif
endloop
set u = null
//Disable dummy after the loop finishes so it doesn't interfere with the map
call PauseUnit(udg_K2DDebrisKiller, true)
endfunction
//===========================================================================
function StartKnockback2DTimer takes nothing returns nothing
call TimerStart(udg_K2DTimer, udg_K2DTimeout, true, function Knockback2DLooper)
endfunction
function InitTrig_Knockback_2D_System takes nothing returns nothing
endfunction
//TESH.scrollpos=8
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 3.1.0.1
One map, one hashtable. Welcome to NewTable 3.1
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
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")
//! 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 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)
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb)
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, 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
endlibrary
//TESH.scrollpos=9
//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=75
//TESH.alwaysfold=0
/*
Lightning System v1.03
by Adiktuz
Basically it allows you to easily create timed or un-timed lightning effects
Features:
->You can create lightnings between two points, two unit, or a point and a unit.
->For lightnings attached to a unit, the system automatically updates the lightning
for changes on the unit (like unit position, height etc)
->Specify the height of the lightning
->Create timed-lightnings that get automatically destroyed when their duration is over
->Supports moving points too (but the lightnings are forced to be timed)
->Allows you to add actions that will run when a lightning has ended and during
update (time of which is equal to T32's period)
->Allows you to attach any type of custom data to each instance
Methods available
How to use: call Lightning.methodName(parameters)
unitToPoint takes unit unit1, real x, real y, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
unitToUnit takes unit unit1, unit unit2, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
pointToPoint takes real sourceX, real sourceY,real targetX, real targetY, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
pointToPointEx takes real sourceX, real deltaSourceX, real sourceY, real deltaSourceY, real targetX, real deltaTargetX, real targetY, real deltaTargetY, real sourceZ, real targetZ, real duration, string leffect, integer eventkey returns thistype
unitToPointEx takes unit unit1, real x, real xx, real y, real yx, real sourceZ, real targetZ, real duration, string leffect, integer eventkey returns thistype
pointToPointExZ takes real sourceX, real deltaSourceX, real sourceY, real deltaSourceY, real targetX, real deltaTargetX, real targetY, real deltaTargetY, real sourceZ, real sourceCurZ, real targetZ, real targetCurZ, real duration, string leffect, integer eventkey returns thistype
unitToPointExZ takes unit unit1, real x, real xx, real y, real yx, real sourceZ, real targetZ, real targetCurZ, real duration, string leffect, integer eventkey returns thistype
Parameters:
unit unit1 -> unit end of the lightning
unit unit2 -> other unit end of the lightning for the UTU methods
real x,y -> X,Y coordinates of the point end of the lightning for the UTP methods
real sourceX,sourceY -> X,Y coordinates of the first point of the lightning for the PTP methods
real sourceZ,targetZ -> height of the lightning at each ends (actual height is calculated as z + height of unit (if there's a unit end) + locationZ)
real deltaSourceX,deltaSourceY,deltaTargetX,deltaTargetY,sourceCurZ,targetCurZ -> for the Ex and ExZ methods, the final value of each coordinate/height
(the lightning moves from sourceX to deltaSourceX, etc through the given time)
boolean timed -> whether the lightning has a timed life or not, defaulted to true for the Ex and ExZ methods
real duration -> timed life of the lightning
string leffect -> string name of the lightning effect
integer eventkey -> a key used to determine the correct update and end functions to run if available
(if you're not using the update and end events, you're probably saf to just set it to 0)
Note: all of the above methods return the Lightning instance so you can save it in a variable
Extra/Interface/Events:
registerUpdateEvent(integer eventkey, code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning with the
same eventkey as the one registered is updated (every T32_PERIOD)
registerEndEvent(integer eventkey, code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning with the
same eventkey as the one registered ends
NOTE: If you're going to use the next three functions/methods, make sure you set the
corresponding boolean at the globals block to true
registerGlobalUpdateEvent(code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning
is updated (every T32_PERIOD)
registerGlobalEndEvent(code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning ends
registerGlobalCreateEvent(code ToDo) returns nothing
-> allows you to register an action that will be run whenever a lightning is created
To allow creation and usage of custom data that is attached to every instance
turn the boolean USE_CUSTOM_DATA to true
then to add a custom data to an instance:
for integers:
set YourLightningInstance.customData[integer key] = value
for others:
set YourLightningInstance.customData.type[integer key] = value
->type can be real,unit,trigger,effect, etc...
->integer key is the key that will point to the data, you can utilize StringHash if you want to use strings
something like: customData.real[StringHash("damage")]
then to load data:
YourLightningInstance.customData.type[integer key]
To forcefully remove a lightning:
remove() returns nothing
To obtain which lightning instance triggered the events:
instance()
*/
//DO NOT EDIT ANYTHING BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING
//Note: I've set the checkvisibility field to false because the lightnings look
// weird when I set it to true (sometimes they "jump")
library LightningSystem requires T32, Table
globals
//Set this to true if you're gonna use the global update event handler
private constant boolean USE_GLOBAL_UPDATE = false
//Set this to true if you're gonna use the global end event handler
private constant boolean USE_GLOBAL_END = false
//Set this to true if you're gonna use the global create event handler
private constant boolean USE_GLOBAL_CREATE = false
//Set this to true if you're gonna use the custom data feature
private constant boolean USE_CUSTOM_DATA = true
private location loc = Location(0,0)
public Table UpdateTable
public Table EndTable
private trigger globalUpdate
private trigger globalEnd
private trigger globalCreate
endglobals
private module init
static method onInit takes nothing returns nothing
set EndTable = Table.create()
set UpdateTable = Table.create()
static if USE_GLOBAL_UPDATE then
set globalUpdate = CreateTrigger()
endif
static if USE_GLOBAL_END then
set globalEnd = CreateTrigger()
endif
static if USE_GLOBAL_CREATE then
set globalCreate = CreateTrigger()
endif
endmethod
endmodule
struct Lightning extends array
lightning light
real sourceX
real targetX
real sourceY
real targetY
real sourceZ
real targetZ
real deltaSourceX
real deltaTargetX
real deltaSourceY
real deltaTargetY
real sourceCurZ
real targetCurZ
real deltaSourceZ
real deltaTargetZ
unit u1
unit u2
integer xtype
boolean timed
boolean moving
real duration
integer eventkey
Table customData
static thistype instance
private static integer instanceCount = 0
private static thistype recycle = 0
private thistype recycleNext
static method registerEndEvent takes integer eventkey, code toDo returns nothing
if not EndTable.handle.has(eventkey) then
set EndTable.trigger[eventkey] = CreateTrigger()
endif
call TriggerAddCondition(EndTable.trigger[eventkey],Filter(toDo))
endmethod
static method registerUpdateEvent takes integer eventkey, code toDo returns nothing
if not UpdateTable.handle.has(eventkey) then
set UpdateTable.trigger[eventkey] = CreateTrigger()
endif
call TriggerAddCondition(UpdateTable.trigger[eventkey],Filter(toDo))
endmethod
static method registerGlobalEndEvent takes code toDo returns nothing
call TriggerAddCondition(globalEnd,Filter(toDo))
endmethod
static method registerGlobalUpdateEvent takes code toDo returns nothing
call TriggerAddCondition(globalUpdate,Filter(toDo))
endmethod
static method registerGlobalCreateEvent takes code toDo returns nothing
call TriggerAddCondition(globalCreate,Filter(toDo))
endmethod
method remove takes nothing returns nothing
call DestroyLightning(this.light)
set instance = this
if EndTable.handle.has(this.eventkey) then
call TriggerEvaluate(EndTable.trigger[this.eventkey])
endif
static if USE_GLOBAL_END then
call TriggerEvaluate(globalEnd)
endif
call this.stopPeriodic()
set this.deltaSourceZ = 0.0
set this.deltaTargetZ = 0.0
set .recycleNext=recycle
set recycle=this
endmethod
static method new takes nothing returns thistype
local thistype this
if (recycle == 0) then
set instanceCount = instanceCount + 1
return instanceCount
else
set this = recycle
set recycle = recycle.recycleNext
endif
return this
endmethod
private method periodic takes nothing returns nothing
if this.xtype == 1 then
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceCurZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
if this.moving then
set this.targetX = this.targetX + this.deltaTargetX
set this.targetY = this.targetY + this.deltaTargetY
endif
call MoveLocation(loc, this.targetX,this.targetY)
if this.deltaTargetZ != 0.0 then
set this.targetZ = this.targetZ + this.deltaTargetZ
endif
set this.targetCurZ = targetZ + GetLocationZ(loc) + this.deltaTargetZ
call MoveLightningEx(this.light,false,this.sourceX,this.sourceY,this.sourceCurZ,this.targetX,this.targetY,this.targetCurZ)
elseif this.xtype == 2 then
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = GetUnitX(this.u2)
set this.targetY = GetUnitY(this.u2)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceCurZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetCurZ = targetZ + GetUnitFlyHeight(this.u2) + GetLocationZ(loc)
call MoveLightningEx(this.light,false,this.sourceX,this.sourceY,this.sourceCurZ,this.targetX,this.targetY,this.targetCurZ)
else
if this.moving then
set this.sourceX = this.sourceX + this.deltaSourceX
set this.targetX = this.targetX + this.deltaTargetX
set this.sourceY = this.sourceY + this.deltaSourceY
set this.targetY = this.targetY + this.deltaTargetY
call MoveLocation(loc, this.sourceX,this.sourceY)
if this.deltaSourceZ != 0.0 then
set this.sourceZ = this.sourceZ + this.deltaSourceZ
endif
if this.deltaTargetZ != 0.0 then
set this.targetZ = this.targetZ + this.deltaTargetZ
endif
set this.sourceCurZ = sourceZ + GetLocationZ(loc) + this.deltaSourceZ
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetCurZ = targetZ + GetLocationZ(loc) + this.deltaTargetZ
call MoveLightningEx(this.light,false,this.sourceX,this.sourceY,this.sourceCurZ,this.targetX,this.targetY,this.targetCurZ)
endif
endif
set instance = this
if UpdateTable.handle.has(this.eventkey) then
call TriggerEvaluate(UpdateTable.trigger[this.eventkey])
endif
static if USE_GLOBAL_UPDATE then
call TriggerEvaluate(globalUpdate)
endif
if this.timed then
set this.duration = this.duration - T32_PERIOD
if this.duration <= 0.0 then
call this.remove()
endif
endif
endmethod
implement T32x
static method unitToPoint takes unit unit1, real x, real y, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = unit1
set this.u2 = null
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = x
set this.targetY = y
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = timed
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 1
set this.moving = false
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method unitToUnit takes unit unit1, unit unit2, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = unit1
set this.u2 = unit2
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = GetUnitX(this.u2)
set this.targetY = GetUnitY(this.u2)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetUnitFlyHeight(this.u2) + GetLocationZ(loc)
set this.timed = timed
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 2
set this.moving = false
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method pointToPoint takes real sourceX, real sourceY,real targetX, real targetY, real sourceZ, real targetZ, boolean timed, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = null
set this.u2 = null
set this.sourceX = sourceX
set this.sourceY = sourceY
set this.targetX = targetX
set this.targetY = targetY
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = timed
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 3
set this.moving = false
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method pointToPointEx takes real sourceX, real deltaSourceX, real sourceY, real deltaSourceY, real targetX, real deltaTargetX, real targetY, real deltaTargetY, real sourceZ, real targetZ, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = null
set this.u2 = null
set this.sourceX = sourceX
set this.sourceY = sourceY
set this.targetX = targetX
set this.targetY = targetY
set this.deltaSourceX = (deltaSourceX - sourceX)*(T32_PERIOD/duration)
set this.deltaSourceY = (deltaSourceY - sourceY)*(T32_PERIOD/duration)
set this.deltaTargetX = (deltaTargetX - targetX)*(T32_PERIOD/duration)
set this.deltaTargetY = (deltaTargetY - targetY)*(T32_PERIOD/duration)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = true
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 3
set this.moving = true
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method unitToPointEx takes unit unit1, real x, real xx, real y, real yx, real sourceZ, real targetZ, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = unit1
set this.u2 = null
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = x
set this.targetY = y
set this.deltaTargetX = (xx - x)*(T32_PERIOD/duration)
set this.deltaTargetY = (yx - y)*(T32_PERIOD/duration)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = true
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 1
set this.moving = true
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method pointToPointExZ takes real sourceX, real deltaSourceX, real sourceY, real deltaSourceY, real targetX, real deltaTargetX, real targetY, real deltaTargetY, real sourceZ, real sourceCurZ, real targetZ, real targetCurZ, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = null
set this.u2 = null
set this.sourceX = sourceX
set this.sourceY = sourceY
set this.targetX = targetX
set this.targetY = targetY
set this.deltaSourceZ = (sourceCurZ - sourceZ)*(T32_PERIOD/duration)
set this.deltaTargetZ = (targetCurZ-targetZ)*(T32_PERIOD/duration)
set this.deltaSourceX = (deltaSourceX - sourceX)*(T32_PERIOD/duration)
set this.deltaSourceY = (deltaSourceY - sourceY)*(T32_PERIOD/duration)
set this.deltaTargetX = (deltaTargetX - targetX)*(T32_PERIOD/duration)
set this.deltaTargetY = (deltaTargetY - targetY)*(T32_PERIOD/duration)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = true
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 3
set this.moving = true
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
static method unitToPointExZ takes unit unit1, real x, real xx, real y, real yx, real sourceZ, real targetZ, real targetCurZ, real duration, string leffect, integer eventkey returns thistype
local thistype this = thistype.new()
set this.u1 = unit1
set this.u2 = null
set this.sourceX = GetUnitX(this.u1)
set this.sourceY = GetUnitY(this.u1)
set this.targetX = x
set this.targetY = y
set this.deltaTargetZ = (targetCurZ - targetZ)*(T32_PERIOD/duration)
set this.deltaTargetX = (xx - x)*(T32_PERIOD/duration)
set this.deltaTargetY = (yx - y)*(T32_PERIOD/duration)
call MoveLocation(loc, this.sourceX,this.sourceY)
set this.sourceZ = sourceZ + GetUnitFlyHeight(this.u1) + GetLocationZ(loc)
call MoveLocation(loc, this.targetX,this.targetY)
set this.targetZ = targetZ + GetLocationZ(loc)
set this.timed = true
set this.duration = duration
set this.eventkey = eventkey
set this.light = AddLightningEx(leffect,false,this.sourceX,this.sourceY,this.sourceZ,this.targetX,this.targetY,this.targetZ)
set this.xtype = 1
set this.moving = true
static if USE_CUSTOM_DATA then
if this.customData < 0 then
set this.customData = Table.create()
endif
endif
static if USE_GLOBAL_CREATE then
call TriggerEvaluate(globalCreate)
endif
call this.startPeriodic()
return this
endmethod
implement init
endstruct
endlibrary
//TESH.scrollpos=114
//TESH.alwaysfold=0
/*
Lightning Utilities v1.01
by Adiktuz
A set of methods that can help you make more out of the
lightning system.
I chose to separate it from the main system because these are
extra methods only to extend the capabilities of the LS.
This way, the user has the option of not using LU library
as the LS will work fine without it.
How to use:
LightningUtils.method(parameters)
Methods:
enumUnits(group g, Lightning light, real radius)
Parameters:
group g -> group that you want to fill with the units between the Lightning's ends
Lightning light -> the Lightning object
real radius -> radius around the Lightning in which unit's will be picked
Note: the following Gradient methods are only for timed lightnings
registerGradientKey(integer eventkey)
->eventkey of the Lightning's that you want to have a color/alpha gradient
Notes:
->it is important that you register first the eventkey using this method
before using the next method which sets the gradient of each Lightning
instance.
->Do this registration only once per eventkey
addLightningGradient(Lightning light, real red1, real blue1, real green1, real alpha1,real red2, real blue2, real green2, real alpha2)
Parameters:
Lightning light -> the Lightning instance that you want to have a gradient
real red1,blue1,green1,alpha1 -> the initial values of the colors/alpha of the Lightning
real red2,blue2,green2,alpha2 -> the final values of the colors/alpha of the Lightning
->these reals are from 0.0 up to 1.0
Notes:
->Make sure that the eventkey used by this Lightning is already registered using the
method above (registerGradientKey)
->This method automatically fetches the duration of the Lightning
->Do not modify the Lightning's duration afterwards else it might look bad
See the Example trigger for an example of how to use these methods.
*/
library LightningUtilities requires LightningSystem
private module init
static method onInit takes nothing returns nothing
set thistype.redTable = Table.create()
set thistype.blueTable = Table.create()
set thistype.greenTable = Table.create()
set thistype.alphaTable = Table.create()
set thistype.redCTable = Table.create()
set thistype.blueCTable = Table.create()
set thistype.greenCTable = Table.create()
set thistype.alphaCTable = Table.create()
endmethod
endmodule
struct LightningUtils extends array
private static group tmpGroup = CreateGroup()
private static unit tmpUnit = null
private static Table redTable
private static Table blueTable
private static Table greenTable
private static Table alphaTable
private static Table redCTable
private static Table blueCTable
private static Table greenCTable
private static Table alphaCTable
static method enumUnits takes group g, Lightning light, real radius returns nothing
local real tdy = (light.targetY-light.sourceY)
local real tdx = (light.targetX-light.sourceX)
local real angle = Atan2(tdy,tdx)
local real distance = (tdy*tdy)+(tdx*tdx)
local integer iend = R2I(distance/(radius*radius))
local real dx = tdx/iend
local real dy = tdy/iend
//we only get units in between the lightning
local integer i = 1
set iend = iend - 1
call GroupClear(g)
loop
call GroupEnumUnitsInRange(tmpGroup,light.sourceX+i*dx,light.sourceY+i*dy,radius,null)
loop
set tmpUnit = FirstOfGroup(tmpGroup)
exitwhen tmpUnit == null
if not IsUnitInGroup(tmpUnit,g) then
call GroupAddUnit(g,tmpUnit)
endif
call GroupRemoveUnit(tmpGroup,tmpUnit)
endloop
set i = i + 1
exitwhen i > iend
endloop
endmethod
//I used Table to save the RGBA values because if I use the GeLightningColor natives, it returns
//rounded down values, resulting to bad behavior at durations > 4.0
static method updateGradient takes nothing returns nothing
set redCTable.real[Lightning.instance] = redCTable.real[Lightning.instance] + redTable.real[Lightning.instance]
set blueCTable.real[Lightning.instance] = blueCTable.real[Lightning.instance] + blueTable.real[Lightning.instance]
set greenCTable.real[Lightning.instance] = greenCTable.real[Lightning.instance] + greenTable.real[Lightning.instance]
set alphaCTable.real[Lightning.instance] = alphaCTable.real[Lightning.instance] + alphaTable.real[Lightning.instance]
call SetLightningColor(Lightning.instance.light, redCTable.real[Lightning.instance],greenCTable.real[Lightning.instance],blueCTable.real[Lightning.instance],alphaCTable.real[Lightning.instance])
endmethod
static method addLightningGradient takes Lightning light, real red1, real blue1, real green1, real alpha1,real red2, real blue2, real green2, real alpha2 returns nothing
set redCTable.real[light] = red1
set blueCTable.real[light] = blue1
set greenCTable.real[light] = green1
set alphaCTable.real[light] = alpha1
set redTable.real[light] = (((red2-red1)/light.duration)*T32_PERIOD)
set blueTable.real[light] = (((blue2-blue1)/light.duration)*T32_PERIOD)
set greenTable.real[light] = (((green2-green1)/light.duration)*T32_PERIOD)
set alphaTable.real[light] = (((alpha2-alpha1)/light.duration)*T32_PERIOD)
endmethod
static method registerGradientKey takes integer eventkey returns nothing
call Lightning.registerUpdateEvent(eventkey,function thistype.updateGradient)
endmethod
implement init
endstruct
endlibrary
library NeatMessages initializer Init
/*
===========================================================================================
Neat Text Messages
by Antares
Recreation of the default text messages with more customizability.
How to import:
Copy this library into your map. To get better looking messages with text shadow and an optional
tooltip-like box around the text, copy the "NeatTextMessage.fdf" and "NeatMessageTemplates.toc"
files from the test map into your map without a subpath.
Edit the parameters in the config section to your liking.
Replace all DisplayTextToForce calls etc. with the appropriate function from this library.
GUI users: You can use the REPLACE_BLIZZARD_FUNCTION_CALLS feature. This will replace all calls
automatically. You don't need to do anything else. If you want to setup multiple message formats,
copy the GUI globals into your map and look at how to setup custom formats with the examples provided
in this map. This is not necessary if you want to stick exclusively to the message format you define
in the config here.
WARNING: Calling neat message functions (but not clear functions) from within local player code
with REPLACE_BLIZZARD_FUNCTION_CALLS will cause a desync!
Default text formatting can be overwritten by setting up NeatFormats. Examples are given in the
test section. All formatting parameters that aren't set for a NeatFormat will use the default
values instead.
NeatMessage creator functions return an integer. This integer is a pointer to the created message
that can be used to edit, extend, or remove the message. The returned integer is asynchronous
and will be 0 for all players for whom the message isn't displayed.
You can set up additional text windows with NeatWindow.create. If you create a neat message
without specifying the window in which it should be created, it will always be created in the
default window specified in the config. Additional windows are not available with the GUI
features.
===========================================================================================
API
===========================================================================================
NeatMessage takes string whichMessage returns nothing
NeatMessageToPlayer takes player whichPlayer, string whichMessage returns nothing
NeatMessageToForce takes force whichForce, string whichMessage returns nothing
NeatMessageTimed takes real duration, string whichMessage returns nothing
NeatMessageToPlayerTimed takes player whichPlayer, real duration, string whichMessage returns nothing
NeatMessageToForceTimed takes force whichForce, real duration, string whichMessage returns nothing
NeatMessageFormatted takes string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageToPlayerFormatted takes player whichPlayer, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageToForceFormatted takes force whichForce, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageTimedFormatted takes real duration, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageToPlayerTimedFormatted takes player whichPlayer, real duration, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageToForceTimedFormatted takes force whichForce, real duration, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageInWindow takes string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageToPlayerInWindow takes player whichPlayer, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageToForceInWindow takes force whichForce, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageTimedInWindow takes real duration, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageToPlayerTimedInWindow takes player whichPlayer, real duration, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageToForceTimedInWindow takes force whichForce, real duration, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageFormattedInWindow takes string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageToPlayerFormattedInWindow takes player whichPlayer, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageToForceFormattedInWindow takes force whichForce, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageTimedFormattedInWindow takes real duration, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageToPlayerTimedFormattedInWindow takes player whichPlayer, real duration, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageToForceTimedFormattedInWindow takes force whichForce, real duration, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
===========================================================================================
EditNeatMessage takes integer messagePointer, string newText returns nothing
AddNeatMessageTimeRemaining takes integer messagePointer, real additionalTime returns nothing
SetNeatMessageTimeRemaining takes integer messagePointer, real newTime returns nothing
RemoveNeatMessage takes integer messagePointer returns nothing
AutoSetNeatMessageTimeRemaining takes integer messagePointer, boolean accountForTimeElapsed returns nothing
IsMessageDisplayed takes integer messagePointer returns boolean
NeatMessageAddIcon takes integer messagePointer, real width, real height, string orientation, string texture
(valid arguments for orientation are "topleft", "topright", "bottomleft", "bottomright")
NeatMessageHideIcon takes integer messagePointer returns nothing
ClearNeatMessages takes nothing returns nothing
ClearNeatMessagesForPlayer takes player whichPlayer returns nothing
ClearNeatMessagesForForce takes force whichForce returns nothing
ClearNeatMessagesInWindow takes NeatWindow whichWindow returns nothing
ClearNeatMessagesForPlayerInWindow takes player whichPlayer, NeatWindow whichWindow returns nothing
ClearNeatMessagesForForceInWindow takes force whichForce, NeatWindow whichWindow returns nothing
set myFormat = NeatFormat.create takes nothing returns NeatFormat
set myFormat.spacing =
set myFormat.fadeOutTime =
set myFormat.fadeInTime =
set myFormat.fontSize =
set myFormat.minDuration =
set myFormat.durationIncrease =
set myFormat.verticalAlignment =
set myFormat.horizontalAlignment =
set myFormat.isBoxed =
call myFormat.copy(copiedFormat)
set myWindow = NeatWindow.create takes real xPosition, real yPosition, real width, real height, integer maxMessages, boolean topToBottom returns NeatWindow
===========================================================================================
GUI API
===========================================================================================
CreateNeatFormat takes string formatName returns nothing
SetNeatFormat takes string formatName returns nothing
ResetNeatVars takes nothing returns nothing
ResetNeatFormat takes nothing returns nothing
===========================================================================================
*/
globals
//=========================================================================================
//Config
//=========================================================================================
//Default text formatting. Can be overwritten by setting up neatFormats.
private constant real MESSAGE_MINIMUM_DURATION = 2.5 //Display duration of a message with zero characters.
private constant real MESSAGE_DURATION_INCREASE_PER_CHARACTER = 0.12
private constant real TEXT_MESSAGE_FONT_SIZE = 14
private constant real SPACING_BETWEEN_MESSAGES = 0.0
private constant real FADE_IN_TIME = 0.0
private constant real FADE_OUT_TIME = 1.8
private constant textaligntype VERTICAL_ALIGNMENT = TEXT_JUSTIFY_MIDDLE //TEXT_JUSTIFY_BOTTOM, TEXT_JUSTIFY_MIDDLE, or TEXT_JUSTIFY_TOP
private constant textaligntype HORIZONTAL_ALIGNMENT = TEXT_JUSTIFY_CENTER //TEXT_JUSTIFY_LEFT, TEXT_JUSTIFY_CENTER, or TEXT_JUSTIFY_RIGHT
private constant boolean BOXED_MESSAGES = false //Create tooltip box around text messages? Requires .fdf file and INCLUDE_FDF enabled.
//Default text window parameters.
constant real TEXT_MESSAGE_X_POSITION = 0.2 //0 = left, 1 = right (bottom-left corner)
constant real TEXT_MESSAGE_Y_POSITION = 0.03 //0 = bottom, 0.6 = top (bottom-left corner)
constant real TEXT_MESSAGE_BLOCK_MAX_HEIGHT = 0.2 //Maximum height of the entire text message block. Messages pushed out of that area will be removed.
constant real TEXT_MESSAGE_BLOCK_WIDTH = 0.4
constant integer MAX_TEXT_MESSAGES = 5 //Maximum number of messages on the screen at the same time. If you want a non-scrolling window, simply set this number to 1.
constant boolean MESSAGE_ORDER_TOP_TO_BOTTOM = false //Set true if new messages should appear above old messages.
//Config
private constant boolean INCLUDE_FDF = true //NeatMessage.fdf has been imported?
private constant boolean COPY_TO_MESSAGE_LOG = true //(Only singleplayer) Copies messages to message log by printing out the message with DisplayTextToPlayer, then clearing all text. Will interfere with other default text messages.
private constant boolean REPLACE_BLIZZARD_FUNCTION_CALLS = false //Replaces Display(Timed)TextToForce, ClearTextMessages, and ClearTextMessagesBJ.
private constant integer TOOLTIP_ABILITY = 'Amls' //For REPLACE_BLIZZARD_FUNCTION_CALLS only. Any unused ability for which the library can change the tooltip to extract the TRIGSTR from.
//=========================================================================================
private boolean isSinglePlayer
private timer masterTimer = CreateTimer()
private timer clearTextTimer = CreateTimer()
private integer numMessagesOnScreen = 0
private boolean doNotClear = false
private integer messageCounter = 0
private integer array frameOfMessage
private NeatWindow array windowOfMessage
private constant real TIME_STEP = 0.05
NeatFormat DEFAULT_NEAT_FORMAT
NeatWindow DEFAULT_NEAT_WINDOW
private NeatWindow array neatWindow
private integer numNeatWindows = 0
endglobals
static if REPLACE_BLIZZARD_FUNCTION_CALLS then
hook DisplayTextToForce DisplayTextToForceHook
hook DisplayTimedTextToForce DisplayTimedTextToForceHook
hook ClearTextMessagesBJ ClearNeatMessagesForForce
hook ClearTextMessages ClearNeatMessages
endif
struct NeatFormat
real spacing = SPACING_BETWEEN_MESSAGES
real fadeOutTime = FADE_OUT_TIME
real fadeInTime = FADE_IN_TIME
real fontSize = TEXT_MESSAGE_FONT_SIZE
real minDuration = MESSAGE_MINIMUM_DURATION
real durationIncrease = MESSAGE_DURATION_INCREASE_PER_CHARACTER
textaligntype verticalAlignment = VERTICAL_ALIGNMENT
textaligntype horizontalAlignment = HORIZONTAL_ALIGNMENT
boolean isBoxed = BOXED_MESSAGES
static method create takes nothing returns NeatFormat
return NeatFormat.allocate()
endmethod
method copy takes NeatFormat copiedFormat returns nothing
set .spacing = copiedFormat.spacing
set .fadeOutTime = copiedFormat.fadeOutTime
set .fadeInTime = copiedFormat.fadeInTime
set .fontSize = copiedFormat.fontSize
set .minDuration = copiedFormat.minDuration
set .durationIncrease = copiedFormat.durationIncrease
set .verticalAlignment = copiedFormat.verticalAlignment
set .horizontalAlignment = copiedFormat.horizontalAlignment
set .isBoxed = copiedFormat.isBoxed
endmethod
endstruct
struct NeatWindow
real xPosition
real yPosition
real maxHeight
real width
integer maxTextMessages
boolean isTopToBottom
framehandle array textMessageFrame[MAX_TEXT_MESSAGES]
framehandle array textMessageText[MAX_TEXT_MESSAGES]
framehandle array textMessageBox[MAX_TEXT_MESSAGES]
framehandle array textCarryingFrame[MAX_TEXT_MESSAGES]
framehandle array textMessageIcon[MAX_TEXT_MESSAGES]
NeatFormat array messageFormat[MAX_TEXT_MESSAGES]
real array messageTimeRemaining[MAX_TEXT_MESSAGES]
real array messageTimeElapsed[MAX_TEXT_MESSAGES]
real array textHeight[MAX_TEXT_MESSAGES]
string array textMessageIconOrientation[MAX_TEXT_MESSAGES]
integer array messageOfFrame[MAX_TEXT_MESSAGES]
integer numMessagesOnScreen = 0
static method create takes real xPosition, real yPosition, real width, real maxHeight, integer maxTextMessages, boolean topToBottom returns NeatWindow
local NeatWindow this = NeatWindow.allocate()
local integer i = 0
set .xPosition = xPosition
set .yPosition = yPosition
set .maxHeight = maxHeight
set .width = width
set .maxTextMessages = maxTextMessages
set .isTopToBottom = topToBottom
if isTopToBottom then
set .yPosition = .yPosition + maxHeight
endif
set numNeatWindows = numNeatWindows + 1
set neatWindow[numNeatWindows] = this
loop
exitwhen i > maxTextMessages - 1
static if INCLUDE_FDF then
set .textMessageFrame[i] = BlzCreateFrame("TextMessage", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), 0, 0)
set .textMessageText[i] = BlzFrameGetChild( .textMessageFrame[i],0)
set .textMessageBox[i] = BlzFrameGetChild( .textMessageFrame[i],1)
call BlzFrameSetSize(.textMessageText[i], width, 0)
call BlzFrameSetScale(.textMessageText[i], TEXT_MESSAGE_FONT_SIZE/10.)
call BlzFrameSetAbsPoint(.textMessageFrame[i], FRAMEPOINT_BOTTOMLEFT, xPosition, yPosition)
call BlzFrameSetTextAlignment(.textMessageText[i] , VERTICAL_ALIGNMENT , HORIZONTAL_ALIGNMENT)
call BlzFrameSetVisible( .textMessageFrame[i] , false )
call BlzFrameSetEnable(.textMessageFrame[i],false)
call BlzFrameSetEnable(.textMessageText[i],false)
call BlzFrameSetText(.textMessageText[i],"")
call BlzFrameSetLevel(.textMessageText[i],1)
call BlzFrameSetLevel(.textMessageBox[i],0)
set .textCarryingFrame[i] = .textMessageText[i]
set .textMessageIcon[i] = BlzCreateFrameByType("BACKDROP", "textMessageIcon" + I2S(i) , BlzGetFrameByName("ConsoleUIBackdrop",0), "", 0)
call BlzFrameSetEnable(.textMessageIcon[i],false)
call BlzFrameSetVisible(.textMessageIcon[i],false)
set .messageFormat[i] = DEFAULT_NEAT_FORMAT
else
set .textMessageFrame[i] = BlzCreateFrame("TextMessage", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), 0, 0)
call BlzFrameSetScale(.textMessageFrame[i], TEXT_MESSAGE_FONT_SIZE/10.)
call BlzFrameSetTextAlignment(.textMessageFrame[i] , VERTICAL_ALIGNMENT , HORIZONTAL_ALIGNMENT)
call BlzFrameSetAbsPoint(.textMessageFrame[i], FRAMEPOINT_BOTTOMLEFT, xPosition, yPosition)
call BlzFrameSetText(.textMessageFrame[i],"")
call BlzFrameSetVisible( .textMessageFrame[i] , false )
call BlzFrameSetEnable(.textMessageFrame[i],false)
set .textCarryingFrame[i] = .textMessageFrame[i]
set .textMessageIcon[i] = BlzCreateFrameByType("BACKDROP", "textMessageIcon" + I2S(i) , BlzGetFrameByName("ConsoleUIBackdrop",0), "", 0)
call BlzFrameSetEnable(.textMessageIcon[i],false)
call BlzFrameSetVisible(.textMessageIcon[i],false)
set .messageFormat[i] = DEFAULT_NEAT_FORMAT
endif
set i = i + 1
endloop
return this
endmethod
endstruct
//=========================================================================================
private function ClearText takes nothing returns nothing
set doNotClear = true
call ClearTextMessages()
set doNotClear = false
endfunction
private function GetAdjustedStringLength takes string whichString returns integer
local integer rawLength = StringLength(whichString)
local integer adjustedLength = rawLength
local integer j = 0
local string secondCharacter
loop
exitwhen j > rawLength - 10
if SubString(whichString, j, j+1) == "|" then
set secondCharacter = StringCase(SubString(whichString, j+1, j+2), false)
if secondCharacter == "c" then
set adjustedLength = adjustedLength - 10
set j = j + 10
elseif secondCharacter == "r" then
set adjustedLength = adjustedLength - 2
set j = j + 2
endif
else
set j = j + 1
endif
endloop
return adjustedLength
endfunction
private function ChangeTextFormatting takes NeatWindow w, integer whichFrame, NeatFormat whichFormat returns nothing
if whichFormat == 0 then
return
endif
set w.messageFormat[whichFrame] = whichFormat
call BlzFrameSetScale(w.textCarryingFrame[whichFrame], whichFormat.fontSize/10.)
call BlzFrameSetTextAlignment( w.textCarryingFrame[whichFrame], whichFormat.verticalAlignment, whichFormat.horizontalAlignment )
endfunction
private function ChangeText takes NeatWindow w, integer whichFrame, string whichText returns nothing
local real height
call BlzFrameSetText( w.textCarryingFrame[whichFrame] , whichText )
if w.maxTextMessages == 1 then
call BlzFrameSetSize( w.textCarryingFrame[whichFrame] , w.width / (w.messageFormat[whichFrame].fontSize/10.) , w.maxHeight / (w.messageFormat[whichFrame].fontSize/10.) )
else
call BlzFrameSetSize( w.textCarryingFrame[whichFrame] , w.width / (w.messageFormat[whichFrame].fontSize/10.) , 0 )
endif
static if INCLUDE_FDF then
call BlzFrameSetSize( w.textMessageFrame[whichFrame] , BlzFrameGetWidth(w.textMessageText[whichFrame]) + 0.008 , BlzFrameGetHeight(w.textMessageText[whichFrame]) + 0.009 )
call BlzFrameSetPoint( w.textMessageBox[whichFrame] , FRAMEPOINT_BOTTOMLEFT , w.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMLEFT , 0 , -0.0007*(w.messageFormat[whichFrame].fontSize-13) )
call BlzFrameSetPoint( w.textMessageBox[whichFrame] , FRAMEPOINT_TOPRIGHT , w.textMessageFrame[whichFrame] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
endif
if w.textMessageIconOrientation[whichFrame] != null then
set height = BlzFrameGetHeight(w.textMessageIcon[whichFrame])
if BlzFrameGetHeight( w.textMessageFrame[whichFrame] ) < height then
call BlzFrameSetSize( w.textCarryingFrame[whichFrame] , w.width / (w.messageFormat[whichFrame].fontSize/10.) , height - 0.018 )
static if INCLUDE_FDF then
call BlzFrameSetSize( w.textMessageFrame[whichFrame] , BlzFrameGetWidth(w.textMessageText[whichFrame]) + 0.008 , BlzFrameGetHeight(w.textMessageText[whichFrame]) + 0.009 )
call BlzFrameSetPoint( w.textMessageBox[whichFrame] , FRAMEPOINT_BOTTOMLEFT , w.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMLEFT , 0 , -0.0007*(w.messageFormat[whichFrame].fontSize-13) )
call BlzFrameSetPoint( w.textMessageBox[whichFrame] , FRAMEPOINT_TOPRIGHT , w.textMessageFrame[whichFrame] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
endif
set w.textHeight[whichFrame] = BlzFrameGetHeight(w.textMessageFrame[whichFrame]) + RMaxBJ(w.messageFormat[whichFrame].spacing , w.messageFormat[whichFrame+1].spacing)
endif
endif
if whichText == "" then
call BlzFrameSetVisible( w.textMessageFrame[whichFrame] , false )
call BlzFrameSetAlpha( w.textMessageFrame[whichFrame] , 255 )
else
call BlzFrameSetVisible( w.textMessageFrame[whichFrame] , true )
endif
endfunction
private function HideTextMessage takes NeatWindow w, integer whichFrame, boolean collapseFrame returns nothing
if BlzFrameGetText(w.textCarryingFrame[whichFrame]) != "" then
set numMessagesOnScreen = numMessagesOnScreen - 1
endif
call ChangeText(w,whichFrame,"")
set w.messageTimeRemaining[whichFrame] = 0
set frameOfMessage[w.messageOfFrame[whichFrame]] = -1
set w.messageOfFrame[whichFrame] = 0
if collapseFrame then
set w.textHeight[whichFrame] = 0
endif
if w.textMessageIconOrientation[whichFrame] != null then
call BlzFrameSetVisible( w.textMessageIcon[whichFrame] , false )
call BlzFrameSetAlpha( w.textMessageIcon[whichFrame] , 255 )
endif
endfunction
private function FadeoutLoop takes nothing returns nothing
local integer i
local integer j = 1
local real scale
local NeatWindow w
if numMessagesOnScreen == 0 then
return
endif
loop
exitwhen j > numNeatWindows
set w = neatWindow[j]
set i = 0
loop
exitwhen i == w.maxTextMessages
if w.messageTimeRemaining[i] > 0 then
set w.messageTimeRemaining[i] = w.messageTimeRemaining[i] - TIME_STEP
set w.messageTimeElapsed[i] = w.messageTimeElapsed[i] + TIME_STEP
if w.messageTimeRemaining[i] < w.messageFormat[i].fadeOutTime then
if w.messageTimeRemaining[i] < 0 then
call HideTextMessage(w,i,false)
else
call BlzFrameSetAlpha( w.textMessageFrame[i] , R2I(255*w.messageTimeRemaining[i]/w.messageFormat[i].fadeOutTime) )
endif
elseif w.messageTimeElapsed[i] < w.messageFormat[i].fadeInTime then
call BlzFrameSetAlpha( w.textMessageFrame[i] , R2I(255*w.messageTimeElapsed[i]/w.messageFormat[i].fadeInTime) )
endif
if w.textMessageIconOrientation[i] != null then
call BlzFrameSetAlpha( w.textMessageIcon[i] , BlzFrameGetAlpha(w.textMessageFrame[i]) )
endif
endif
set i = i + 1
endloop
set j = j + 1
endloop
endfunction
private function RepositionAllMessages takes NeatWindow w returns nothing
local integer i
local real array yOffset
local real textBlockHeight
//=========================================================================================
//Get message heights
//=========================================================================================
set yOffset[0] = 0
set textBlockHeight = w.textHeight[0]
set i = 1
loop
exitwhen i > w.maxTextMessages - 1
set yOffset[i] = yOffset[i-1] + w.textHeight[i-1]
if BlzFrameGetText(w.textCarryingFrame[i]) != "" then
set textBlockHeight = RMinBJ( yOffset[i] + w.textHeight[i] , w.maxHeight )
endif
set i = i + 1
endloop
//=========================================================================================
//Reposition messages
//=========================================================================================
set i = 0
loop
exitwhen i > w.maxTextMessages - 1
if yOffset[i] + w.textHeight[i] > w.maxHeight then
call HideTextMessage(w,i,true)
elseif w.isTopToBottom then
call BlzFrameSetAbsPoint( w.textMessageFrame[i] , FRAMEPOINT_BOTTOMLEFT , w.xPosition , w.yPosition - w.textHeight[i] - yOffset[i] )
else
call BlzFrameSetAbsPoint( w.textMessageFrame[i] , FRAMEPOINT_BOTTOMLEFT , w.xPosition , w.yPosition + yOffset[i] )
endif
set i = i + 1
endloop
endfunction
private function AddTextMessage takes string whichText, real forcedDuration, NeatFormat whichFormat, NeatWindow w returns integer
local integer i
local real array yOffset
local real textBlockHeight
local framehandle tempFrame
if whichText == "" then
return 0
endif
static if COPY_TO_MESSAGE_LOG and not REPLACE_BLIZZARD_FUNCTION_CALLS then
if isSinglePlayer then
call DisplayTextToPlayer( GetLocalPlayer() , 0 , 0 , whichText + "
" )
call ClearTextMessages()
endif
endif
if BlzFrameGetText(w.textCarryingFrame[w.maxTextMessages - 1]) == "" then
set numMessagesOnScreen = numMessagesOnScreen + 1
endif
//=========================================================================================
//Transfer messages to next frame
//=========================================================================================
set tempFrame = w.textMessageIcon[w.maxTextMessages - 1]
if w.textMessageIconOrientation[w.maxTextMessages - 1] != null then
call BlzFrameSetVisible( w.textMessageIcon[w.maxTextMessages - 1] , false )
call BlzFrameSetAlpha( w.textMessageIcon[w.maxTextMessages - 1] , 255 )
endif
set i = w.maxTextMessages - 2
loop
exitwhen i < 0
set w.messageTimeRemaining[i+1] = w.messageTimeRemaining[i]
set w.messageTimeElapsed[i+1] = w.messageTimeElapsed[i]
set w.textMessageIcon[i+1] = w.textMessageIcon[i]
set w.textMessageIconOrientation[i+1] = w.textMessageIconOrientation[i]
if w.textMessageIconOrientation[i] != null then
if w.textMessageIconOrientation[i+1] == "topleft" then
call BlzFrameSetPoint( w.textMessageIcon[i+1] , FRAMEPOINT_TOPRIGHT , w.textMessageFrame[i+1] , FRAMEPOINT_TOPLEFT , 0 , 0 )
elseif w.textMessageIconOrientation[i+1] == "topright" then
call BlzFrameSetPoint( w.textMessageIcon[i+1] , FRAMEPOINT_TOPLEFT , w.textMessageFrame[i+1] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
elseif w.textMessageIconOrientation[i+1] == "bottomleft" then
call BlzFrameSetPoint( w.textMessageIcon[i+1] , FRAMEPOINT_BOTTOMRIGHT , w.textMessageFrame[i+1] , FRAMEPOINT_BOTTOMLEFT , 0 , 0 )
elseif w.textMessageIconOrientation[i+1] == "bottomright" then
call BlzFrameSetPoint( w.textMessageIcon[i+1] , FRAMEPOINT_BOTTOMLEFT , w.textMessageFrame[i+1] , FRAMEPOINT_BOTTOMRIGHT , 0 , 0 )
endif
endif
call ChangeTextFormatting(w, i+1, w.messageFormat[i])
set w.textHeight[i+1] = w.textHeight[i]
call ChangeText(w, i+1, BlzFrameGetText(w.textCarryingFrame[i]))
if w.messageOfFrame[i] != 0 then
set w.messageOfFrame[i+1] = w.messageOfFrame[i]
set frameOfMessage[w.messageOfFrame[i+1]] = i + 1
endif
if w.messageTimeRemaining[i+1] < w.messageFormat[i+1].fadeOutTime then
call BlzFrameSetAlpha( w.textMessageFrame[i+1] , R2I(255*w.messageTimeRemaining[i+1]/w.messageFormat[i+1].fadeOutTime) )
else
call BlzFrameSetAlpha( w.textMessageFrame[i+1] , 255 )
endif
call BlzFrameSetVisible( w.textMessageFrame[i+1] , true )
static if INCLUDE_FDF then
call BlzFrameSetVisible( w.textMessageBox[i+1] , BlzFrameIsVisible(w.textMessageBox[i]) )
endif
set i = i - 1
endloop
set w.textMessageIcon[0] = tempFrame
//=========================================================================================
//Setup new message
//=========================================================================================
call ChangeTextFormatting(w, 0, whichFormat)
call ChangeText(w, 0, whichText)
set w.textHeight[0] = BlzFrameGetHeight(w.textMessageFrame[0]) + RMaxBJ(whichFormat.spacing , w.messageFormat[1].spacing)
static if INCLUDE_FDF then
call BlzFrameSetVisible( w.textMessageBox[0], whichFormat.isBoxed )
endif
if forcedDuration != 0 then
set w.messageTimeRemaining[0] = forcedDuration + whichFormat.fadeOutTime
else
set w.messageTimeRemaining[0] = whichFormat.minDuration + whichFormat.durationIncrease*GetAdjustedStringLength(whichText) + whichFormat.fadeOutTime
endif
set w.messageTimeElapsed[0] = 0
if whichFormat.fadeInTime > 0 then
call BlzFrameSetAlpha(w.textMessageFrame[0] , 0)
else
call BlzFrameSetAlpha(w.textMessageFrame[0] , 255)
endif
call BlzFrameSetVisible( w.textMessageFrame[0] , true )
set w.textMessageIconOrientation[0] = null
if messageCounter == JASS_MAX_ARRAY_SIZE then
set messageCounter = 1
else
set messageCounter = messageCounter + 1
endif
set w.messageOfFrame[0] = messageCounter
set frameOfMessage[messageCounter] = 0
set windowOfMessage[messageCounter] = w
if w.maxTextMessages > 1 then
call RepositionAllMessages(w)
endif
return messageCounter
endfunction
private function Init takes nothing returns nothing
local integer i = 0
local integer p
local integer numPlayers
static if INCLUDE_FDF then
call BlzLoadTOCFile("NeatMessageTemplates.toc")
endif
call TimerStart( masterTimer , TIME_STEP , true , function FadeoutLoop )
set DEFAULT_NEAT_FORMAT = NeatFormat.create()
set DEFAULT_NEAT_WINDOW = NeatWindow.create(TEXT_MESSAGE_X_POSITION, TEXT_MESSAGE_Y_POSITION, TEXT_MESSAGE_BLOCK_WIDTH, TEXT_MESSAGE_BLOCK_MAX_HEIGHT, MAX_TEXT_MESSAGES, MESSAGE_ORDER_TOP_TO_BOTTOM)
static if COPY_TO_MESSAGE_LOG then
set p = 0
set numPlayers = 0
loop
exitwhen p > 23
if GetPlayerSlotState(Player(p)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(p)) == MAP_CONTROL_USER then
set numPlayers = numPlayers + 1
endif
set p = p + 1
endloop
set isSinglePlayer = numPlayers == 1
endif
static if REPLACE_BLIZZARD_FUNCTION_CALLS then
set currentNeatFormat = NeatFormat.create()
call ExecuteFunc("ResetNeatVars")
endif
endfunction
//===========================================================================================
//API
//===========================================================================================
//Constructors
//===========================================================================================
function NeatMessage takes string message returns integer
return AddTextMessage(message, 0, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endfunction
function NeatMessageToPlayer takes player whichPlayer, string message returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message , 0, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToForce takes force whichForce, string message returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, 0, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageTimed takes real duration, string message returns integer
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endfunction
function NeatMessageToPlayerTimed takes player whichPlayer, real duration, string message returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToForceTimed takes force whichForce, real duration, string message returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageFormatted takes string message, NeatFormat whichFormat returns integer
return AddTextMessage(message, 0, whichFormat, DEFAULT_NEAT_WINDOW)
endfunction
function NeatMessageToPlayerFormatted takes player whichPlayer, string message, NeatFormat whichFormat returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message , 0, whichFormat, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToForceFormatted takes force whichForce, string message, NeatFormat whichFormat returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, 0, whichFormat, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToPlayerTimedFormatted takes player whichPlayer, real duration, string message, NeatFormat whichFormat returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message, duration, whichFormat, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToForceTimedFormatted takes force whichForce, real duration, string message, NeatFormat whichFormat returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, duration, whichFormat, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageTimedFormatted takes real duration, string message, NeatFormat whichFormat returns integer
return AddTextMessage(message, duration, whichFormat, DEFAULT_NEAT_WINDOW)
endfunction
function NeatMessageInWindow takes string message, NeatWindow whichWindow returns integer
return AddTextMessage(message, 0, DEFAULT_NEAT_FORMAT, whichWindow)
endfunction
function NeatMessageToPlayerInWindow takes player whichPlayer, string message, NeatWindow whichWindow returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message , 0, DEFAULT_NEAT_FORMAT, whichWindow)
endif
return 0
endfunction
function NeatMessageToForceInWindow takes force whichForce, string message, NeatWindow whichWindow returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, 0, DEFAULT_NEAT_FORMAT, whichWindow)
endif
return 0
endfunction
function NeatMessageTimedInWindow takes real duration, string message, NeatWindow whichWindow returns integer
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, whichWindow)
endfunction
function NeatMessageToPlayerTimedInWindow takes player whichPlayer, real duration, string message, NeatWindow whichWindow returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, whichWindow)
endif
return 0
endfunction
function NeatMessageToForceTimedInWindow takes force whichForce, real duration, string message, NeatWindow whichWindow returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, whichWindow)
endif
return 0
endfunction
function NeatMessageFormattedInWindow takes string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
return AddTextMessage(message, 0, whichFormat, whichWindow)
endfunction
function NeatMessageToPlayerFormattedInWindow takes player whichPlayer, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message , 0, whichFormat, whichWindow)
endif
return 0
endfunction
function NeatMessageToForceFormattedInWindow takes force whichForce, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, 0, whichFormat, whichWindow)
endif
return 0
endfunction
function NeatMessageToPlayerTimedFormattedInWindow takes player whichPlayer, real duration, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message, duration, whichFormat, whichWindow)
endif
return 0
endfunction
function NeatMessageToForceTimedFormattedInWindow takes force whichForce, real duration, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, duration, whichFormat, whichWindow)
endif
return 0
endfunction
function NeatMessageTimedFormattedInWindow takes real duration, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
return AddTextMessage(message, duration, whichFormat, whichWindow)
endfunction
//Utility
//===========================================================================================
function EditNeatMessage takes integer messagePointer, string newText returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
if messagePointer == 0 or whichFrame == -1 then
return
endif
call ChangeText(whichWindow, whichFrame, newText)
set whichWindow.textHeight[whichFrame] = BlzFrameGetHeight(whichWindow.textMessageFrame[whichFrame]) + RMaxBJ(whichWindow.messageFormat[whichFrame].spacing , whichWindow.messageFormat[whichFrame+1].spacing)
call RepositionAllMessages(whichWindow)
endfunction
function AddNeatMessageTimeRemaining takes integer messagePointer, real additionalTime returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
if messagePointer == 0 or whichFrame == -1 then
return
endif
set whichWindow.messageTimeRemaining[whichFrame] = RMaxBJ(whichWindow.messageTimeRemaining[whichFrame] + additionalTime, whichWindow.messageFormat[whichFrame].fadeOutTime)
endfunction
function SetNeatMessageTimeRemaining takes integer messagePointer, real newTime returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
if messagePointer == 0 or whichFrame == -1 then
return
endif
set whichWindow.messageTimeRemaining[whichFrame] = RMaxBJ(newTime, whichWindow.messageFormat[whichFrame].fadeOutTime)
endfunction
function AutoSetNeatMessageTimeRemaining takes integer messagePointer, boolean accountForTimeElapsed returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
local NeatFormat whichFormat = whichWindow.messageFormat[whichFrame]
if messagePointer == 0 or whichFrame == -1 then
return
endif
set whichWindow.messageTimeRemaining[whichFrame] = whichFormat.minDuration + whichFormat.durationIncrease*GetAdjustedStringLength(BlzFrameGetText(whichWindow.textCarryingFrame[whichFrame])) + whichFormat.fadeOutTime
if accountForTimeElapsed then
set whichWindow.messageTimeRemaining[whichFrame] = RMaxBJ(whichWindow.messageTimeRemaining[whichFrame] - whichWindow.messageTimeElapsed[whichFrame], whichFormat.fadeOutTime)
endif
endfunction
function RemoveNeatMessage takes integer messagePointer returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
if messagePointer == 0 or whichFrame == -1 then
return
endif
set whichWindow.messageTimeRemaining[whichFrame] = whichWindow.messageFormat[whichFrame].fadeOutTime
endfunction
function IsNeatMessageDisplayed takes integer messagePointer returns boolean
return frameOfMessage[messagePointer] != -1
endfunction
function NeatMessageAddIcon takes integer messagePointer, real width, real height, string orientation, string texture returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
call BlzFrameSetVisible( whichWindow.textMessageIcon[whichFrame] , true )
call BlzFrameSetAlpha( whichWindow.textMessageIcon[whichFrame] , 255 )
call BlzFrameSetSize( whichWindow.textMessageIcon[whichFrame] , width , height )
if orientation == "topleft" then
call BlzFrameSetPoint( whichWindow.textMessageIcon[whichFrame] , FRAMEPOINT_TOPRIGHT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_TOPLEFT , 0 , 0 )
elseif orientation == "topright" then
call BlzFrameSetPoint( whichWindow.textMessageIcon[whichFrame] , FRAMEPOINT_TOPLEFT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
elseif orientation == "bottomleft" then
call BlzFrameSetPoint( whichWindow.textMessageIcon[whichFrame] , FRAMEPOINT_BOTTOMRIGHT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMLEFT , 0 , 0 )
elseif orientation == "bottomright" then
call BlzFrameSetPoint( whichWindow.textMessageIcon[whichFrame] , FRAMEPOINT_BOTTOMLEFT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMRIGHT , 0 , 0 )
else
debug call BJDebugMsg("Invalid icon orientation...")
return
endif
call BlzFrameSetTexture( whichWindow.textMessageIcon[whichFrame] , texture , 0 , true )
set whichWindow.textMessageIconOrientation[whichFrame] = orientation
if BlzFrameGetHeight( whichWindow.textMessageFrame[whichFrame] ) < height then
call BlzFrameSetSize( whichWindow.textCarryingFrame[whichFrame] , whichWindow.width / (whichWindow.messageFormat[whichFrame].fontSize/10.) , height - 0.018 )
static if INCLUDE_FDF then
call BlzFrameSetSize( whichWindow.textMessageFrame[whichFrame] , BlzFrameGetWidth(whichWindow.textMessageText[whichFrame]) + 0.008 , BlzFrameGetHeight(whichWindow.textMessageText[whichFrame]) + 0.009 )
call BlzFrameSetPoint( whichWindow.textMessageBox[whichFrame] , FRAMEPOINT_BOTTOMLEFT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMLEFT , 0 , -0.0007*(whichWindow.messageFormat[whichFrame].fontSize-13) )
call BlzFrameSetPoint( whichWindow.textMessageBox[whichFrame] , FRAMEPOINT_TOPRIGHT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
endif
set whichWindow.textHeight[whichFrame] = BlzFrameGetHeight(whichWindow.textMessageFrame[whichFrame]) + RMaxBJ(whichWindow.messageFormat[whichFrame].spacing , whichWindow.messageFormat[whichFrame+1].spacing)
call RepositionAllMessages(whichWindow)
endif
endfunction
function NeatMessageHideIcon takes integer messagePointer returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
call BlzFrameSetVisible( whichWindow.textMessageIcon[whichFrame] , false )
set whichWindow.textMessageIconOrientation[whichFrame] = null
endfunction
function ClearNeatMessagesForPlayer takes player whichPlayer returns nothing
local integer i
local integer j = 1
if GetLocalPlayer() == whichPlayer then
loop
exitwhen j > numNeatWindows
set i = 0
loop
exitwhen i > neatWindow[j].maxTextMessages - 1
call HideTextMessage(neatWindow[j], i, true)
set i = i + 1
endloop
set j = j + 1
endloop
endif
endfunction
function ClearNeatMessages takes nothing returns nothing
local integer i
local integer j = 1
if doNotClear then
return
endif
loop
exitwhen j > numNeatWindows
set i = 0
loop
exitwhen i > neatWindow[j].maxTextMessages - 1
call HideTextMessage(neatWindow[j], i, true)
set i = i + 1
endloop
set j = j + 1
endloop
endfunction
function ClearNeatMessagesForForce takes force whichForce returns nothing
local integer i
local integer j = 1
if IsPlayerInForce(GetLocalPlayer() , whichForce) then
loop
exitwhen j > numNeatWindows
set i = 0
loop
exitwhen i > neatWindow[j].maxTextMessages - 1
call HideTextMessage(neatWindow[j], i, true)
set i = i + 1
endloop
set j = j + 1
endloop
endif
endfunction
function ClearNeatMessagesForPlayerInWindow takes player whichPlayer, NeatWindow whichWindow returns nothing
local integer i
if GetLocalPlayer() == whichPlayer then
set i = 0
loop
exitwhen i > whichWindow.maxTextMessages - 1
call HideTextMessage(whichWindow, i, true)
set i = i + 1
endloop
endif
endfunction
function ClearNeatMessagesInWindow takes NeatWindow whichWindow returns nothing
local integer i
set i = 0
loop
exitwhen i > whichWindow.maxTextMessages - 1
call HideTextMessage(whichWindow, i, true)
set i = i + 1
endloop
endfunction
function ClearNeatMessagesForForceInWindow takes force whichForce, NeatWindow whichWindow returns nothing
local integer i
if IsPlayerInForce(GetLocalPlayer() , whichForce) then
set i = 0
loop
exitwhen i > whichWindow.maxTextMessages - 1
call HideTextMessage(whichWindow, i, true)
set i = i + 1
endloop
endif
endfunction
//===========================================================================================
//GUI API
//===========================================================================================
static if REPLACE_BLIZZARD_FUNCTION_CALLS then
globals
NeatFormat currentNeatFormat
private hashtable GUIhash = InitHashtable()
endglobals
function CreateNeatFormat takes string formatName returns nothing
local NeatFormat whichFormat = NeatFormat.create()
call SaveInteger( GUIhash , StringHash(formatName) , 0 , whichFormat )
set whichFormat.spacing = udg_spacing
set whichFormat.minDuration = udg_minDuration
set whichFormat.durationIncrease = udg_durationIncrease
set whichFormat.isBoxed = udg_isBoxed
set whichFormat.fadeOutTime = udg_fadeOutTime
set whichFormat.fadeInTime = udg_fadeInTime
set whichFormat.fontSize = udg_fontSize
if udg_verticalAlignment == "bottom" then
set whichFormat.verticalAlignment = TEXT_JUSTIFY_BOTTOM
elseif udg_verticalAlignment == "middle" then
set whichFormat.verticalAlignment = TEXT_JUSTIFY_MIDDLE
elseif udg_verticalAlignment == "top" then
set whichFormat.verticalAlignment = TEXT_JUSTIFY_TOP
else
debug call BJDebugMsg("Invalid vertical alignment specificed. Valid types are bottom, middle, and top.")
endif
if udg_horizontalAlignment == "left" then
set whichFormat.horizontalAlignment = TEXT_JUSTIFY_LEFT
elseif udg_horizontalAlignment == "center" then
set whichFormat.horizontalAlignment = TEXT_JUSTIFY_CENTER
elseif udg_horizontalAlignment == "right" then
set whichFormat.horizontalAlignment = TEXT_JUSTIFY_RIGHT
else
debug call BJDebugMsg("Invalid horizontal alignment specificed. Valid types are left, center, and right.")
endif
endfunction
function SetNeatFormat takes string formatName returns nothing
local NeatFormat whichFormat = LoadInteger( GUIhash , StringHash(formatName) , 0 )
if whichFormat == 0 then
debug call BJDebugMsg("Could not find neat format with specified name...")
else
set currentNeatFormat = whichFormat
endif
endfunction
function ResetNeatVars takes nothing returns nothing
set udg_spacing = SPACING_BETWEEN_MESSAGES
set udg_minDuration = MESSAGE_MINIMUM_DURATION
set udg_durationIncrease = MESSAGE_DURATION_INCREASE_PER_CHARACTER
set udg_isBoxed = BOXED_MESSAGES
set udg_fadeOutTime = FADE_OUT_TIME
set udg_fadeInTime = FADE_IN_TIME
set udg_fontSize = TEXT_MESSAGE_FONT_SIZE
if VERTICAL_ALIGNMENT == TEXT_JUSTIFY_BOTTOM then
set udg_verticalAlignment = "bottom"
elseif VERTICAL_ALIGNMENT == TEXT_JUSTIFY_MIDDLE then
set udg_verticalAlignment = "middle"
elseif VERTICAL_ALIGNMENT == TEXT_JUSTIFY_TOP then
set udg_verticalAlignment = "top"
endif
if HORIZONTAL_ALIGNMENT == TEXT_JUSTIFY_LEFT then
set udg_horizontalAlignment = "left"
elseif HORIZONTAL_ALIGNMENT == TEXT_JUSTIFY_CENTER then
set udg_horizontalAlignment = "center"
elseif HORIZONTAL_ALIGNMENT == TEXT_JUSTIFY_RIGHT then
set udg_horizontalAlignment = "right"
endif
endfunction
function ResetNeatFormat takes nothing returns nothing
set currentNeatFormat = DEFAULT_NEAT_FORMAT
endfunction
//===========================================================================================
//GUI Helper functions
//===========================================================================================
function DisplayTextToForceHook takes force whichForce, string message returns nothing
local string extractedString
call BlzSetAbilityTooltip( TOOLTIP_ABILITY , message , 0 )
set extractedString = BlzGetAbilityTooltip( TOOLTIP_ABILITY , 0 )
call NeatMessageToForceFormatted( whichForce , extractedString , currentNeatFormat )
call TimerStart( clearTextTimer , 0.0 , false , function ClearText )
endfunction
function DisplayTimedTextToForceHook takes force whichForce, real duration, string message returns nothing
local string extractedString
call BlzSetAbilityTooltip( TOOLTIP_ABILITY , message , 0 )
set extractedString = BlzGetAbilityTooltip( TOOLTIP_ABILITY , 0 )
call NeatMessageToForceTimedFormatted( whichForce , duration , extractedString, currentNeatFormat )
call TimerStart( clearTextTimer , 0.0 , false , function ClearText )
endfunction
endif
endlibrary
/*
==========================================================================================================================================================
Antares' Hero Selection
==========================================================================================================================================================
A P I
==========================================================================================================================================================
function InitHeroSelection takes nothing returns nothing //Creates hero list, player list, and frames.
function BeginHeroSelection takes nothing returns nothing //Enforces camera and/or shows menu, based on config.
function EnableHeroSelection takes nothing returns nothing //Allows heroes to be picked.
function EnablePlayerHeroSelection takes player whichPlayer, boolean enable returns nothing
function EnablePlayerHeroPreselection takes player whichPlayer, boolean enable returns nothing
function PlayerEscapeHeroSelection takes player whichPlayer returns nothing //Hides frames and unlocks camera for player.
function PlayerReturnToHeroSelection takes player whichPlayer returns nothing //Allows player to repick his or her hero.
function HeroSelectionAllRandom takes nothing returns nothing
function HeroSelectionPlayerForceSelect takes player whichPlayer, Hero whichHero returns nothing
function HeroSelectionPlayerForceRandom takes player whichPlayer returns nothing
function HeroSelectionForceEnd takes nothing returns nothing //Will give each player who hasn't picked a random hero.
function BanHeroFromSelection takes Hero whichHero, boolean disable returns nothing //Hero will remain in the menu.
function HeroSelectionSetTimeRemaining takes real time returns nothing
function HeroSelectionAddTimeRemaining takes real time returns nothing
function RestartHeroSelection takes nothing returns nothing //For game restarts. Resets all variables and calls BeginHeroSelection. No one can be in hero selection at the time.
function StartHeroBanPhase takes real timeLimit returns nothing //Starts a ban phase with specified time limit, after which EnableHeroSelection is called. Requires MENU_INCLUDE_BAN_BUTTON.
function StartHeroSelectionTimer takes real timeLimit, code callback returns nothing //Starts the timer that is displayed during hero selection with a custom callback function.
function NoOneInHeroSelection takes nothing returns boolean
function EveryoneHasHero takes nothing returns boolean
==========================================================================================================================================================
How to import:
Copy into empty triggers in your map:
HeroSelectionDocumentation
HeroSelectionConfig
HeroSelectionCallbacks
HeroDeclaration
HeroSelection
Import:
HeroSelectionMenu.fdf
CustomTooltip.fdf
HeroSelectionTemplates.toc
GeneralHeroGlow.mdx (optional)
Copy Destructables:
Hero No Shadow (optional)
Hero Shadow (optional)
===========================================================================================
Getting started:
HeroSelection has a long config that you should go through from top to bottom, with the
customization options becoming more and more specific as you progress. The fine-tuning
section has a vast number of options, which may look overwhelming, but you do not need
to edit any of them to get a working system.
To get a working system, here is the To-Do List:
1. Set up the terrain and the hero selection camera.
2. Import your heroes into HeroDeclaration and set the fields.
3. Set up abilities that are used to fetch hero descriptions and icons.
4. Create a trigger in your map that initializes the hero selection.
5. Set up the interface functions OnEscape and OnFinal.
===========================================================================================
1. Hero Selection Camera
To set up the hero selection camera, edit
FOREGROUND_HERO_X
FOREGROUND_HERO_Y
HERO_SELECTION_ANGLE
===========================================================================================
2. Importing Heroes
To import your heroes, go into HeroDeclaration and create a repository for your heroes:
globals
Hero MyHero1
Hero MyHero2
...
endglobals
In the HeroDeclaration function, initialize the heroes with
set MyHero1 = Hero.create()
set MyHero2 = Hero.create()
...
Then, set the fields for your heroes as specified. Some of the variables may already be
set elsewhere in your map. In that case, I recommend writing a function to automatically
copy the variables into those fields.
Note that fields that should be "false", "null", or "0" do not have to be set as they are the
default values.
===========================================================================================
3. Tooltip abilities
To set up a tooltip ability, create a new non-hero ability with one level. The tooltip will be
used to create the hero description.
===========================================================================================
4. Initializing Hero Selection
This system has no init function that runs automatically, since it requires the hero and
player lists to initialize and you may want to set them at a later point. To initialize, do:
call InitHeroSelection()
This will create frames, create the hero list, and initialize players. To start hero selection, do:
call BeginHeroSelection()
This will enforce the hero selection camera and/or show the menu to players, depending on
your config. However, players will not be able to select heroes until you do:
call EnableHeroSelection()
===========================================================================================
5. Creating the Interface
Now that we have a trigger to begin hero selection, we have to be able to exit it and
create the heroes that were selected. To do this, edit the OnEscape and OnFinal function
in the callbacks section. The variables you can use in those functions are "heroIdOfPlayer",
which stores the unit id of the hero selected, and "heroIconPathOfPlayer", which can be useful
for updating a multiboard icon, for example.
===========================================================================================
After the hero selection system is successfully integrated into your map, go to the config
section and adjust the parameters controlling the system's behavior. Then, when everything
is working as intended, you can go into the fine-tuning section and go nuts customizing
every part of the system.
You may want to change some parameters based on the game state. In that case, make the
parameter public and un-constant it. Note, however, that changing some constants after
hero selection has been initialized might have no effect and/or lead to bugs.
===========================================================================================
You can customize the appearance of the hero selection menu background by editing the
HeroSelectionMenu.fdf. By default, the background uses the escape menu textures of whatever
race the player is playing, but this can be changed by editing BackdropBackground and
BackdropEdgeFile. Remove DecorateFileNames when using imported assets!
One important parameter is BackdropCornerSize, which controls the thickness of the border
texture. Make sure to update MENU_BORDER_TILE_SIZE accordingly, so that the textures remain
seamless.
Read more here on how to edit an .fdf file:
https://www.hiveworkshop.com/threads/ui-reading-a-fdf.315850/
===========================================================================================
Neat Messages:
The showcase map includes NeatMessages, a library designed to give more control over text
messages. With default text messages, you will most likely run into the problem that they
cannot be placed where you want them to and they might hide a UI element or the heroes as a
result. Therefore, if you want to add text messages to the hero selection, I strongly
recommend using NeatMessages.
https://www.hiveworkshop.com/threads/neat-text-messages.350336/
===========================================================================================
How to setup multiple pages:
Some maps might have more heroes than fit on one page. To setup multiple pages, divide your
heroes into multiple categories in HeroDeclaration. To hide the category captions, set the
CATEGORY_NAMES to null in the config under SetCategories. Now, set the PAGE_OF_CATEGORY
to the page at which the heroes of that category should be appear.
===========================================================================================
How to setup a draft:
To setup a draft, call BeginHeroSelection, but then, instead of using EnableHeroSelection,
call EnablePlayerHeroSelection for the player that should pick the first hero. Set a time
limit for that player picking his or her hero with StartHeroSelectionTimer(timeLimit, yourCallback).
In your callback, call HeroSelectionPlayerForceRandom. Finally, in the OnPick custom code function,
write logic to enable hero selection for the next player.
===========================================================================================
Screen coordinates:
All UI element positions are given in screen coordinates. x-coordinates go from 0 (left) to
0.8, excluding the widescreen area (no frame can be put there). y-coordinates go from 0 (bottom)
to 0.6 (top). On a 1080p monitor, 0.00056 corresponds to 1 pixel. 0.01 corresponds to ~18 pixel.
You can place the hero selection menu as well as the mouse-over tooltips into the widescreen
area by setting the x-position to a negative number / a number greater than 0.8. The width of
the widescreen area is 0.6*(16/9 - 4/3)/2 = 0.1333, so the lowest possible x-coordinate is
-0.1333 and the highest 0.9333. The position will automatically be adjusted for players who
are not using a widescreen monitor.
Read more on screen coordinates here:
https://www.hiveworkshop.com/threads/ui-positionate-frames.315860/
===========================================================================================
Page cycle button styles (for PAGE_CYCLE_BUTTON_TEXT_STYLE):
"EnvelopRandomButton" Icon-style buttons directly left and right of the random buttons.
"LeftRightMenuButton" Icon-style buttons left and right of the menu in the same row as the random buttons.
"BelowRandomButton" Icon-style buttons below the random buttons.
"LeftRightMiddleButton" Icon-style buttons in the middle of the left and right edges of the menu.
"RightVerticalButton" Icon-style buttons in the middle of the right edge of the menu, arranged vertically.
"EnvelopAcceptButton" Text-style buttons directly left and right of the accept button.
"LeftRightBottomButton" Text-style buttons on the bottom edge in the left and right corners.
"TangentTopButton" Text-style buttons in the center of the top edge right next to each other.
"LeftRightTopButton" Text-style buttons on the top edge in the left and right corners.
*/
library HeroDeclaration
globals
Hero Myrmidon
Hero Paladin
Hero Blademaster
Hero Shadow_Hunter
Hero Tauren_Chieftain
Hero MoonPriestess
Hero Knight
Hero Beastmaster
Hero Dreadlord
Hero Lich
Hero Crypt_Lord
Hero Pit_Lord
Hero Ghost
Hero Warlock
Hero Archmage_Female
Hero Archmage_Male
Hero Marine
Hero Druid_of_the_Claw
Hero Runemaster
Hero Blood_Mage
Hero Ranger
Hero Warden
Hero Demon_Hunter
Hero Huntress
Hero Dryad
Hero Sorceress
Hero SunAdept
Hero Mountain_Cleric
Hero Mountain_King
Hero Rifleman
Hero Stone_Giant
Hero PandarenBrewmaster
Hero Storm_Brewmaster
Hero Earth_Brewmaster
Hero Fire_Brewmaster
endglobals
function HeroDeclaration takes nothing returns nothing
//========================================================================================
//Here you declare all heroes in your map. The order in which you create them determine the order in which they appear in the hero selection menu.
//The fields that should be set are:
/*
integer array abilities The abilities of this hero. Starts at index 1.
boolean array isNonHeroAbility Set this flag if the ability at the index is a non-hero ability. This will make the tooltip instead of the research tooltip appear on mouse-over.
integer unitId Unit id of the hero.
integer tooltipAbility Set up any non-hero ability with one level for each hero and write the hero's description in its tooltip.
string selectEmote Sound path of the emote the hero should play when selected.
animtype selectAnim animType of select animation. For example, "spell" is ANIMTYPE_SPELL.
subanimtype selectSubAnim subanimtype of select animation. For example "spell slam" is ANIMTYPE_SPELL + SUBANIMTYPE_SLAM.
real selectAnimLength Length of the select animation. If set incorrectly, animation will be interrupted or freeze.
integer category In which category in the menu should this hero be put?
boolean needsHeroGlow A hero glow will be added to heroes with models that don't have a glow. Requires "GeneralHeroGlow.mdx" to be imported.
boolean unavailable Heroes with this flag will appear in the menu but cannot be picked.
boolean array unavailableToTeam Hero will not appear in the menu for players in a team for which this flag was set. Can be used to create completely different hero rosters for different teams.
*/
//========================================================================================
set Archmage_Female = Hero.create()
set Archmage_Male = Hero.create()
set Beastmaster = Hero.create()
set Blademaster = Hero.create()
set Blood_Mage = Hero.create()
set Crypt_Lord = Hero.create()
set Demon_Hunter = Hero.create()
set Dreadlord = Hero.create()
set Druid_of_the_Claw = Hero.create()
set Dryad = Hero.create()
set Earth_Brewmaster = Hero.create()
set Fire_Brewmaster = Hero.create()
set Ghost = Hero.create()
set Huntress = Hero.create()
set Knight = Hero.create()
set Lich = Hero.create()
set Marine = Hero.create()
set MoonPriestess = Hero.create()
set Mountain_Cleric = Hero.create()
set Mountain_King = Hero.create()
set Myrmidon = Hero.create()
set Paladin = Hero.create()
set PandarenBrewmaster = Hero.create()
set Pit_Lord = Hero.create()
set Ranger = Hero.create()
set Rifleman = Hero.create()
set Runemaster = Hero.create()
set Shadow_Hunter = Hero.create()
set Sorceress = Hero.create()
set Stone_Giant = Hero.create()
set Storm_Brewmaster = Hero.create()
set SunAdept = Hero.create()
set Tauren_Chieftain = Hero.create()
set Warden = Hero.create()
set Warlock = Hero.create()
//========================================================================================
// Archmage female
set Archmage_Female.abilities[1] = 'ACmf' // Mana Shield
set Archmage_Female.abilities[2] = 'A00G' /// Crushing Wave
set Archmage_Female.abilities[3] = 'A01B' // Summon Sea Elemental
set Archmage_Female.abilities[4] = 'AHdr' // Drain Life & Mana
set Archmage_Female.abilities[5] = 'A02O' // Frost paralysis
set Archmage_Female.abilities[6] = 'A04R' // Elemental Wave
set Archmage_Female.abilities[7] = 'A022' // Rain of Eis
set Archmage_Female.isNonHeroAbility[1] = true
set Archmage_Female.isNonHeroAbility[7] = true
set Archmage_Female.selectEmote = "Units\\Human\\Jaina\\JainaWarcry1.flac"
set Archmage_Female.unitId = 'Hjai' // Archmage (f) (Hero)
set Archmage_Female.tooltipAbility = 'ds01' // Archmage (f) (Hero Description)
set Archmage_Female.needsHeroGlow = true
set Archmage_Female.category = 2
set Archmage_Female.selectAnim = ANIM_TYPE_SPELL
set Archmage_Female.selectSubAnim = null
set Archmage_Female.selectAnimLength = 2.700
//========================================================================================
// Archmage male
set Archmage_Male.abilities[1] = 'A00B' // Frigid Protection
set Archmage_Male.abilities[2] = 'A005' // Blizzard
set Archmage_Male.abilities[3] = 'AHwe' // Summon Water Elemental
set Archmage_Male.abilities[4] = 'A003' // Frost Bolt
set Archmage_Male.abilities[5] = 'A00P' // Debilitation Aura
set Archmage_Male.abilities[6] = 'AEsf' // Starfall
set Archmage_Male.abilities[7] = 'A074' // Water shield
set Archmage_Male.isNonHeroAbility[1] = true
set Archmage_Male.isNonHeroAbility[7] = true
set Archmage_Male.selectEmote = "Units\\Human\\HeroArchMage\\HeroArchMageWarcry1.flac"
set Archmage_Male.unitId = 'Hamg' // Archmage (m) (Hero)
set Archmage_Male.tooltipAbility = 'ds02' // Archmage (m) (Hero Description)
set Archmage_Male.needsHeroGlow = true
set Archmage_Male.category = 2
set Archmage_Male.selectAnim = ANIM_TYPE_SPELL
set Archmage_Male.selectSubAnim = null
set Archmage_Male.selectAnimLength = 2.700
//========================================================================================
// Beastmaster
set Beastmaster.abilities[1] = 'A009' // Summon Spirit Pig
set Beastmaster.abilities[2] = 'ANsg' // Summon Bear
set Beastmaster.abilities[3] = 'ACs7' // Summon Spirit Wolves
set Beastmaster.abilities[4] = 'ANsw' // Summon Hawk
set Beastmaster.abilities[5] = 'AEah' // Thorns Aura
set Beastmaster.abilities[6] = 'Arsp' // Stampede
set Beastmaster.abilities[7] = 'A02W' // Terror Wolf
set Beastmaster.isNonHeroAbility[1] = true
set Beastmaster.isNonHeroAbility[7] = true
set Beastmaster.selectEmote = "Units\\Creeps\\Beastmaster\\BeastmasterWarcry1.flac"
set Beastmaster.unitId = 'Nbst' // Beastmaster (Hero)
set Beastmaster.tooltipAbility = 'ds03' // Beastmaster (Hero Description)
set Beastmaster.needsHeroGlow = true
set Beastmaster.category = 3
set Beastmaster.selectAnim = ANIM_TYPE_SPELL
set Beastmaster.selectSubAnim = null
set Beastmaster.selectAnimLength = 0.967
//========================================================================================
// Blademaster
set Blademaster.abilities[1] = 'A04N' // Bersek
set Blademaster.abilities[2] = 'Agyv' // Whirlwinds
set Blademaster.abilities[3] = 'AOmi' // Mirror Image
set Blademaster.abilities[4] = 'AOcr' // Critical Strike
set Blademaster.abilities[5] = 'A045' // War Drums Aura
set Blademaster.abilities[6] = 'AOww' // Bladestorm
set Blademaster.abilities[7] = 'A01F' // Partition
set Blademaster.isNonHeroAbility[1] = true
set Blademaster.isNonHeroAbility[7] = true
set Blademaster.selectEmote = "Units\\Orc\\HeroBlademaster\\HeroBlademasterWarcry1.flac"
set Blademaster.unitId = 'Obla' // Blademaster (Hero)
set Blademaster.tooltipAbility = 'ds04' // Blademaster (Hero Description)
set Blademaster.needsHeroGlow = true
set Blademaster.category = 1
set Blademaster.selectAnim = ANIM_TYPE_ATTACK
set Blademaster.selectSubAnim = null
set Blademaster.selectAnimLength = 1.167
//========================================================================================
// Blood Mage
set Blood_Mage.abilities[1] = 'Amls' // Aerial Shackles
set Blood_Mage.abilities[2] = 'A02S' // Burning Oil
set Blood_Mage.abilities[3] = 'ANbf' // Breath of Fire
set Blood_Mage.abilities[4] = 'A00I' // Firebolt
set Blood_Mage.abilities[5] = 'ANic' // Incinerate
set Blood_Mage.abilities[6] = 'AHpx' // Phoenix
set Blood_Mage.abilities[7] = 'A00S' // Rain Of Fire
set Blood_Mage.isNonHeroAbility[1] = true
set Blood_Mage.isNonHeroAbility[7] = true
set Blood_Mage.selectEmote = "Units\\Human\\HeroBloodElf\\HeroBloodElfMageWarcry1.flac"
set Blood_Mage.unitId = 'Hblm' // Blood Mage (Hero)
set Blood_Mage.tooltipAbility = 'ds05' // Blood Mage (Hero Description)
set Blood_Mage.needsHeroGlow = true
set Blood_Mage.category = 2
set Blood_Mage.selectAnim = ANIM_TYPE_SPELL
set Blood_Mage.selectSubAnim = null
set Blood_Mage.selectAnimLength = 1.500
//========================================================================================
// Crypt Lord
set Crypt_Lord.abilities[1] = 'A024' // Anub'arak's Claws
set Crypt_Lord.abilities[2] = 'AUim' // Impale
set Crypt_Lord.abilities[3] = 'AUcb' // Carrion Beetles
set Crypt_Lord.abilities[4] = 'A03B' // Plague Sting
set Crypt_Lord.abilities[5] = 'AUts' // Spiked Carapace
set Crypt_Lord.abilities[6] = 'AUls' // Locust Swarm
set Crypt_Lord.abilities[7] = 'A00Q' // Burrow Impale
set Crypt_Lord.isNonHeroAbility[1] = true
set Crypt_Lord.isNonHeroAbility[7] = true
set Crypt_Lord.selectEmote = "Units\\Undead\\HeroCryptLord\\HeroCryptLordWarcry1.flac"
set Crypt_Lord.unitId = 'Ucrl' // Crypt Lord (Hero)
set Crypt_Lord.tooltipAbility = 'ds06' // Crypt Lord (Hero Description)
set Crypt_Lord.needsHeroGlow = true
set Crypt_Lord.category = 4
set Crypt_Lord.selectAnim = ANIM_TYPE_SPELL
set Crypt_Lord.selectSubAnim = null
set Crypt_Lord.selectAnimLength = 1.200
//========================================================================================
// Demon Hunter
set Demon_Hunter.abilities[1] = 'ACr1' // Roar
set Demon_Hunter.abilities[2] = 'A00K' // Negative Energy
set Demon_Hunter.abilities[3] = 'AEim' // Immolation
set Demon_Hunter.abilities[4] = 'AEev' // Evasion
set Demon_Hunter.abilities[5] = 'A076' // Vampiric Aura
set Demon_Hunter.abilities[6] = 'AEme' // Metamorphosis
set Demon_Hunter.abilities[7] = 'A02K' // Resistant Skin & Upgrade
set Demon_Hunter.isNonHeroAbility[1] = true
set Demon_Hunter.isNonHeroAbility[7] = true
set Demon_Hunter.selectEmote = "Units\\NightElf\\Illidan\\IllidanWarcry1.flac"
set Demon_Hunter.unitId = 'Edem' // Demon Hunter (Hero)
set Demon_Hunter.tooltipAbility = 'ds07' // Demon Hunter (Hero Description)
set Demon_Hunter.needsHeroGlow = true
set Demon_Hunter.category = 1
set Demon_Hunter.selectAnim = ANIM_TYPE_ATTACK
set Demon_Hunter.selectSubAnim = null
set Demon_Hunter.selectAnimLength = 1.333
//========================================================================================
// Dreadlord
set Dreadlord.abilities[1] = 'A07O' // Cursed Claws
set Dreadlord.abilities[2] = 'AUcs' // Carrion Swarm
set Dreadlord.abilities[3] = 'A01P' // Chaos
set Dreadlord.abilities[4] = 'A01R' // Death Coil
set Dreadlord.abilities[5] = 'A034' // Aura of Blight
set Dreadlord.abilities[6] = 'AUin' // Inferno
set Dreadlord.abilities[7] = 'A03O' // Infernal Fall
set Dreadlord.isNonHeroAbility[1] = true
set Dreadlord.isNonHeroAbility[7] = true
set Dreadlord.selectEmote = "Units\\Undead\\HeroDreadlord\\HeroDreadlordWarcry1.flac"
set Dreadlord.unitId = 'Udre' // Dreadlord (Hero)
set Dreadlord.tooltipAbility = 'ds08' // Dreadlord(Hero Description)
set Dreadlord.needsHeroGlow = true
set Dreadlord.category = 2
set Dreadlord.selectAnim = ANIM_TYPE_SPELL
set Dreadlord.selectSubAnim = null
set Dreadlord.selectAnimLength = 1.833
//========================================================================================
// Druid of the Claw
set Druid_of_the_Claw.abilities[1] = 'A059' // Strength of the Wild
set Druid_of_the_Claw.abilities[2] = 'AEer' // Entangling Roots
set Druid_of_the_Claw.abilities[3] = 'A054' // Rejuvenation
set Druid_of_the_Claw.abilities[4] = 'A00W' // Mark of the Claw
set Druid_of_the_Claw.abilities[5] = 'A05A' // Hardened Skin
set Druid_of_the_Claw.abilities[6] = 'A05B' // Bear Form
set Druid_of_the_Claw.abilities[7] = 'A05D' // Owl Scout
set Druid_of_the_Claw.isNonHeroAbility[1] = true
set Druid_of_the_Claw.isNonHeroAbility[7] = true
set Druid_of_the_Claw.selectEmote = "Units\\NightElf\\DruidoftheClaw\\DruidoftheClawWarcry1.flac"
set Druid_of_the_Claw.unitId = 'E00B' // Druid of the Claw (Hero)
set Druid_of_the_Claw.tooltipAbility = 'ds09' // Druid of the Claw (Hero Description)
set Druid_of_the_Claw.needsHeroGlow = true
set Druid_of_the_Claw.category = 1
set Druid_of_the_Claw.selectAnim = ANIM_TYPE_SPELL
set Druid_of_the_Claw.selectSubAnim = null
set Druid_of_the_Claw.selectAnimLength = 2.167
//========================================================================================
// Dryad
set Dryad.abilities[1] = 'ACvs' // Envenomed Weapons
set Dryad.abilities[2] = 'A02A' // Multiple Arrows
set Dryad.abilities[3] = 'A028' // Evasion
set Dryad.abilities[4] = 'A08N' // Piercing Arrows
set Dryad.abilities[5] = 'AOae' // Endurance Aura
set Dryad.abilities[6] = 'A029' // TrueshotAura
set Dryad.abilities[7] = 'A027' // Spell Immunity
set Dryad.isNonHeroAbility[1] = true
set Dryad.isNonHeroAbility[7] = true
set Dryad.selectEmote = "Units\\NightElf\\Dryad\\DryadWarcry1.flac"
set Dryad.unitId = 'H007' // Dryad (Hero)
set Dryad.tooltipAbility = 'ds10' // Dryad (Hero Description)
set Dryad.needsHeroGlow = true
set Dryad.category = 1
set Dryad.selectAnim = ANIM_TYPE_ATTACK
set Dryad.selectSubAnim = null
set Dryad.selectAnimLength = 1.500
//========================================================================================
// Earth_Brewmaster
set Earth_Brewmaster.abilities[1] = 'ACro' // Battle Roar
set Earth_Brewmaster.abilities[2] = 'AOsh' // Shockwave
set Earth_Brewmaster.abilities[3] = 'AOws' // War Stomp
set Earth_Brewmaster.abilities[4] = 'A055' // Pulverize
set Earth_Brewmaster.abilities[5] = 'AOae' // Endurance Aura
set Earth_Brewmaster.abilities[6] = 'AOeq' // Earthquake
set Earth_Brewmaster.abilities[7] = 'A07N' // BeastAttack
set Earth_Brewmaster.isNonHeroAbility[1] = true
set Earth_Brewmaster.isNonHeroAbility[7] = true
set Earth_Brewmaster.selectEmote = "Units\\Creeps\\EarthPandarenBrewmaster\\EarthPandarenBrewmasterWarcry1.flac"
set Earth_Brewmaster.unitId = 'O008' // Earth Brewmaster (Hero)
set Earth_Brewmaster.tooltipAbility = 'ds11' // Earth Brewmaster (Hero Description)
set Earth_Brewmaster.needsHeroGlow = true
set Earth_Brewmaster.category = 2
set Earth_Brewmaster.selectAnim = ANIM_TYPE_ATTACK
set Earth_Brewmaster.selectSubAnim = null
set Earth_Brewmaster.selectAnimLength = 1.200
//========================================================================================
// Fire_Brewmaster
set Fire_Brewmaster.abilities[1] = 'Amls' // Aerial Shackles
set Fire_Brewmaster.abilities[2] = 'Acdh' // Drunken Haze
set Fire_Brewmaster.abilities[3] = 'ANbf' // Breath of Fire
set Fire_Brewmaster.abilities[4] = 'A00I' // Firebolt
set Fire_Brewmaster.abilities[5] = 'A03J' // Permanent Immolation
set Fire_Brewmaster.abilities[6] = 'AHpx' // Phoenix
set Fire_Brewmaster.abilities[7] = 'A00S' // Rain Of Fire
set Fire_Brewmaster.isNonHeroAbility[1] = true
set Fire_Brewmaster.isNonHeroAbility[7] = true
set Fire_Brewmaster.selectEmote = "Units\\Creeps\\FirePandarenBrewmaster\\FirePandarenBrewmasterWarcry1.flac"
set Fire_Brewmaster.unitId = 'O009' // Fire Brewmaster (Hero)
set Fire_Brewmaster.tooltipAbility = 'ds12' // Fire Brewmaster (Hero Description)
set Fire_Brewmaster.needsHeroGlow = true
set Fire_Brewmaster.category = 2
set Fire_Brewmaster.selectAnim = ANIM_TYPE_ATTACK
set Fire_Brewmaster.selectSubAnim = null
set Fire_Brewmaster.selectAnimLength = 1.200
//========================================================================================
// Ghost
set Ghost.abilities[1] = 'A03S' // Command Aura
set Ghost.abilities[2] = 'A044' // Death Coil
set Ghost.abilities[3] = 'A04G' // Permanent Immolation
set Ghost.abilities[4] = 'AOcr' // Critical Strike
set Ghost.abilities[5] = 'AOae' // Endurance Aura
set Ghost.abilities[6] = 'A03P' // Phantom Fleet
set Ghost.abilities[7] = 'A00X' // Rain Of Chaos
set Ghost.isNonHeroAbility[1] = true
set Ghost.isNonHeroAbility[7] = true
set Ghost.selectEmote = "Units\\Undead\\ObsidianDestroyer\\ObsidianDestroyerWarcry1.flac"
set Ghost.unitId = 'N00W' // Ghost (Hero)
set Ghost.tooltipAbility = 'ds13' // Ghost (Hero Description)
set Ghost.needsHeroGlow = true
set Ghost.category = 1
set Ghost.selectAnim = ANIM_TYPE_SPELL
set Ghost.selectSubAnim = null
set Ghost.selectAnimLength = 1.167
//========================================================================================
// Huntress
set Huntress.abilities[1] = 'Amgr' // Moon Glaive
set Huntress.abilities[2] = 'A02B' // Own Lightning
set Huntress.abilities[3] = 'A02H' // Forked Lightning
set Huntress.abilities[4] = 'A02C' // Elune's Grace
set Huntress.abilities[5] = 'A030' // Command Aura
set Huntress.abilities[6] = 'A02G' // Moon Explosion
set Huntress.abilities[7] = 'A007' // Neutralisation
set Huntress.isNonHeroAbility[1] = true
set Huntress.isNonHeroAbility[7] = true
set Huntress.selectEmote = "Units\\NighElf\\Huntress\\HuntressWarcry1.flac"
set Huntress.unitId = 'H000' // Huntress (Hero)
set Huntress.tooltipAbility = 'ds14' // Huntress (Hero Description)
set Huntress.needsHeroGlow = true
set Huntress.category = 1
set Huntress.selectAnim = ANIM_TYPE_SPELL
set Huntress.selectSubAnim = null
set Huntress.selectAnimLength = 1.000
//========================================================================================
// Knight
set Knight.abilities[1] = 'ACce' // Cleaving Attack
set Knight.abilities[2] = 'A01S' // Holy Light
set Knight.abilities[3] = 'A02J' // Divine Shield
set Knight.abilities[4] = 'A011' // Sword Mastery
set Knight.abilities[5] = 'AOae' // Endurance Aura
set Knight.abilities[6] = 'A012' // Light Roar
set Knight.abilities[7] = 'A06A' // Knight Armor
set Knight.isNonHeroAbility[1] = true
set Knight.isNonHeroAbility[7] = true
set Knight.selectEmote = "Units\\NightElf\\HeroWarden\\HeroWardenWarcry1.flac"
set Knight.unitId = 'Harf' // Knight (Hero)
set Knight.tooltipAbility = 'ds15' // Knight (Hero Description)
set Knight.needsHeroGlow = true
set Knight.category = 1
set Knight.selectAnim = ANIM_TYPE_SPELL
set Knight.selectSubAnim = null
set Knight.selectAnimLength = 2.167
//========================================================================================
// Lich
set Lich.abilities[1] = 'A01L' // Frost Frenzy
set Lich.abilities[2] = 'AUfn' // Frost Nova
set Lich.abilities[3] = 'A04C' // Breath of Frost
set Lich.abilities[4] = 'ANfa' // Frost Arrows
set Lich.abilities[5] = 'AUfu' // Forst Armor
set Lich.abilities[6] = 'A035' // Summon Forst Beast
set Lich.abilities[7] = 'A006' // Frost Chaos
set Lich.isNonHeroAbility[1] = true
set Lich.isNonHeroAbility[7] = true
set Lich.selectEmote = "Units\\Undead\\HeroLich\\HeroLichWarcry1.flac"
set Lich.unitId = 'Ulic' // Lich (Hero)
set Lich.tooltipAbility = 'ds16' // Lich (Hero Description)
set Lich.needsHeroGlow = true
set Lich.category = 2
set Lich.selectAnim = ANIM_TYPE_ATTACK
set Lich.selectSubAnim = null
set Lich.selectAnimLength = 1.300
//========================================================================================
// Marine
set Marine.abilities[1] = 'ACbn' // Banish
set Marine.abilities[2] = 'A00V' // Hurl Bomb
set Marine.abilities[3] = 'A00U' // Fan of Rockets
set Marine.abilities[4] = 'A008' // Metal Armor
set Marine.abilities[5] = 'A03Y' // Life Regeneration Aura
set Marine.abilities[6] = 'A00T' // Clone
set Marine.abilities[7] = 'A02T' // Cluster Rockets
set Marine.isNonHeroAbility[1] = true
set Marine.isNonHeroAbility[7] = true
set Marine.selectEmote = "Units\\Critters\\Marine\\MarineWarcry1.flac"
set Marine.unitId = 'H002' // Marine (hero)
set Marine.tooltipAbility = 'ds17' // Marine (Hero Description)
set Marine.needsHeroGlow = true
set Marine.category = 1
set Marine.selectAnim = ANIM_TYPE_SPELL
set Marine.selectSubAnim = null
set Marine.selectAnimLength = 4.000
//========================================================================================
// Mountain_Cleric
set Mountain_Cleric.abilities[1] = 'A08Y' // Blessed Hammer
set Mountain_Cleric.abilities[2] = 'A095' // Holy Storm
set Mountain_Cleric.abilities[3] = 'A093' // Holy Wave
set Mountain_Cleric.abilities[4] = 'A091' // Healing Drum
set Mountain_Cleric.abilities[5] = 'A094' // Mountain God's Protection
set Mountain_Cleric.abilities[6] = 'A096' // Gift from the Mountain God
set Mountain_Cleric.abilities[7] = 'A097' // Heart of Stone
set Mountain_Cleric.isNonHeroAbility[1] = true
set Mountain_Cleric.isNonHeroAbility[7] = true
set Mountain_Cleric.selectEmote = "Units\\Critters\\MountainKing\\MountainKingWarcry1.flac"
set Mountain_Cleric.unitId = 'H00F' // Mountain Cleric (hero)
set Mountain_Cleric.tooltipAbility = 'ds35' // Mountain Cleric (Hero Description)
set Mountain_Cleric.needsHeroGlow = true
set Mountain_Cleric.category = 5
set Mountain_Cleric.selectAnim = ANIM_TYPE_ATTACK
set Mountain_Cleric.selectSubAnim = null
set Mountain_Cleric.selectAnimLength = 1.000
//========================================================================================
// Mountain_King
set Mountain_King.abilities[1] = 'A07B' // Artefact Collector
set Mountain_King.abilities[2] = 'AHtb' // Storm Bolt
set Mountain_King.abilities[3] = 'AHtc' // Thunder Clap
set Mountain_King.abilities[4] = 'AHbh' // Bash
set Mountain_King.abilities[5] = 'A077' // Muradin's hammer
set Mountain_King.abilities[6] = 'AHav' // Avatar
set Mountain_King.abilities[7] = 'A017' // Lightning Shield
set Mountain_King.isNonHeroAbility[1] = true
set Mountain_King.isNonHeroAbility[7] = true
set Mountain_King.selectEmote = "Units\\Critters\\MountainKing\\MountainKing.flac"
set Mountain_King.unitId = 'Hmkg' // Mountain King (hero)
set Mountain_King.tooltipAbility = 'ds18' // Mountain King (Hero Description)
set Mountain_King.needsHeroGlow = true
set Mountain_King.category = 1
set Mountain_King.selectAnim = ANIM_TYPE_ATTACK
set Mountain_King.selectSubAnim = null
set Mountain_King.selectAnimLength = 1.000
//========================================================================================
// Myrmidon
set Myrmidon.abilities[1] = 'ACce' // Cleaving Attack
set Myrmidon.abilities[2] = 'A06V' // Summon Venus
set Myrmidon.abilities[3] = 'A070' // Shock
set Myrmidon.abilities[4] = 'AHbh' // Bash
set Myrmidon.abilities[5] = 'A04X' // Realm of Souls
set Myrmidon.abilities[6] = 'A071' // Avatar
set Myrmidon.abilities[7] = 'A072' // Naga Wave
set Myrmidon.isNonHeroAbility[1] = true
set Myrmidon.isNonHeroAbility[7] = true
set Myrmidon.selectEmote = "Units\\Naga\\NagaMyrmidon\\NagaMyrmidonWarcry1.flac"
set Myrmidon.unitId = 'N00Z' // Myrmidon (hero)
set Myrmidon.tooltipAbility = 'ds19' // Myrmidon (Hero Description)
set Myrmidon.needsHeroGlow = true
set Myrmidon.category = 1
set Myrmidon.selectAnim = ANIM_TYPE_ATTACK
set Myrmidon.selectSubAnim = null
set Myrmidon.selectAnimLength = 1.000
//========================================================================================
// Paladin
set Paladin.abilities[1] = 'ANta' // Knight's Challenge
set Paladin.abilities[2] = 'A00M' // Healing Wave // Upgrade 'A061' : AOhw
set Paladin.abilities[3] = 'A00A' // Fan of Light
set Paladin.abilities[4] = 'A06T' // True Tank
set Paladin.abilities[5] = 'AHad' // Devotion Aura
set Paladin.abilities[6] = 'A00Y' // Pure Light
set Paladin.abilities[7] = 'A010' // Light Frenzy
set Paladin.isNonHeroAbility[1] = true
set Paladin.isNonHeroAbility[7] = true
set Paladin.selectEmote = "Units\\Human\\HeroPaladin\\HeroPaladinWarcry1.flac"
set Paladin.unitId = 'Hpal' // Paladin (hero)
set Paladin.tooltipAbility = 'ds20' // Paladin (Hero Description)
set Paladin.needsHeroGlow = false
set Paladin.category = 4
set Paladin.selectAnim = ANIM_TYPE_SPELL
set Paladin.selectSubAnim = null
set Paladin.selectAnimLength = 2.166
//========================================================================================
// PandarenBrewmaster
set PandarenBrewmaster.abilities[1] = 'ACen' // Ensnare
set PandarenBrewmaster.abilities[2] = 'Acdh' // Drunken Haze
set PandarenBrewmaster.abilities[3] = 'ANbf' // Breath Of Fire
set PandarenBrewmaster.abilities[4] = 'ANdb' // Drunken Brawler
set PandarenBrewmaster.abilities[5] = 'A03J' // Permanent Immolation
set PandarenBrewmaster.abilities[6] = 'AOeq' // Earthquake
set PandarenBrewmaster.abilities[7] = 'ANto' // Tornado
set PandarenBrewmaster.isNonHeroAbility[1] = true
set PandarenBrewmaster.isNonHeroAbility[7] = true
set PandarenBrewmaster.selectEmote = "Units\\Creeps\\PandarenBrewmaster\\PandarenBrewmasterWarcry1.flac"
set PandarenBrewmaster.unitId = 'Npbm' // PandarenBrewmaster (hero)
set PandarenBrewmaster.tooltipAbility = 'ds33' // PandarenBrewmaster (Hero Description)
set PandarenBrewmaster.needsHeroGlow = false
set PandarenBrewmaster.category = 2
set PandarenBrewmaster.selectAnim = ANIM_TYPE_SPELL
set PandarenBrewmaster.selectSubAnim = null
set PandarenBrewmaster.selectAnimLength = 0.967
//========================================================================================
// Pit Lord
set Pit_Lord.abilities[1] = 'A02E' // Howl of Terror
set Pit_Lord.abilities[2] = 'ANrf' // Rain of Fire
set Pit_Lord.abilities[3] = 'AOw2' // War Stomp
set Pit_Lord.abilities[4] = 'ANca' // Cleaving Attack
set Pit_Lord.abilities[5] = 'AUau' // Unholy Aura
set Pit_Lord.abilities[6] = 'A06S' // Doom
set Pit_Lord.abilities[7] = 'A019' // Finger of Death
set Pit_Lord.isNonHeroAbility[1] = true
set Pit_Lord.isNonHeroAbility[7] = true
set Pit_Lord.selectEmote = "Units\\Demon\\HeroPitLord\\HeroPitLordWarcry1.flac"
set Pit_Lord.unitId = 'Nplh' // Pit Lord (hero)
set Pit_Lord.tooltipAbility = 'ds21' // Pit Lord (Hero Description)
set Pit_Lord.needsHeroGlow = false
set Pit_Lord.category = 4
set Pit_Lord.selectAnim = ANIM_TYPE_SPELL
set Pit_Lord.selectSubAnim = null
set Pit_Lord.selectAnimLength = 1.566
//========================================================================================
// Moon Priestess
set MoonPriestess.abilities[1] = 'ACrj' // Rejuvenation
set MoonPriestess.abilities[2] = 'AOcl' // Chain Lightning
set MoonPriestess.abilities[3] = 'ANwm' // Watery Minions
set MoonPriestess.abilities[4] = 'ANfl' // Forked Lightning
set MoonPriestess.abilities[5] = 'AEar' // Trueshot Aura
set MoonPriestess.abilities[6] = 'A01M' // Monsoon
set MoonPriestess.abilities[7] = 'A02U' // Lightning Chaos
set MoonPriestess.isNonHeroAbility[1] = true
set MoonPriestess.isNonHeroAbility[7] = true
set MoonPriestess.selectEmote = "Units\\NighElf\\HeroMoonPriestess\\HeroMoonPriestessWarcry1.flac"
set MoonPriestess.unitId = 'Emoo' // Moon Priestess (hero)
set MoonPriestess.tooltipAbility = 'ds22' // Moon Priestess (Hero Description)
set MoonPriestess.needsHeroGlow = false
set MoonPriestess.category = 2
set MoonPriestess.selectAnim = ANIM_TYPE_SPELL
set MoonPriestess.selectSubAnim = null
set MoonPriestess.selectAnimLength = 1.367
//========================================================================================
// Ranger
set Ranger.abilities[1] = 'Anhe' // Heal
set Ranger.abilities[2] = 'A02N' // Inner Fire
set Ranger.abilities[3] = 'AHfa' // Searing Arrow
set Ranger.abilities[4] = 'A000' // Critical Arrows
set Ranger.abilities[5] = 'AOae' // Endurance Aura
set Ranger.abilities[6] = 'A015' // Exploding Arrows
set Ranger.abilities[7] = 'A013' // Rocket Hail
set Ranger.isNonHeroAbility[1] = true
set Ranger.isNonHeroAbility[7] = true
set Ranger.selectEmote = "Units\\Creeps\\SylvanusWindrunner\\SylvanusWindrunnerWarcry1.flac"
set Ranger.unitId = 'Hvwd' // Ranger (hero)
set Ranger.tooltipAbility = 'ds23' // Ranger (Hero Description)
set Ranger.needsHeroGlow = false
set Ranger.category = 1
set Ranger.selectAnim = ANIM_TYPE_SPELL
set Ranger.selectSubAnim = null
set Ranger.selectAnimLength = 1.334
//========================================================================================
// Rifleman
set Rifleman.abilities[1] = 'ACbl' // Bloodlust
set Rifleman.abilities[2] = 'A00V' // Hurl Bomb
set Rifleman.abilities[3] = 'A036' // Plasma Rifle
set Rifleman.abilities[4] = 'A03D' // Shield
set Rifleman.abilities[5] = 'AEar' // Trueshot Aura
set Rifleman.abilities[6] = 'A038' // Laser
set Rifleman.abilities[7] = 'A039' // Weapons Upgrade
set Rifleman.isNonHeroAbility[1] = true
set Rifleman.isNonHeroAbility[7] = true
set Rifleman.selectEmote = "Units\\Human\\Rifleman\\RiflemanWarcry1.flac"
set Rifleman.unitId = 'H008' // Rifleman (hero)
set Rifleman.tooltipAbility = 'ds24' // Rifleman (Hero Description)
set Rifleman.needsHeroGlow = false
set Rifleman.category = 1
set Rifleman.selectAnim = ANIM_TYPE_SPELL
set Rifleman.selectSubAnim = null
set Rifleman.selectAnimLength = 1.266
//========================================================================================
// Runemaster
set Runemaster.abilities[1] = 'A05Z' // Runic Weapon
set Runemaster.abilities[2] = 'A064' // Exorcism
set Runemaster.abilities[3] = 'A05O' // Rune Crystal
set Runemaster.abilities[4] = 'A062' // Magic Defense
set Runemaster.abilities[5] = 'A05L' // Critical Strike Aura
set Runemaster.abilities[6] = 'A061' // Spiritual Presence
set Runemaster.abilities[7] = 'A060' // Rune Clock
set Runemaster.isNonHeroAbility[1] = true
set Runemaster.isNonHeroAbility[7] = true
set Runemaster.selectEmote = "Units\\Human\\HeroPaladin\\HeroPaladinWarcry1.flac"
set Runemaster.unitId = 'H00C' // Runemaster (hero)
set Runemaster.tooltipAbility = 'ds25' // Runemaster (Hero Description)
set Runemaster.needsHeroGlow = false
set Runemaster.category = 4
set Runemaster.selectAnim = ANIM_TYPE_SPELL
set Runemaster.selectSubAnim = null
set Runemaster.selectAnimLength = 1.167
//========================================================================================
// Shadow_Hunter
set Shadow_Hunter.abilities[1] = 'Ahwd' // Healing Ward
set Shadow_Hunter.abilities[2] = 'A01U' // Bomb Trap
set Shadow_Hunter.abilities[3] = 'Arsw' // Serpent Ward
set Shadow_Hunter.abilities[4] = 'Adtg' // Wards Mastery
set Shadow_Hunter.abilities[5] = 'A05L' // Critical Strike Aura
set Shadow_Hunter.abilities[6] = 'A01X' // Laser Trap
set Shadow_Hunter.abilities[7] = 'A03F' // Hex
set Shadow_Hunter.isNonHeroAbility[1] = true
set Shadow_Hunter.isNonHeroAbility[7] = true
set Shadow_Hunter.selectEmote = "Units\\Orc\\HeroShadowHunter\\HeroShadowHunterWarcry1.flac"
set Shadow_Hunter.unitId = 'Oshd' // Shadow Hunter (hero)
set Shadow_Hunter.tooltipAbility = 'ds26' // Shadow Hunter (Hero Description)
set Shadow_Hunter.needsHeroGlow = false
set Shadow_Hunter.category = 3
set Shadow_Hunter.selectAnim = ANIM_TYPE_SPELL
set Shadow_Hunter.selectSubAnim = null
set Shadow_Hunter.selectAnimLength = 1.667
//========================================================================================
// Sorceress
set Sorceress.abilities[1] = 'ACsk' // Resistant Skin
set Sorceress.abilities[2] = 'ANmo' // Monsoon
set Sorceress.abilities[3] = 'A03K' // Phase Shift
set Sorceress.abilities[4] = 'A03I' // Permanent Lightning
set Sorceress.abilities[5] = 'A03Y' // Life Regeneration Aura
set Sorceress.abilities[6] = 'A03H' // Lightning Shock
set Sorceress.abilities[7] = 'A071' // Lightning Attacks
set Sorceress.isNonHeroAbility[1] = true
set Sorceress.isNonHeroAbility[7] = true
set Sorceress.selectEmote = "Units\\Human\\Sorceress\\SorceressWarcry1.flac"
set Sorceress.unitId = 'H009' // Sorceress (hero)
set Sorceress.tooltipAbility = 'ds27' // Sorceress (Hero Description)
set Sorceress.needsHeroGlow = false
set Sorceress.category = 2
set Sorceress.selectAnim = ANIM_TYPE_SPELL
set Sorceress.selectSubAnim = null
set Sorceress.selectAnimLength = 1.534
//========================================================================================
// Stone_Giant
set Stone_Giant.abilities[1] = 'A06C' // War Drums Aura
set Stone_Giant.abilities[2] = 'A06E' // Avalanche
set Stone_Giant.abilities[3] = 'A06G' // Toss
set Stone_Giant.abilities[4] = 'A05M' // Tiny tiny !
set Stone_Giant.abilities[5] = 'A066' // Craggy Exterior
set Stone_Giant.abilities[6] = 'A068' // Grow
set Stone_Giant.abilities[7] = 'A06L' // War Club
set Stone_Giant.isNonHeroAbility[1] = true
set Stone_Giant.isNonHeroAbility[7] = true
set Stone_Giant.selectEmote = "Units\\NightElf\\MountainGiant\\MountainGiantWarcry1.flac"
set Stone_Giant.unitId = 'U00B' // Stone Giant (hero)
set Stone_Giant.tooltipAbility = 'ds28' // Stone Giant (Hero Description)
set Stone_Giant.needsHeroGlow = false
set Stone_Giant.category = 4
set Stone_Giant.selectAnim = ANIM_TYPE_SPELL
set Stone_Giant.selectSubAnim = null
set Stone_Giant.selectAnimLength = 1.666
//========================================================================================
// Storm_Brewmaster
set Storm_Brewmaster.abilities[1] = 'ACsk' // Resistant Skin
set Storm_Brewmaster.abilities[2] = 'ANmo' // Monsoon
set Storm_Brewmaster.abilities[3] = 'A03K' // Phase Shift
set Storm_Brewmaster.abilities[4] = 'A03I' // Permanent Lightning
set Storm_Brewmaster.abilities[5] = 'A03Y' // Life Regeneration Aura
set Storm_Brewmaster.abilities[6] = 'A03H' // Lightning Shock
set Storm_Brewmaster.abilities[7] = 'ANto' // Tornado
set Storm_Brewmaster.isNonHeroAbility[1] = true
set Storm_Brewmaster.isNonHeroAbility[7] = true
set Storm_Brewmaster.selectEmote = "Units\\Creeps\\StormPandarenBrewmaster\\StormPandarenBrewmasterWarcry1.flac"
set Storm_Brewmaster.unitId = 'O007' // Storm_Brewmaster (hero)
set Storm_Brewmaster.tooltipAbility = 'ds29' // Storm_Brewmaster (Hero Description)
set Storm_Brewmaster.needsHeroGlow = false
set Storm_Brewmaster.category = 2
set Storm_Brewmaster.selectAnim = ANIM_TYPE_SPELL
set Storm_Brewmaster.selectSubAnim = null
set Storm_Brewmaster.selectAnimLength = 12.334
//========================================================================================
// Sun Adept
set SunAdept.abilities[1] = 'A08X' // Healing hand
set SunAdept.abilities[2] = 'A08O' // Summon Faerie
set SunAdept.abilities[3] = 'A09E' // Bouncing Light
set SunAdept.abilities[4] = 'A08R' // Purification
set SunAdept.abilities[5] = 'A08S' // Guardian Angels
set SunAdept.abilities[6] = 'A08P' // Hyper Nova
set SunAdept.abilities[7] = 'A08L' // Mass Magic
set SunAdept.isNonHeroAbility[1] = true
set SunAdept.isNonHeroAbility[7] = true
set SunAdept.selectEmote = "Units\\Human\\Sorceress\\SorceressWarcry1.flac"
set SunAdept.unitId = 'H00G' // Sun Adept (hero)
set SunAdept.tooltipAbility = 'ds34' // Sun Adept (Hero Description)
set SunAdept.needsHeroGlow = false
set SunAdept.category = 5
set SunAdept.selectAnim = ANIM_TYPE_SPELL
set SunAdept.selectSubAnim = null
set SunAdept.selectAnimLength = 1.500
//========================================================================================
// Tauren_Chieftain
set Tauren_Chieftain.abilities[1] = 'ACro' // Battle Roar
set Tauren_Chieftain.abilities[2] = 'AOsh' // Shockwave
set Tauren_Chieftain.abilities[3] = 'AOws' // War Stomp
set Tauren_Chieftain.abilities[4] = 'A055' // Pulverize
set Tauren_Chieftain.abilities[5] = 'AOae' // Endurance Aura
set Tauren_Chieftain.abilities[6] = 'A00E' // Lightning Stroke
set Tauren_Chieftain.abilities[7] = 'A07N' // BeastAttack
set Tauren_Chieftain.isNonHeroAbility[1] = true
set Tauren_Chieftain.isNonHeroAbility[7] = true
set Tauren_Chieftain.selectEmote = "Units\\Orc\\HeroTaurenChieftain\\HeroTaurenChieftainWarcry1.flac"
set Tauren_Chieftain.unitId = 'Otch' // Tauren Chieftain (Hero)
set Tauren_Chieftain.tooltipAbility = 'ds30' // Tauren Chieftain (Hero Description)
set Tauren_Chieftain.needsHeroGlow = true
set Tauren_Chieftain.category = 2
set Tauren_Chieftain.selectAnim = ANIM_TYPE_SPELL
set Tauren_Chieftain.selectSubAnim = null
set Tauren_Chieftain.selectAnimLength = 1.167
//========================================================================================
// Warden
set Warden.abilities[1] = 'A00D' // Blink
set Warden.abilities[2] = 'AEfk' // Fan of Knives
set Warden.abilities[3] = 'AEsh' // Shadow Strike
set Warden.abilities[4] = 'A07L' // Scream
set Warden.abilities[5] = 'A00N' // Attribute Bonus
set Warden.abilities[6] = 'AEsv' // Vengeance
set Warden.abilities[7] = 'A023' // Polymorph & upgrades
set Warden.isNonHeroAbility[1] = true
set Warden.isNonHeroAbility[7] = true
set Warden.selectEmote = "Units\\NightElf\\HeroWarden\\HeroWardenWarcry1.flac"
set Warden.unitId = 'Ewar' // Tauren Chieftain (Hero)
set Warden.tooltipAbility = 'ds31' // Tauren Chieftain (Hero Description)
set Warden.needsHeroGlow = true
set Warden.category = 1
set Warden.selectAnim = ANIM_TYPE_SPELL
set Warden.selectSubAnim = null
set Warden.selectAnimLength = 1.200
//========================================================================================
// Warlock
set Warlock.abilities[1] = 'Scri' // Cripple
set Warlock.abilities[2] = 'ACs8' // Spirit Beast
set Warlock.abilities[3] = 'A00J' // Dark Portal
set Warlock.abilities[4] = 'A03L' // Warlock Archimonde
set Warlock.abilities[5] = 'Acdb' // Evasion & Critical Strike
set Warlock.abilities[6] = 'A03G' // Gargoyle Swarm
set Warlock.abilities[7] = 'A03O' // Infernal Fall
set Warlock.isNonHeroAbility[1] = true
set Warlock.isNonHeroAbility[7] = true
set Warlock.selectEmote = "Units\\Demon\\Kiljaeden\\KiljaedenWarcry1.flac"
set Warlock.unitId = 'Nklj' // Tauren Chieftain (Hero)
set Warlock.tooltipAbility = 'ds32' // Tauren Chieftain (Hero Description)
set Warlock.needsHeroGlow = true
set Warlock.category = 3
set Warlock.selectAnim = ANIM_TYPE_SPELL
set Warlock.selectSubAnim = null
set Warlock.selectAnimLength = 3.133
endfunction
endlibrary
library HeroSelectionCallbacks requires HeroSelection
globals
//=============================================================================================================================
//Globals for interfacing with rest of map. All player indices start at 0.
//=============================================================================================================================
integer array heroIdOfPlayer //Stores the unit id of the hero each player picked.
string array heroIconPathOfPlayer //Stores the icon path of the hero each player picked.
// Added
boolean array isRandomHero //Stores if the hero was randomly picked
//=============================================================================================================================
//Can also be used in custom code. Read-Only!
boolean array playerHasHero //Player has picked a hero (does not mean hero was created).
boolean array isInHeroSelection //Player is currently viewing the hero selection screen.
//Multiple heroes selection behavior
integer NUMBER_OF_HEROES_PER_PLAYER = 2 //Number of heroes a player must pick -- Added for dual mode
integer array playerHeroesNb //Number of picked heroes by player
boolean HERO_CAN_BE_PICKED_MULTIPLE_TIMES = false // Moved from config as its selected in game
boolean inBanPhase = false
boolean array playerIsHuman
string array coloredPlayerName
boolean array heroSelectionDisabledForPlayer
boolean array heroPreselectionDisabledForPlayer
boolean array playerHasBan
boolean array isRepicking
integer numPlayersWithHero = 0
integer numPlayersInSelection = 0
integer numSelectingPlayers = 0
integer numSelectingHumans = 0
integer array playerNumberOfSlot //To get the player index when iterating over all selecting players from 1 to numSelectingPlayers.
effect array selectedHero
effect array backgroundHeroHighlight
effect array backgroundHero
real array BACKGROUND_HERO_X
real array BACKGROUND_HERO_Y
integer NUMBER_OF_HEROES = 0
endglobals
//=================================================================================================================================
//These functions are the main interface with the rest of your map.
//=================================================================================================================================
function HeroSelectionOnEscape takes player whichPlayer returns nothing
//Here you can insert code that is executed when a player is kicked out of hero selection. This should include creating
//the hero that player selected as well as setting that player's camera location. Will be executed before OnFinal.
//Example code:
local integer i
local real x
local real y
local boolean fullRandom = true
local integer gold
set i = 0
loop
exitwhen i > NUMBER_OF_HEROES_PER_PLAYER-1
set x = HERO_SPAWN_X
set y = HERO_SPAWN_Y
if i > 0 then
set x = SECONDARY_HERO_SPAWN_X
set y = SECONDARY_HERO_SPAWN_Y
endif
set udg_LightHeroes[GetConvertedPlayerId(whichPlayer) + i*MAX_NUMBER_OF_PLAYERS] = CreateUnit(whichPlayer, heroIdOfPlayer[GetPlayerId(whichPlayer) + i*MAX_NUMBER_OF_PLAYERS], x, y, 90)
call UnitAddItemById(udg_LightHeroes[GetConvertedPlayerId(whichPlayer) + i*MAX_NUMBER_OF_PLAYERS], 'stwp')
call UnitAddItemById(udg_LightHeroes[GetConvertedPlayerId(whichPlayer) + i*MAX_NUMBER_OF_PLAYERS], 'ankh')
call UnitAddItemById(udg_LightHeroes[GetConvertedPlayerId(whichPlayer) + i*MAX_NUMBER_OF_PLAYERS], 'pghe')
set fullRandom = fullRandom and isRandomHero[GetPlayerId(whichPlayer) + i*MAX_NUMBER_OF_PLAYERS]
set i = i +1
endloop
if udg_ModeDual then
set udg_DualHeroChange[GetConvertedPlayerId(whichPlayer)] = true
endif
// Starting ressources
if udg_ModeEasy then
if fullRandom then
set gold = PLAYER_GOLD_EASY_RANDOM
else
set gold = PLAYER_GOLD_EASY_NORMAL
endif
else
if fullRandom then
set gold = PLAYER_GOLD_RANDOM
else
set gold = PLAYER_GOLD_NORMAL
endif
endif
call SetPlayerStateBJ(whichPlayer, PLAYER_STATE_RESOURCE_GOLD, gold)
call SetPlayerStateBJ(whichPlayer, PLAYER_STATE_RESOURCE_LUMBER, 0)
// Starting camera
if GetLocalPlayer() == whichPlayer then
call SetCameraPosition(HERO_SPAWN_X, HERO_SPAWN_Y)
endif
// Clean up
static if LIBRARY_NeatMessages then
call ClearNeatMessages()
else
call ClearTextMessages()
endif
endfunction
function HeroSelectionOnFinal takes nothing returns nothing
//Here you can insert the code that is executed when hero selection is concluded. This will most likely involve calling the main
//function that progresses your map after hero selection ends. It is recommended to execute player-specific actions in OnEscape
//instead.
//Example code:
// static if LIBRARY_TestHeroSelection then
// call ExecuteFunc("BeginGame")
// endif
call TriggerExecute(gg_trg_StartGameSelectionEnd)
endfunction
//=================================================================================================================================
//These functions allow you to add additional visual effects, sounds, texts etc. to the hero selection that can't be achieved with
//the default options.
//=================================================================================================================================
function HeroSelectionOnRestart takes nothing returns nothing
//Here you can insert additional code that is executed when hero selection is restarted.
//Example code:
static if LIBRARY_TestHeroSelection then
local integer i = 1
local integer P
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
call DestroyEffect(circles[P])
set i = i + 1
endloop
endif
endfunction
function HeroSelectionOnLast takes nothing returns nothing
//Here you can insert additional code that is executed when the last player picks a hero.
//Example code:
static if LIBRARY_TestHeroSelection then
call ExecuteFunc("MusicSlowFadeOut")
endif
endfunction
function HeroSelectionOnPick takes player whichPlayer, integer whichHero returns nothing
//Here you can insert additional code that is executed when a player picks a hero.
endfunction
function HeroSelectionOnPreselect takes player whichPlayer, Hero oldHero, Hero newHero returns nothing
//Here you can insert additional code that is executed when a player switches to a new hero during pre-selection.
endfunction
function HeroSelectionOnReturn takes player whichPlayer returns nothing
//Here you can insert additional code that is executed when a player returns to hero selection.
if udg_ModeRandom then
call HeroSelectionAllRandom()
endif
endfunction
function HeroSelectionOnAllRandom takes nothing returns nothing
//Here you can insert additional code that is executed when all random mode is selected.
endfunction
function HeroSelectionOnBegin takes nothing returns nothing
//Here you can insert additional code that is executed when players are locked into hero selection.
//Example code:
static if LIBRARY_NeatMessages and LIBRARY_TestHeroSelection then
call NeatMessageTimedInWindow(5, "Heroes from all across Azeroth have gathered to fight the darkness. Only you can save this land...", centerWindow)
endif
endfunction
function HeroSelectionOnEnable takes nothing returns nothing
//Here you can insert additional code that is executed when hero selection is enabled.
//Example code:
local integer i = 1
local integer P
local location tempLoc
static if LIBRARY_TestHeroSelection then
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set circles[P] = AddSpecialEffect("buildings\\other\\CircleOfPower\\CircleOfPower.mdl", BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P])
call BlzSetSpecialEffectColorByPlayer(circles[P], Player(PLAYER_NEUTRAL_AGGRESSIVE))
set tempLoc = Location(BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P])
call BlzSetSpecialEffectZ(circles[P], GetLocationZ(tempLoc) + 10)
call RemoveLocation(tempLoc)
set i = i + 1
endloop
endif
call ClearMapMusic()
call PlayMusic("Nightsong.mp3")
endfunction
function HeroSelectionOnExpire takes nothing returns nothing
//Here you can insert the code that is executed when the timer for the hero selection duration expires.
//Example code:
static if LIBRARY_NeatMessages and LIBRARY_TestHeroSelection then
call NeatMessageTimedInWindow(4, "The time has expired.", centerWindow)
endif
endfunction
function HeroSelectionOnLeave takes player whichPlayer returns nothing
//Here you can insert additional code that is executed when a player who is in hero selection leaves the game.
endfunction
endlibrary
library HeroSelectionConfig requires optional NeatMessages
//=================================================================================================================================
//These constants determine important aspects of the hero selection. You can fine-tune visuals, sounds etc. further down below.
//=================================================================================================================================
globals
constant boolean HERO_SELECTION_ENABLE_DEBUG_MODE = false //Printout errors and check for function crashes.
//Overall behavior.
// MOVE TO CALLBACK (mode selection in game// constant boolean HERO_CAN_BE_PICKED_MULTIPLE_TIMES = false //Set to true if the same hero should be able to be picked multiple times.
constant boolean AUTO_SET_SELECTING_PLAYERS = false //Set to true if hero selection should be enabled for all human players. Set to false if you want to set the player list manually (which can include computer players).
constant boolean COMPUTER_AUTO_PICK_RANDOM_HERO = false //Set to true if computer players that are in hero selection should automatically pick a random hero after all human players have picked.
constant boolean ESCAPE_PLAYER_AFTER_SELECTING = true //Set to true if a player picking a hero should kick him or her out of the hero selection menu and camera.
constant boolean CONCEAL_HERO_PICKS_FROM_ENEMIES = false //Set to true if hero picks should be concealed from enemies (players in another team or all players if there are no teams), including emote, effect, and text messages.
constant boolean MENU_INCLUDE_RANDOM_PICK = true //Include button for picking a random hero at the bottom of the menu.
constant boolean MENU_INCLUDE_SUGGEST_RANDOM = true //Include button to pre-select a random hero next to the random pick button.
constant real TIME_LIMIT = 60 //Set a time limit after which players who haven't chosen a hero will be assigned one at random. Set to 0 for no time limit.
//Multiple heroes behavior
constant integer MAX_NUMBER_OF_PLAYERS = 8 //How many players can play the map. Used to access secondaries heroes in array.
//Camera and foreground hero positions (background hero locations are set in GetPlayerBackgroundHeroLocation).
constant boolean SEPARATE_LOCATIONS_FOR_EACH_TEAM = false //Set to true if each team gets a different selection screen (all following values will be ignored and you have to set the array equivalents in InitArrays).
constant real FOREGROUND_HERO_X = -4050 //-128 //x-Position of foreground hero.
constant real FOREGROUND_HERO_Y = -6600 //256 //y-Position of foreground hero.
constant real HERO_SELECTION_ANGLE = 90 //Hero selection viewing direction of camera (0 = facing east, 90 = facing north etc.).
//Visuals.
constant boolean ENFORCE_CAMERA = true //Set to false if players' camera should not be changed during hero selection.
constant boolean HIDE_GAME_UI = true //Set to true if all UI elements other than the hero selection menu should be hidden.
constant boolean CREATE_FOREGROUND_HERO = true //Set to false if no foreground hero should appear on preselection.
constant boolean CREATE_BACKGROUND_HEROES = true //Set to true if players should be able to see other players' picks in the background.
endglobals
//=================================================================================================================================
globals
string array CATEGORY_NAMES //Names of the hero categories that appear in the hero selection menu.
integer array PAGE_OF_CATEGORY //Set the page at which that category appears within the menu. When there is more than one page, page cycle buttons appear in the menu.
boolean array PLAYER_SELECTS_HERO //List of all players that are supposed to participate in hero selection (true = participates).
integer array TEAM_OF_PLAYER //Team of player, 1, 2 etc. 0 = no teams.
string array PLAYER_COLOR //Hex color string (including "|cff") for each player.
endglobals
function SetCategories takes nothing returns nothing
//Set the names of the categories in the hero selection menu. Starts at index 1. Set to null for no category caption.
set CATEGORY_NAMES[1] = "|cffffcc00Fighter|r"
set CATEGORY_NAMES[2] = "|cffffcc00Caster|r"
set CATEGORY_NAMES[3] = "|cffffcc00Summoner|r"
set CATEGORY_NAMES[4] = "|cffffcc00Tank|r"
set CATEGORY_NAMES[5] = "|cffffcc00Healer|r"
//Set the pages of the categories in the hero selection menu. Pages start at 1.
set PAGE_OF_CATEGORY[1] = 1
set PAGE_OF_CATEGORY[2] = 2
set PAGE_OF_CATEGORY[3] = 3
set PAGE_OF_CATEGORY[4] = 3
set PAGE_OF_CATEGORY[5] = 3
endfunction
function HeroSelectionInitPlayers takes nothing returns nothing
//Set the list of players for which hero selection is enabled (only if AUTO_SET_SELECTING_PLAYERS = false). It is disabled here because computer players must be added to the hero selection.
set PLAYER_SELECTS_HERO[0] = true
set PLAYER_SELECTS_HERO[1] = true
set PLAYER_SELECTS_HERO[2] = true
set PLAYER_SELECTS_HERO[3] = true
set PLAYER_SELECTS_HERO[4] = true
set PLAYER_SELECTS_HERO[5] = true
set PLAYER_SELECTS_HERO[6] = true
set PLAYER_SELECTS_HERO[7] = true
//Set the teams of players. Players with team 0 are enemies to all players.
set TEAM_OF_PLAYER[0] = 1
set TEAM_OF_PLAYER[1] = 1
set TEAM_OF_PLAYER[2] = 1
set TEAM_OF_PLAYER[3] = 1
set TEAM_OF_PLAYER[4] = 1
set TEAM_OF_PLAYER[5] = 1
set TEAM_OF_PLAYER[6] = 1
set TEAM_OF_PLAYER[7] = 1
endfunction
//=================================================================================================================================
globals
real array TEAM_FOREGROUND_HERO_X //x-Position of foreground hero.
real array TEAM_FOREGROUND_HERO_Y //y-Position of foreground hero.
real array TEAM_HERO_SELECTION_ANGLE //Hero selection camera yaw in degrees (0 = facing east, 90 = facing north etc.).
endglobals
function HeroSelectionInitArrays takes nothing returns nothing
//If you set SEPARATE_LOCATIONS_FOR_EACH_TEAM = true, initialize the above array variables here.
endfunction
function GetPlayerBackgroundHeroLocation takes integer playerIndex, integer whichSlot, integer numberOfPlayers, integer whichTeam, integer slotInTeam, integer teamSize returns location
local real deltaHorizontal
local real deltaVertical
//=================================================================================================================================
/*
CREATE_BACKGROUND_HEROES only.
Here you can customize the positions of the background heroes. Init loops through and calls this function for each player.
Input:
playerIndex
whichSlot The position of the player when enumerating all players for which hero selection is enabled.
numberOfPlayers The number of players for which hero selection is enabled.
whichTeam The team the player is assigned to. Discard if there are no teams.
slotInTeam The position of that player within his or her team. Discard if there are no teams.
teamSize The size of the player's team. Discard if there are no teams.
Output:
deltaVertical The distance between the background hero and the camera eye position along the camera viewing direction.
deltaHorizontal The offset between the background hero and the camera eye position perpendicular to the camera viewing direction.
*/
//==================================================================================================================================
//Example code:
local real angle
local real dist
local real BACKGROUND_HERO_OFFSET_BACKROW = 1600
local real BACKGROUND_HERO_OFFSET_FRONTROW = 1250
local real BACKGROUND_SECONDARY_HEROES_OFFSET = 150 // For multiple heroes selection. OffSet per secondary hero
local real BACKGROUND_SECONDARY_HEROES_ANGLE_OFFSET = 2 // For multiple heroes selection. OffSet per secondary hero
local integer pIndex
local integer mult
if teamSize == 1 then
set angle = 9
set dist = BACKGROUND_HERO_OFFSET_BACKROW
// elseif slotInTeam == 1 then
// set angle = 5
// set dist = BACKGROUND_HERO_OFFSET_BACKROW
// elseif slotInTeam == 2 then
// set angle = 13
// set dist = BACKGROUND_HERO_OFFSET_BACKROW
// else
// set angle = 9
// set dist = BACKGROUND_HERO_OFFSET_FRONTROW
// endif
else
set angle = 90 / teamSize
set dist = BACKGROUND_HERO_OFFSET_BACKROW
endif
if whichTeam == 2 then
set angle = -angle
endif
// Adding offset for secondary picks
set pIndex = ModuloInteger(playerIndex, MAX_NUMBER_OF_PLAYERS)
set mult = playerIndex / MAX_NUMBER_OF_PLAYERS -1
set dist = dist + mult * BACKGROUND_SECONDARY_HEROES_OFFSET
set angle = angle + mult * BACKGROUND_SECONDARY_HEROES_ANGLE_OFFSET
set deltaHorizontal = Sin(Deg2Rad(angle))*dist
set deltaVertical = Cos(Deg2Rad(angle))*dist
//==================================================================================================================================
return Location(deltaHorizontal, deltaVertical)
endfunction
//==================================================================================================================================
// F I N E - T U N I N G
//==================================================================================================================================
globals
//How hero selection looks like before it is enabled (you can ignore this if you plan on enabling hero selection right away).
constant boolean SHOW_MENU_BEFORE_ENABLED = false //Set to true if players should be able to see the hero selection menu before hero selection is enabled.
constant boolean PRE_SELECT_BEFORE_ENABLED = false //Set to true if players should be able to pre-select heroes before hero selection is enabled.
//Camera setup (ENFORCE_CAMERA).
constant real CAMERA_PITCH = 12 //Hero selection camera angle of attack in degrees. 0 = facing horizon, 90 = facing ground.
constant real CAMERA_DISTANCE = 1300 //Hero selection camera distance from camera target.
constant real CAMERA_TARGET_OFFSET = 500 //Distance between foreground hero and camera target. Positive = Camera target is behind the hero.
constant real CAMERA_PERPENDICULAR_OFFSET = 0 //Shifts the camera left (negative) or right (positive) with respect to the foreground hero.
constant real CAMERA_Z_OFFSET = 100 //Hero selection camera z-offset.
constant real CAMERA_FIELD_OF_VIEW = 70 //Hero selection camera field of view.
//Sounds and visuals of hero pre-selection (CREATE_FOREGROUND_HERO).
constant string PRESELECT_EFFECT = null //Special effect played on the hero's position for the triggering player when switching to a new hero during pre-selection. Set to null for no effect.
constant boolean PLAY_EMOTE_ON_PRESELECT = true //Set to true if the hero should play its selection emote when the player switches to that hero during pre-selection.
constant boolean PLAY_ANIMATION_ON_PRESELECT = true //Set to true if the hero should play the selection animation when the player switches to that hero during pre-selection.
constant boolean PHANTOM_HERO_WHEN_CANNOT_BE_PICKED = true //Set to true if a hero should be black and transparent when it is pre-selected but cannot be picked.
constant real FOREGROUND_HERO_Z = 0 //z-Position of foreground hero.
//Sounds and visuals on hero pick.
constant string PICK_EFFECT = "HolyLight.mdx" //Special effect played on the hero's position for the triggering player when picking a hero. Set to null for no effect.
constant string PICK_SOUND = "Sound\\Interface\\ItemReceived.flac" //Sound effect played for the triggering player when selecting a hero. Set to null for no sound.
constant boolean PLAY_EMOTE_ON_PICK = false //Set to true if a hero should play its selection emote when a player chooses that hero.
constant boolean PLAY_ANIMATION_ON_PICK = false //Set to true if a hero should play the selection animation when a player chooses that hero.
constant real PLAYER_PICK_ESCAPE_DELAY = 4.0 //Delay between selecting a hero and being kicked out of hero selection (ESCAPE_PLAYER_AFTER_SELECTING).
constant real FOREGROUND_HERO_FADEOUT_DELAY = 1.0 //The time it takes for the foreground hero to start fading out after being selected.
constant real FOREGROUND_HERO_FADEOUT_TIME = 1.5 //The time it takes for the foreground hero to fade out after being selected.
constant string OTHER_PLAYER_HERO_PICK_SOUND = "Sound\\Interface\\InGameChatWhat1.flac" //Sound played when another player picks a hero. Set to null for no sound.
//Text messages on hero pick.
constant boolean CREATE_TEXT_MESSAGE_ON_PICK = true //Set to true if a text message should be sent to all other players when a hero is picked (except to enemies when concealed).
constant real TEXT_MESSAGE_X_OFFSET = 0.35 //x-Offset of text messages from default. Text messages will still suck. Recommend using NeatMessages.
constant real TEXT_MESSAGE_Y_OFFSET = 0 //y-Offset of text messages from default.
constant boolean USE_HERO_PROPER_NAME = false //Instead of the hero's name, its proper name will be displayed in the text message. For example, "Uther" instead of "Paladin". Will temporarily create a hero to get the proper name.
constant boolean MESSAGE_EVEN_WHEN_CONCEALED = true //Set to true if players should still get a message notifying that a player has picked a hero even when it is concealed which hero was picked (CONCEAL_HERO_PICKS_FROM_ENEMIES).
constant boolean INCLUDE_PROGRESSION_IN_MESSAGE = false //Set to true if the displayed text message should include how many players have selected their hero.
//Sounds and visuals of background heroes (CREATE_BACKGROUND_HEROES).
constant boolean PLAYER_TEXT_TAGS = true //Create text tags that show the players' names over the background heroes.
constant real BACKGROUND_HERO_FADEIN_TIME = 1.0 //The time it takes for the background hero to fade in after being selected.
constant boolean PLAY_EMOTE_ON_BACKGROUND_HERO = true //Set to true if a hero should play its selection emote for all other players as it fades in.
constant boolean PLAY_ANIMATION_ON_BACKGROUND_HERO = true //Set to true if the background hero should play its selection animation as it fades in.
constant string BACKGROUND_HERO_FADEIN_EFFECT = "HolyLightRoyal.mdx" //Special effect played on the background hero as it fades in.
constant string BACKGROUND_HERO_SELF_HIGHLIGHT = "RadianceHoly.mdx" //Special effect added at the location of a player's own background hero to highlight it. Set to null for no highlight.
constant real BACKGROUND_HERO_HIGHLIGHT_Z = 70 //z-Position of background hero self highlight.
constant real BACKGROUND_HERO_FACING_POINT_OFFSET = -500 //Adjusts where the background heroes face. 0 = Background heroes will face the foreground hero. Negative value = Face a point closer to the camera. Positive value = Face a point further from the camera.
constant string CONCEALED_HERO_EFFECT = "Objects\\InventoryItems\\QuestionMark\\QuestionMark.mdl" //Model for the background hero seen by a player for which the hero pick was concealed.
//Last player picks a hero.
constant real LAST_PLAYER_SELECT_END_DELAY = 6.0 //The amount of time after the last player selects a hero and the hero selection ends (ignore if ESCAPE_PLAYER_AFTER_SELECTING)
constant boolean DELETE_BACKGROUND_HEROES_AFTER_END = false //Set to false if background heroes should not get removed when hero selection ends. For example, when a player repicks, they are still there.
//Layout of hero selection menu.
constant integer MENU_NUMBER_OF_COLUMNS = 4 //Number of hero buttons per row.
constant real MENU_X_LEFT = -4.0 //x-Position of left edge of hero selection menu.
constant real MENU_Y_TOP = 0.55 //y-Position of top edge of hero selection menu.
constant real MENU_BUTTON_SIZE = 0.039 //Size of individual hero buttons.
constant real MENU_LEFT_RIGHT_EDGE_GAP = 0.02 //Gap between left and right edges of menu and first and last buttons.
constant real MENU_TOP_EDGE_GAP = 0.015 //Gap between top edge of menu and first button/first category title.
constant real MENU_BOTTOM_EDGE_GAP = 0.02 //Gap between bottom edge of menu and last button.
constant real MENU_BUTTON_BUTTON_GAP = 0.005 //Gap between two individual hero buttons.
constant real MENU_CATEGORY_FONT_SIZE = 16 //Font size of category titles.
constant real MENU_CATEGORY_GAP = 0.028 //Gap between buttons of two different categories.
constant real MENU_CATEGORY_TITLE_Y = -0.001 //y-Position shift between category title and center of gap between categories.
constant real MENU_BORDER_TILE_SIZE = 0.03 //This rounds up the width and height of the menu to an integer multiple of the specified number. This is useful if you're using a tiled border texture, so that there's no discontinuity in the texture.
//Value should be equal to BackdropCornerSize in the HeroSelectionMenu.fdf. Set to 0 to disable.
constant real MENU_HEROES_RANDOM_GAP = 0.02 //Additional gap between last hero button and random pick button.
//Select button.
constant string SELECT_BUTTON_TEXT = "Accept" //The text in the select hero button at the bottom of the menu.
constant real SELECT_BUTTON_SCALE = 1.0 //The scale of the select hero button at the bottom of the menu.
constant real SELECT_BUTTON_WIDTH = 0.092 //The width of the select hero button at the bottom of the menu.
//Display of random options.
constant string RANDOM_HERO_TOOLTIP = "Choose a random hero.|nUse this on all your heroes to get more starting ressources"
constant string SUGGEST_RANDOM_TOOLTIP = "Suggest a random hero, but don't select it just yet."
constant string RANDOM_HERO_ICON = "ReplaceableTextures\\CommandButtons\\BTNQuestion Mark.blp"
constant string SUGGEST_RANDOM_ICON = "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn.blp"
constant boolean RANDOM_SELECT_CYCLE_STYLE = true //Set to true if the foreground hero should cycle randomly between different heroes while random hero is pre-selected. Set to false if a question mark should be shown.
constant real RANDOM_SELECT_CYCLE_INTERVAL = 0.2 //How fast the heroes are cycled through when random hero is pre-selected (RANDOM_SELECT_CYCLE_STYLE).
//Layout of ability preview buttons.
constant real HERO_ABILITY_PREVIEW_BUTTON_X = 0.0 //x-Position of topmost ability preview button relative to topright corner of menu.
constant real HERO_ABILITY_PREVIEW_BUTTON_Y = -0.016 //y-Position of topmost ability preview button relative to topright corner of menu.
constant real HERO_ABILITY_PREVIEW_BUTTON_SIZE = 0.025 //The size of the hero ability preview buttons that appear on the right side of the menu when pre-selecting a hero.
constant boolean ABILITY_BUTTON_HORIZONTAL_LAYOUT = false //Set to true if ability preview buttons should be arranged horizontally instead of vertically.
constant string HERO_ABILITY_LEVEL_DELIMITER = " - [" //To get the name of a hero ability without the level-text from the tooltip (such as "Stormbolt - [Level 1]" -> "Stormbolt"). This string is used to detect where the name of the ability ends. Change if ability names in tooltips are not correct. Set to null if not necessary.
//Page cycle buttons (when multiple pages are set).
constant string PAGE_CYCLE_BUTTON_STYLE = "EnvelopRandomButton" //Set the page cycle button style. Styles are found in documentation.
constant real MENU_PAGE_CYCLE_X_OFFSET = 0.0 //x-Position of the page cycle buttons relative to the auto-position. Positive = moves inward. Negative = moves outward.
constant real MENU_PAGE_CYCLE_Y_OFFSET = 0.0 //y-Position of the page cycle up button relative to the auto-position.
constant real MENU_PAGE_CYCLE_SCALE = 1.0 //Sets the size of the page cycle buttons relative to hero buttons (for icon style buttons) or the accept button (for text style buttons).
constant string MENU_PAGE_DOWN_ICON = "ReplaceableTextures\\CommandButtons\\BTNNagaBurrow.blp" //(for icon style buttons)
constant string MENU_PAGE_UP_ICON = "ReplaceableTextures\\CommandButtons\\BTNNagaUnBurrow.blp" //(for icon style buttons)
//Display of big caption.
constant string HERO_SELECTION_CAPTION = "Choose your Hero!" //The text displayed on the screen during hero selection. Set null to omit text.
constant real CAPTION_FONT_SIZE = 30 //Font size of hero selection caption.
constant real CAPTION_X = 0.4 //x-Position of caption center.
constant real CAPTION_Y = 0.41 //y-Position of caption center.
constant string CAPTION_COLOR_1 = "|cffffcc00" //Caption will cycle between color 1 and color 2. Set the same for no color cycling.
constant string CAPTION_COLOR_2 = "|cffffffff" //Caption will cycle between color 1 and color 2. Set the same for no color cycling.
constant integer CAPTION_ALPHA_1 = 255 //Caption will cycle between alpha 1 and alpha 2. Set the same for no alpha cycling.
constant integer CAPTION_ALPHA_2 = 255 //Caption will cycle between alpha 1 and alpha 2. Set the same for no alpha cycling.
constant real CAPTION_CYCLE_TIME = 4.0 //The time it takes for the caption to cycle between color 1/2 and alpha 1/2.
constant real CAPTION_FADEOUT_TIME = 2.0 //The time it takes for the caption to fade out after a player has picked a hero. Set to -1 for no fade out.
//Position of hero button and ability preview button mouse-over tooltips.
constant real TOOLTIP_LEFT_X = 0.51
constant real TOOLTIP_Y = 0.13
constant boolean TOOLTIP_LOCK_TOP = false //Set to true if TOOLTIP_Y should refer to top instead of bottom edge.
constant real TOOLTIP_WIDTH = 0.29
//Display of countdown timer.
constant string TIMER_TEXT = "Time Remaining: |cffffcc00" //Text before the time remaining display
constant string TIMER_BAN_PHASE_TEXT = "Ban Phase: |cffffcc00" //Text before the time remaining display during the ban phase.
constant real TIMER_FONT_SIZE = 11 //Font size of the time remaining display.
constant real TIMER_X = 0.4 //x-Position of center of the time remaining display.
constant real TIMER_Y = 0.59 //y-Position of the center of the time remaining display.
//Shadows of heroes.
constant boolean CREATE_SHADOWS = false //Create shadows with destructables for heroes (since they are special effects and don't have shadows). Import destructables from showcase map to enable.
constant integer SHADOW_DESTRUCTABLE_ID = 'Dsha' //Destructable id of the shadow that's created for heroes.
constant integer NO_SHADOW_DESTRUCTABLE_ID = 'Dnsh' //Dummy destructable without a shadow.
endglobals
//=================================================================================================================================
function HeroSelectionSetPlayerColors takes nothing returns nothing
//Set the colors of players shown for their names in text messages.
set PLAYER_COLOR[0] = "|cffff0402"
set PLAYER_COLOR[1] = "|cff1052ff"
set PLAYER_COLOR[2] = "|cff1BE6BA"
set PLAYER_COLOR[3] = "|cff8530b1"
set PLAYER_COLOR[4] = "|cfffffc00"
set PLAYER_COLOR[5] = "|cffff8a0d"
set PLAYER_COLOR[6] = "|cff20bf00"
set PLAYER_COLOR[7] = "|cffE35BAF"
set PLAYER_COLOR[8] = "|cff949697"
set PLAYER_COLOR[9] = "|cff7EBFF1"
set PLAYER_COLOR[10] = "|cff106247"
set PLAYER_COLOR[11] = "|cff4F2B05"
set PLAYER_COLOR[12] = "|cff9C0000"
set PLAYER_COLOR[13] = "|cff0000C2"
set PLAYER_COLOR[14] = "|cff00EBEB"
set PLAYER_COLOR[15] = "|cffBE00FF"
set PLAYER_COLOR[16] = "|cffECCC86"
set PLAYER_COLOR[17] = "|cffF7A48B"
set PLAYER_COLOR[18] = "|cffBFFF80"
set PLAYER_COLOR[19] = "|cffDBB8EC"
set PLAYER_COLOR[20] = "|cff4F4F55"
set PLAYER_COLOR[21] = "|cffECF0FF"
set PLAYER_COLOR[22] = "|cff00781E"
set PLAYER_COLOR[23] = "|cffA46F34"
endfunction
endlibrary
library HeroSelection requires HeroSelectionConfig, HeroDeclaration, optional NeatMessages
globals
private effect array selectedHeroGlow
private effect array backgroundHeroGlow
private destructable array backgroundHeroShadow
private destructable foregroundHeroShadow = null
private texttag array backgroundHeroTextTag
private integer array preselectedHeroIndex
private integer array pickedHeroIndex
private integer localPlayerId //asynchronous
private boolean array heroIndexWasPicked
private boolean array heroIndexWasBanned
private real localForegroundHeroX //asynchronous
private real localForegroundHeroY //asynchronous
private real localHeroSelectionAngle //asynchronous
private integer currentPage = 1 //asynchronous
private framehandle captionFrame = null
private framehandle timerFrame = null
private framehandle heroSelectionMenu = null
private framehandle array heroSelectionButton
private framehandle array heroSelectionButtonIcon
private framehandle heroSelectionButtonHighlight
private framehandle array heroSelectionButtonIconClicked
private framehandle array heroSelectionButtonTooltip
private framehandle array heroSelectionButtonTooltipTitle
private framehandle array heroSelectionButtonTooltipText
private framehandle array heroSelectionAbility
private framehandle array heroSelectionAbilityHover
private framehandle array heroSelectionAbilityTooltip
private framehandle array heroSelectionAbilityTooltipTitle
private framehandle array heroSelectionAbilityTooltipText
private framehandle array heroSelectionCategory
private framehandle heroAcceptButton = null
private framehandle heroBanButton = null
private camerasetup heroSelectionCamera = CreateCameraSetup()
private hashtable hash = InitHashtable()
private timer lockCameraTimer = CreateTimer()
private timer captionTimer = CreateTimer()
private timer countdownTimer = CreateTimer()
private timer countdownUpdateTimer = CreateTimer()
private trigger heroSelectionButtonTrigger
private trigger pageCycleTrigger
private framehandle fullScreenFrame
private framehandle fullScreenParent
private boolean isForcedSelect = false
private real captionAlphaMultiplier = 1
private constant real TOOLTIP_BASE_HEIGHT = 0.032
private real tooltipLeftXLocal
private constant string HEX_STRING = "0123456789abcdef"
private integer r1
private integer r2
private integer g1
private integer g2
private integer b1
private integer b2
private integer storePlayerIndex
private integer storeHeroIndex
private real GARBAGE_DUMP_X
private real GARBAGE_DUMP_Y
private integer NUMBER_OF_CATEGORIES = 0
private integer NUMBER_OF_PAGES = 1
private integer NUMBER_OF_ABILITY_FRAMES = 0
private integer NUMBER_OF_TEAMS = 0
private integer RANDOM_HERO = 0
private integer SUGGEST_RANDOM = 0
private integer PAGE_DOWN = 0
private integer PAGE_UP = 0
endglobals
private function interface playerCallback takes player whichPlayer returns nothing
private function interface playerHeroCallback takes player whichPlayer, Hero whichHero returns nothing
private function interface playerHeroHeroCallback takes player whichPlayer, Hero oldHero, Hero newHero returns nothing
private function interface noArgCallback takes nothing returns nothing
struct Hero
//Fields that must be set.
//===================================
integer array abilities[9]
boolean array isNonHeroAbility[9]
integer unitId
integer tooltipAbility
string selectEmote
animtype selectAnim
subanimtype selectSubAnim
real selectAnimLength
integer category
boolean needsHeroGlow
boolean unavailable
boolean array unavailableToTeam[5]
//===================================
readonly string modelPath
readonly integer index
readonly string iconPath
readonly real scalingValue
readonly string name
readonly integer red
readonly integer green
readonly integer blue
readonly static integer numHeroes = 0
readonly static Hero array list
static method create takes nothing returns Hero
local Hero this = Hero.allocate()
set numHeroes = numHeroes + 1
set .index = numHeroes
set list[numHeroes] = this
return this
endmethod
method GetValues takes nothing returns nothing
local unit tempUnit
local item tempItem
set tempUnit = CreateUnit( Player(PLAYER_NEUTRAL_PASSIVE) , .unitId , GARBAGE_DUMP_X , GARBAGE_DUMP_Y , 0 )
set .name = GetUnitName(tempUnit)
set .scalingValue = BlzGetUnitRealField( tempUnit , UNIT_RF_SCALING_VALUE )
set .red = BlzGetUnitIntegerField(tempUnit, UNIT_IF_TINTING_COLOR_RED)
set .green = BlzGetUnitIntegerField(tempUnit, UNIT_IF_TINTING_COLOR_GREEN)
set .blue = BlzGetUnitIntegerField(tempUnit, UNIT_IF_TINTING_COLOR_BLUE)
set .iconPath = BlzGetAbilityIcon(GetUnitTypeId(tempUnit))
call RemoveUnit(tempUnit)
set tempUnit = null
set tempItem = CreateItem('afac', GARBAGE_DUMP_X, GARBAGE_DUMP_Y)
call BlzSetItemSkin(tempItem, .unitId)
set .modelPath = BlzGetItemStringField(tempItem, ITEM_SF_MODEL_USED)
call RemoveItem(tempItem)
set tempItem = null
endmethod
endstruct
//==========================================================================================================================================================
//Utility
//==========================================================================================================================================================
private function InitFullScreenParents takes nothing returns nothing
local framehandle board
call CreateLeaderboardBJ(bj_FORCE_ALL_PLAYERS, "title")
set board = BlzGetFrameByName("Leaderboard", 0)
call BlzFrameSetSize(board, 0, 0)
call BlzFrameSetVisible(BlzGetFrameByName("LeaderboardBackdrop", 0), false)
call BlzFrameSetVisible(BlzGetFrameByName("LeaderboardTitle", 0), false)
set fullScreenParent = BlzCreateFrameByType("FRAME", "fullScreenParent", board, "", 0)
set fullScreenFrame = BlzCreateFrameByType("FRAME", "fullscreen", fullScreenParent, "", 0)
call BlzFrameSetVisible(fullScreenFrame, false)
call BlzFrameSetSize(fullScreenFrame, BlzGetLocalClientWidth()/I2R(BlzGetLocalClientHeight())*0.6, 0.6)
call BlzFrameSetAbsPoint(fullScreenFrame, FRAMEPOINT_BOTTOM, 0.4, 0)
endfunction
private function CheckForCrash takes nothing returns nothing
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
local timer crashTimer = GetExpiredTimer()
local boolean didCrash = LoadBoolean(hash, GetHandleId(crashTimer), 0)
local string functionName
if didCrash then
set functionName = LoadStr(hash, GetHandleId(crashTimer), 1)
call BJDebugMsg("|cffff0000Warning:|r " + functionName + " function crashed...")
endif
call FlushChildHashtable(hash, GetHandleId(crashTimer))
call DestroyTimer(crashTimer)
set crashTimer = null
endif
endfunction
private function InitCrashCheck takes string functionName returns nothing
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
local timer crashTimer = CreateTimer()
call TimerStart(crashTimer, 0.01, false, function CheckForCrash)
call SaveBoolean(hash, GetHandleId(crashTimer), 0, true)
call SaveStr(hash, GetHandleId(crashTimer), 1, functionName)
call SaveTimerHandle(hash, StringHash(functionName), 0, crashTimer)
set crashTimer = null
endif
endfunction
private function NoCrash takes string functionName returns nothing
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
local timer crashTimer = LoadTimerHandle(hash, StringHash(functionName), 0)
call SaveBoolean(hash, GetHandleId(crashTimer), 0, false)
set crashTimer = null
endif
endfunction
private function GetClockString takes real rawSeconds returns string
local integer seconds
local integer minutes = R2I(rawSeconds) / 60
local string clock
set seconds = ModuloInteger(R2I(rawSeconds), 60)
set clock = I2S(minutes) + ":"
if seconds >= 10 then
set clock = clock + I2S(seconds)
else
set clock = clock + "0" + I2S(seconds)
endif
return clock
endfunction
private function TimeExpires takes nothing returns nothing
local noArgCallback onExpire = HeroSelectionOnExpire
call ExecuteFunc("HeroSelectionForceEnd")
call PauseTimer(countdownUpdateTimer)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(0))
call onExpire.evaluate()
endfunction
private function GetLocZ takes real x, real y returns real
local location tempLoc = Location(x,y)
local real z = GetLocationZ(tempLoc)
call RemoveLocation(tempLoc)
set tempLoc = null
return z
endfunction
private function TimerCounterPlus takes timer t returns integer
local integer counter = LoadInteger( hash , GetHandleId(t) , 0 )
set counter = counter + 1
call SaveInteger( hash , GetHandleId(t) , 0 , counter )
return counter
endfunction
private function PlaySoundLocal takes string soundPath, boolean localPlayerCanHearSound returns nothing
local real volume
local sound s = CreateSound( soundPath , FALSE, FALSE, FALSE, 10, 10, "DefaultEAXON")
call SetSoundChannel(s, 0)
if localPlayerCanHearSound then
set volume = 100
else
set volume = 0
endif
call SetSoundVolumeBJ( s , volume )
call StartSound(s)
call KillSoundWhenDone(s)
set s = null
endfunction
private function GetPickedHeroDisplayedName takes integer heroIndex, boolean capitalize returns string
local unit tempUnit
local string name
static if USE_HERO_PROPER_NAME then
set tempUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), Hero.list[heroIndex].unitId, GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
set name = "|cffffcc00" + GetHeroProperName(tempUnit) + "|r"
call RemoveUnit(tempUnit)
set tempUnit = null
return name
else
if capitalize then
return "The |cffffcc00" + Hero.list[heroIndex].name + "|r"
else
return "the |cffffcc00" + Hero.list[heroIndex].name + "|r"
endif
endif
endfunction
//==========================================================================================================================================================
//Hero Menu
//==========================================================================================================================================================
private function HeroCanBePicked takes integer heroIndex returns boolean
return heroIndex != 0 and not ((heroIndexWasPicked[heroIndex] and not HERO_CAN_BE_PICKED_MULTIPLE_TIMES) or Hero.list[heroIndex].unavailable or heroIndexWasBanned[heroIndex])
endfunction
private function GetHeroAbilityName takes integer id returns string
local string tooltip = BlzGetAbilityTooltip(id, 0)
local integer length = StringLength(tooltip)
local integer delimLength = StringLength(HERO_ABILITY_LEVEL_DELIMITER)
local integer i = 1
local integer j
if HERO_ABILITY_LEVEL_DELIMITER == null or HERO_ABILITY_LEVEL_DELIMITER == "" then
return tooltip
endif
loop
exitwhen i > length - delimLength
set j = 0
loop
exitwhen SubString(tooltip, i+j, i+j+1) != SubString(HERO_ABILITY_LEVEL_DELIMITER, j, j+1)
if j == delimLength - 1 then
return SubString(tooltip, 0, i)
endif
set j = j + 1
endloop
set i = i + 1
endloop
return tooltip
endfunction
private function GetRandomHero takes integer currentSelection, integer P returns integer
local integer array heroesAvailable
local integer numChars = 0
local integer i
local boolean currentSelectionAvailable = false
local integer currentSelectionChar
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
if HeroCanBePicked(i) and not Hero.list[i].unavailableToTeam[TEAM_OF_PLAYER[P]] then
set numChars = numChars + 1
set heroesAvailable[numChars] = i
if currentSelection == i then
set currentSelectionAvailable = true
set currentSelectionChar = numChars
endif
endif
set i = i + 1
endloop
if currentSelectionAvailable and currentSelection != 0 then
//Return random hero not currently selected.
return heroesAvailable[ModuloInteger( currentSelectionChar + GetRandomInt(0,numChars-2) , numChars ) + 1]
else
return heroesAvailable[GetRandomInt(1,numChars)]
endif
endfunction
private function SetButtonFrames takes integer whichButton returns nothing
set heroSelectionButtonIcon[whichButton] = BlzFrameGetChild(heroSelectionButton[whichButton],0)
set heroSelectionButtonIconClicked[whichButton] = BlzFrameGetChild(heroSelectionButton[whichButton],1)
call BlzFrameClearAllPoints(heroSelectionButtonIconClicked[whichButton])
call BlzFrameSetPoint(heroSelectionButtonIconClicked[whichButton], FRAMEPOINT_BOTTOMLEFT, heroSelectionButton[whichButton], FRAMEPOINT_BOTTOMLEFT, 0.001, 0.001)
call BlzFrameSetPoint(heroSelectionButtonIconClicked[whichButton], FRAMEPOINT_TOPRIGHT, heroSelectionButton[whichButton], FRAMEPOINT_TOPRIGHT, -0.001, -0.001)
endfunction
private function SetButtonTooltip takes integer whichButton, string whichTitle, string whichTooltip returns nothing
set heroSelectionButtonTooltip[whichButton] = BlzCreateFrame("CustomTooltip", heroSelectionButton[whichButton], 0, 0)
if TOOLTIP_LOCK_TOP then
call BlzFrameSetAbsPoint( heroSelectionButtonTooltip[whichButton] , FRAMEPOINT_TOPLEFT , TOOLTIP_LEFT_X , TOOLTIP_Y )
else
call BlzFrameSetAbsPoint( heroSelectionButtonTooltip[whichButton] , FRAMEPOINT_BOTTOMLEFT , TOOLTIP_LEFT_X , TOOLTIP_Y )
endif
call BlzFrameSetTooltip( heroSelectionButton[whichButton] , heroSelectionButtonTooltip[whichButton] )
set heroSelectionButtonTooltipTitle[whichButton] = BlzFrameGetChild(heroSelectionButtonTooltip[whichButton],0)
set heroSelectionButtonTooltipText[whichButton] = BlzFrameGetChild(heroSelectionButtonTooltip[whichButton],1)
call BlzFrameSetText( heroSelectionButtonTooltipTitle[whichButton] , whichTitle )
call BlzFrameSetText( heroSelectionButtonTooltipText[whichButton] , whichTooltip )
call BlzFrameSetSize(heroSelectionButtonTooltipText[whichButton] , TOOLTIP_WIDTH - 0.01 , 0.0 )
call BlzFrameSetSize(heroSelectionButtonTooltip[whichButton] , TOOLTIP_WIDTH , BlzFrameGetHeight(heroSelectionButtonTooltipText[whichButton]) + TOOLTIP_BASE_HEIGHT)
endfunction
private function SetButtonTextures takes integer whichButton, boolean disabledTexture returns nothing
local integer i
local integer stringLength
local string disabledPath
local string path
if whichButton <= NUMBER_OF_HEROES then
set path = Hero.list[whichButton].iconPath
elseif whichButton == RANDOM_HERO then
set path = RANDOM_HERO_ICON
elseif whichButton == SUGGEST_RANDOM then
set path = SUGGEST_RANDOM_ICON
elseif whichButton == PAGE_DOWN then
set path = MENU_PAGE_DOWN_ICON
else
set path = MENU_PAGE_UP_ICON
endif
if not disabledTexture then
call BlzFrameSetTexture(heroSelectionButtonIcon[whichButton] , path , 0 , true)
call BlzFrameSetTexture(heroSelectionButtonIconClicked[whichButton] , path , 0 , true)
else
set stringLength = StringLength(path)
set i = 0
loop
exitwhen i > stringLength - 3
if SubString(path, i, i+1) == "B" and SubString(path, i+1, i+2) == "T" and SubString(path, i+2, i+3) == "N" then
set disabledPath = "ReplaceableTextures\\CommandButtonsDisabled\\DISBTN" + SubString(path, i+3, stringLength)
call BlzFrameSetTexture(heroSelectionButtonIcon[whichButton] , disabledPath, 0 , true)
call BlzFrameSetTexture(heroSelectionButtonIconClicked[whichButton] , disabledPath, 0 , true)
return
endif
set i = i + 1
endloop
endif
endfunction
//==========================================================================================================================================================
//Caption
//==========================================================================================================================================================
private function TextColor takes nothing returns nothing
local integer counter = TimerCounterPlus(GetExpiredTimer())
local real colorState = (1 + Cos(2*bj_PI*counter/(CAPTION_CYCLE_TIME/0.02)))/2
local integer r = R2I((1 - colorState)*r1 + colorState*r2)
local integer g = R2I((1 - colorState)*g1 + colorState*g2)
local integer b = R2I((1 - colorState)*b1 + colorState*b2)
local integer array c
local integer i
local string colorString = "|cff"
set c[1] = r/16
set c[2] = r - 16*c[1]
set c[3] = g/16
set c[4] = g - 16*c[3]
set c[5] = b/16
set c[6] = b - 16*c[5]
set i = 1
loop
exitwhen i > 6
set colorString = colorString + SubString(HEX_STRING, c[i], c[i] + 1)
set i = i + 1
endloop
if CAPTION_FADEOUT_TIME != -1 then
if playerHasHero[localPlayerId] then
if CAPTION_FADEOUT_TIME > 0 then
set captionAlphaMultiplier = RMaxBJ(0, captionAlphaMultiplier - 1/(50*CAPTION_FADEOUT_TIME))
else
set captionAlphaMultiplier = 0
endif
endif
endif
call BlzFrameSetText(captionFrame, colorString + HERO_SELECTION_CAPTION + "|r")
call BlzFrameSetAlpha(captionFrame, R2I(captionAlphaMultiplier*((1 - colorState)*CAPTION_ALPHA_1 + colorState*CAPTION_ALPHA_2)))
endfunction
private function HexToInt takes string hexString returns integer
local integer i = 0
local integer int = 0
local string firstChar = StringCase(SubString(hexString, 0, 1), false)
local string secondChar = StringCase(SubString(hexString, 1, 2), false)
local boolean firstCharFound = false
local boolean secondCharFound = false
set i = 0
loop
exitwhen i > 15 or (firstCharFound and secondCharFound)
if not firstCharFound and firstChar == SubString(HEX_STRING, i, i + 1) then
set int = int + 16*i
set firstCharFound = true
endif
if not secondCharFound and secondChar == SubString(HEX_STRING, i, i + 1) then
set int = int + i
set secondCharFound = true
endif
set i = i + 1
endloop
return int
endfunction
private function AnimateCaption takes nothing returns nothing
local integer i
local string s
local integer j
//Get RGB values of CAPTION_COLOR_1 and CAPTION_COLO_2.
set r1 = HexToInt(SubString(CAPTION_COLOR_1, 4, 6))
set g1 = HexToInt(SubString(CAPTION_COLOR_1, 6, 8))
set b1 = HexToInt(SubString(CAPTION_COLOR_1, 8, 10))
set r2 = HexToInt(SubString(CAPTION_COLOR_2, 4, 6))
set g2 = HexToInt(SubString(CAPTION_COLOR_2, 6, 8))
set b2 = HexToInt(SubString(CAPTION_COLOR_2, 8, 10))
call TimerStart( captionTimer , 0.02 , true , function TextColor )
call SaveInteger( hash , GetHandleId(captionTimer) , 0 , 0 )
endfunction
//==========================================================================================================================================================
//Hero Effects
//==========================================================================================================================================================
private function FadeInBackgroundHero takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer counter = TimerCounterPlus(t)
local integer P = LoadInteger( hash , GetHandleId(t) , 1 )
local boolean concealed = LoadBoolean( hash , GetHandleId(t) , 2 )
local Hero whichHero = Hero.list[pickedHeroIndex[P]]
//Hero for owner fades in later than for other players since it's still in the front.
local integer ownerDelay = R2I(50*(FOREGROUND_HERO_FADEOUT_DELAY + FOREGROUND_HERO_FADEOUT_TIME)/BACKGROUND_HERO_FADEIN_TIME)
local string modelPath
if localPlayerId != P and counter <= 51 then
if concealed then
call BlzSetSpecialEffectAlpha( backgroundHero[P] , R2I(2.5*counter) )
if whichHero.needsHeroGlow then
call BlzSetSpecialEffectAlpha( backgroundHeroGlow[P], R2I(2.5*counter) )
endif
else
call BlzSetSpecialEffectAlpha( backgroundHero[P] , 5*counter )
if whichHero.needsHeroGlow then
call BlzSetSpecialEffectAlpha( backgroundHeroGlow[P], 5*counter )
endif
endif
elseif localPlayerId == P and counter > ownerDelay then
call BlzSetSpecialEffectAlpha( backgroundHero[P] , 5*(counter - ownerDelay))
if whichHero.needsHeroGlow then
call BlzSetSpecialEffectAlpha( backgroundHeroGlow[P], 5*(counter - ownerDelay))
endif
endif
static if CREATE_SHADOWS then
if counter == ownerDelay then
call RemoveDestructable(backgroundHeroShadow[P])
set backgroundHeroShadow[P] = CreateDestructable(SHADOW_DESTRUCTABLE_ID, BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P], 0, 1, 0)
endif
endif
if counter == 51 + ownerDelay then
if BACKGROUND_HERO_SELF_HIGHLIGHT != null then
if localPlayerId == P then
set modelPath = BACKGROUND_HERO_SELF_HIGHLIGHT
else
set modelPath = ""
endif
set backgroundHeroHighlight[P] = AddSpecialEffect(modelPath, BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P])
call BlzSetSpecialEffectZ(backgroundHeroHighlight[P], GetLocZ(BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P]) + BACKGROUND_HERO_HIGHLIGHT_Z)
endif
call FlushChildHashtable( hash , GetHandleId(t) )
call DestroyTimer(t)
endif
set t = null
endfunction
private function FadeoutForegroundHero takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer counter = TimerCounterPlus(t)
local integer P = LoadInteger( hash , GetHandleId(t) , 1 )
if counter < 51 and FOREGROUND_HERO_FADEOUT_TIME > 0 then
call BlzSetSpecialEffectAlpha( selectedHero[P] , 255 - 5*counter )
call BlzSetSpecialEffectAlpha( selectedHeroGlow[P] , 255 - 5*counter )
call TimerStart( t , FOREGROUND_HERO_FADEOUT_TIME/50.0 , false , function FadeoutForegroundHero )
else
call BlzSetSpecialEffectPosition( selectedHero[P] , GARBAGE_DUMP_X , GARBAGE_DUMP_Y , 0 )
call DestroyEffect( selectedHero[P] )
call DestroyEffect( selectedHeroGlow[P] )
call FlushChildHashtable( hash , GetHandleId(t) )
call DestroyTimer(t)
endif
set t = null
endfunction
private function ResetAnimation takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
local integer P = LoadInteger( hash , id , 0 )
local integer heroIndex = LoadInteger( hash , id , 1 )
local boolean animateBackgroundHero = LoadBoolean( hash , id , 2)
if animateBackgroundHero then
if localPlayerId != P then
call BlzSpecialEffectClearSubAnimations(backgroundHero[P])
call BlzPlaySpecialEffect( backgroundHero[P] , ANIM_TYPE_STAND )
endif
else
if preselectedHeroIndex[P] == heroIndex then
call BlzSpecialEffectClearSubAnimations(selectedHero[P])
call BlzPlaySpecialEffect( selectedHero[P] , ANIM_TYPE_STAND )
endif
endif
call FlushChildHashtable( hash , GetHandleId(t) )
call DestroyTimer(t)
set t = null
endfunction
private function PlayHeroAnimation takes integer P, integer heroIndex, boolean animateBackgroundHero returns nothing
local timer t = CreateTimer()
local integer id = GetHandleId(t)
if animateBackgroundHero then
if localPlayerId != P and (not CONCEAL_HERO_PICKS_FROM_ENEMIES or (TEAM_OF_PLAYER[localPlayerId] == TEAM_OF_PLAYER[P] and TEAM_OF_PLAYER[P] != 0)) then
call BlzSpecialEffectAddSubAnimation( backgroundHero[P] , Hero.list[heroIndex].selectSubAnim )
call BlzPlaySpecialEffect( backgroundHero[P] , Hero.list[heroIndex].selectAnim )
endif
else
call BlzSpecialEffectAddSubAnimation( selectedHero[P] , Hero.list[heroIndex].selectSubAnim )
call BlzPlaySpecialEffect( selectedHero[P] , Hero.list[heroIndex].selectAnim )
endif
call TimerStart( t , Hero.list[heroIndex].selectAnimLength , false , function ResetAnimation )
call SaveInteger( hash , id , 0 , P )
call SaveInteger( hash , id , 1 , heroIndex )
call SaveBoolean( hash , id , 2 , animateBackgroundHero)
set t = null
endfunction
private function CreateNewForegroundHero takes integer P, integer heroIndex returns nothing
local string modelPath = ""
local string glowPath = ""
local real locZ
local Hero whichHero = Hero.list[heroIndex]
if localPlayerId == P then
if heroIndex == RANDOM_HERO then
set modelPath = "Objects\\InventoryItems\\QuestionMark\\QuestionMark.mdl"
else
set modelPath = whichHero.modelPath
endif
if whichHero.needsHeroGlow then
set glowPath = "GeneralHeroGlow.mdx"
endif
endif
call BlzSetSpecialEffectPosition( selectedHero[P] , GARBAGE_DUMP_X , GARBAGE_DUMP_Y , 0 )
call BlzSetSpecialEffectTimeScale( selectedHero[P] , 9999)
call BlzSetSpecialEffectScale(selectedHero[P], 0)
call DestroyEffect(selectedHero[P])
call DestroyEffect(selectedHeroGlow[P])
set selectedHero[P] = AddSpecialEffect( modelPath , localForegroundHeroX , localForegroundHeroY )
set selectedHeroGlow[P] = AddSpecialEffect( glowPath , localForegroundHeroX , localForegroundHeroY )
set locZ = GetLocZ(localForegroundHeroX, localForegroundHeroY)
call BlzSetSpecialEffectZ( selectedHero[P], locZ + FOREGROUND_HERO_Z )
call BlzSetSpecialEffectZ( selectedHeroGlow[P], locZ + FOREGROUND_HERO_Z )
call BlzSetSpecialEffectYaw( selectedHero[P] , Deg2Rad(localHeroSelectionAngle) + bj_PI )
if heroIndex != RANDOM_HERO then
call BlzSetSpecialEffectScale( selectedHero[P] , whichHero.scalingValue )
endif
if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED and (heroSelectionDisabledForPlayer[P] or not HeroCanBePicked(heroIndex)) then
call BlzSetSpecialEffectColor( selectedHero[P], 0, 0, 0 )
call BlzSetSpecialEffectAlpha( selectedHero[P], 128 )
else
call BlzSetSpecialEffectColor( selectedHero[P] , whichHero.red , whichHero.green , whichHero.blue)
endif
endfunction
private function DeleteBackgroundHero takes integer P returns nothing
call BlzSetSpecialEffectPosition(backgroundHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DestroyEffect(backgroundHero[P])
call DestroyEffect(backgroundHeroGlow[P])
call DestroyEffect(backgroundHeroHighlight[P])
static if CREATE_SHADOWS then
call RemoveDestructable(backgroundHeroShadow[P])
endif
static if PLAYER_TEXT_TAGS then
call DestroyTextTag(backgroundHeroTextTag[P])
endif
endfunction
//==========================================================================================================================================================
//Miscellaneous.
//==========================================================================================================================================================
private function UpdateTimerFrame takes nothing returns nothing
if inBanPhase then
call BlzFrameSetText(timerFrame, TIMER_BAN_PHASE_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
else
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
endif
endfunction
private function LockSelecterCamera takes nothing returns nothing
local integer i = 1
loop
exitwhen i > numSelectingPlayers
if isInHeroSelection[playerNumberOfSlot[i]] then
call CameraSetupApplyForPlayer( true, heroSelectionCamera, Player(playerNumberOfSlot[i]), 0 )
endif
set i = i + 1
endloop
endfunction
//==========================================================================================================================================================
//End hero selection.
//==========================================================================================================================================================
private function EscapePlayer takes player whichPlayer returns nothing
local integer P = GetPlayerId(whichPlayer)
local playerCallback onEscape = HeroSelectionOnEscape
if not isInHeroSelection[P] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to escape player who is not in hero selection...")
endif
return
endif
call InitCrashCheck("EscapePlayer")
set isInHeroSelection[P] = false
set preselectedHeroIndex[P] = 0
set isRepicking[P] = false
set playerHasBan[P] = false
set numPlayersInSelection = numPlayersInSelection - 1
call DestroyEffect(selectedHero[P])
call ResetToGameCameraForPlayer(whichPlayer, 0)
if localPlayerId == P then
call BlzFrameSetVisible(heroSelectionMenu, false)
call BlzFrameSetVisible(captionFrame, false)
call BlzFrameSetVisible(timerFrame, false)
static if HIDE_GAME_UI then
call BlzHideOriginFrames(false)
call BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), true)
endif
endif
call onEscape.evaluate(whichPlayer)
call NoCrash("EscapePlayer")
endfunction
private function EndHeroSelection takes nothing returns nothing
local integer i = 1
local integer j
local integer P
local noArgCallback onFinal = HeroSelectionOnFinal
call InitCrashCheck("EndHeroSelection")
call PauseTimer(lockCameraTimer)
call PauseTimer(countdownTimer)
call PauseTimer(countdownUpdateTimer)
static if not ESCAPE_PLAYER_AFTER_SELECTING then
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
call EscapePlayer(Player(P))
set i = i + 1
endloop
endif
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
call BlzSetSpecialEffectPosition(selectedHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DestroyEffect(selectedHero[P])
call DestroyEffect(selectedHeroGlow[P])
// Added loop to support multiple heroes selection
set j = 0
loop
exitwhen j > NUMBER_OF_HEROES_PER_PLAYER - 1
static if DELETE_BACKGROUND_HEROES_AFTER_END then
call BlzSetSpecialEffectPosition(backgroundHero[P + j * MAX_NUMBER_OF_PLAYERS], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DeleteBackgroundHero(P + j * MAX_NUMBER_OF_PLAYERS)
endif
set j = j + 1
endloop
set i = i + 1
endloop
static if CREATE_SHADOWS then
call RemoveDestructable(foregroundHeroShadow)
endif
call onFinal.evaluate()
call NoCrash("EndHeroSelection")
endfunction
private function EscapePlayerCaller takes nothing returns nothing
local timer t = GetExpiredTimer()
call EscapePlayer(Player(LoadInteger(hash, GetHandleId(t), 0)))
call FlushChildHashtable(hash, GetHandleId(t))
call DestroyTimer(t)
set t = null
static if ESCAPE_PLAYER_AFTER_SELECTING then
if numPlayersInSelection == 0 then
call EndHeroSelection()
endif
endif
endfunction
private function EndHeroSelectionCaller takes nothing returns nothing
local timer t = GetExpiredTimer()
call EndHeroSelection()
call DestroyTimer(t)
set t = null
endfunction
private function OnPlayerLeave takes nothing returns nothing
local player whichPlayer = GetTriggerPlayer()
local integer P = GetPlayerId(whichPlayer)
local integer i = 1
local integer j = 1
local playerCallback onLeave = HeroSelectionOnLeave
call InitCrashCheck("OnPlayerLeave")
loop
exitwhen i > numSelectingPlayers
if P == playerNumberOfSlot[i] then
set j = i
loop
exitwhen j > numSelectingPlayers - 1
set playerNumberOfSlot[j] = playerNumberOfSlot[j+1]
set j = j + 1
endloop
exitwhen true
endif
set i = i + 1
endloop
set numSelectingPlayers = numSelectingPlayers - 1
if isInHeroSelection[P] then
call onLeave.evaluate(whichPlayer)
set numPlayersInSelection = numPlayersInSelection - 1
set isInHeroSelection[P] = false
endif
if playerHasHero[P] then
// Added loop to support multiple heroes selection
set j = 0
loop
exitwhen j > playerHeroesNb[P] -1
set numPlayersWithHero = numPlayersWithHero - 1
set playerHasHero[P + j * MAX_NUMBER_OF_PLAYERS] = false
call BlzSetSpecialEffectPosition(selectedHero[P+ j * MAX_NUMBER_OF_PLAYERS], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DestroyEffect(selectedHero[P+ j * MAX_NUMBER_OF_PLAYERS])
call DestroyEffect(selectedHeroGlow[P+ j * MAX_NUMBER_OF_PLAYERS])
call DestroyEffect(selectedHeroGlow[P+ j * MAX_NUMBER_OF_PLAYERS])
call BlzSetSpecialEffectPosition(backgroundHero[P+ j * MAX_NUMBER_OF_PLAYERS], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DeleteBackgroundHero(P+ j * MAX_NUMBER_OF_PLAYERS)
endloop
elseif numPlayersWithHero == numSelectingPlayers and not isRepicking[P] then
call TimerStart( CreateTimer() , LAST_PLAYER_SELECT_END_DELAY , false , function EndHeroSelectionCaller )
endif
call NoCrash("OnPlayerLeave")
endfunction
//==========================================================================================================================================================
//Cycle Page.
//==========================================================================================================================================================
private function CyclePage takes nothing returns nothing
local player whichPlayer = GetTriggerPlayer()
local integer P = GetPlayerId(whichPlayer)
local integer i
call PlaySoundLocal( "Sound\\Interface\\BigButtonClick.flac" , localPlayerId == P )
if localPlayerId == P then
if BlzGetTriggerFrame() == heroSelectionButton[PAGE_DOWN] then
set currentPage = ModuloInteger(currentPage - 2, NUMBER_OF_PAGES) + 1
else
set currentPage = ModuloInteger(currentPage, NUMBER_OF_PAGES) + 1
endif
call BlzFrameSetVisible(heroSelectionButtonHighlight, PAGE_OF_CATEGORY[Hero.list[preselectedHeroIndex[P]].category] == currentPage)
set i = 1
loop
exitwhen i > NUMBER_OF_CATEGORIES
call BlzFrameSetVisible(heroSelectionCategory[i], PAGE_OF_CATEGORY[i] == currentPage)
set i = i + 1
endloop
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
call BlzFrameSetVisible(heroSelectionButton[i], PAGE_OF_CATEGORY[Hero.list[i].category] == currentPage)
set i = i + 1
endloop
endif
endfunction
//==========================================================================================================================================================
//Ban Hero.
//==========================================================================================================================================================
private function ExecuteBan takes Hero whichHero, boolean disable returns nothing
local integer i
local integer P
if disable then
call BlzFrameSetText(heroSelectionButtonTooltipText[whichHero.index] , BlzGetAbilityExtendedTooltip(whichHero.tooltipAbility, 0) + "|n|n|cffff0000This hero was banned.|r")
else
call BlzFrameSetText(heroSelectionButtonTooltipText[whichHero.index] , BlzGetAbilityExtendedTooltip(whichHero.tooltipAbility, 0))
endif
call BlzFrameSetSize(heroSelectionButtonTooltipText[whichHero.index] , TOOLTIP_WIDTH - 0.01 , 0.0 )
call BlzFrameSetSize(heroSelectionButtonTooltip[whichHero.index] , TOOLTIP_WIDTH , BlzFrameGetHeight(heroSelectionButtonTooltipText[whichHero.index]) + TOOLTIP_BASE_HEIGHT)
set heroIndexWasBanned[whichHero.index] = true
call SetButtonTextures(whichHero.index, not HeroCanBePicked(whichHero.index))
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if localPlayerId == P and preselectedHeroIndex[P] == whichHero then
call BlzFrameSetEnable( heroAcceptButton , false )
call BlzFrameSetEnable( heroBanButton , false )
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
call BlzSetSpecialEffectAlpha(selectedHero[P], 128)
call BlzSetSpecialEffectColor(selectedHero[P], 0, 0, 0)
endif
endif
set i = i + 1
endloop
endfunction
private function BanHero takes nothing returns nothing
local string message
local player whichPlayer = GetTriggerPlayer()
local integer P = GetPlayerId(whichPlayer)
local integer heroIndex = preselectedHeroIndex[P]
call InitCrashCheck("BanHero")
if localPlayerId == P then
call BlzFrameSetEnable(heroBanButton, false)
endif
set playerHasBan[P] = false
set message = GetPickedHeroDisplayedName(heroIndex, true) + " was banned."
static if LIBRARY_NeatMessages then
call NeatMessage(message)
else
call DisplayTextToPlayer(GetLocalPlayer(), TEXT_MESSAGE_X_OFFSET, TEXT_MESSAGE_Y_OFFSET, message)
endif
if OTHER_PLAYER_HERO_PICK_SOUND != null then
call PlaySoundLocal(OTHER_PLAYER_HERO_PICK_SOUND, true)
endif
call ExecuteBan(Hero.list[heroIndex], true)
call NoCrash("BanHero")
endfunction
//==========================================================================================================================================================
//Pick Hero.
//==========================================================================================================================================================
private function PickHero takes nothing returns nothing
local integer i
local integer j
local integer P
local integer Q
local string message
local player whichPlayer
local integer heroIndex
local timer t
local string modelPath
local boolean wasRandomSelect
local boolean concealed
local integer id
local boolean allHumanPlayersHaveHeroes
local effect pickEffect
local noArgCallback onLast = HeroSelectionOnLast
local playerHeroCallback onPick = HeroSelectionOnPick
call InitCrashCheck("PickHero")
if not isForcedSelect then
set whichPlayer = GetTriggerPlayer()
set P = GetPlayerId(whichPlayer)
set heroIndex = preselectedHeroIndex[P]
else
set P = storePlayerIndex
set whichPlayer = Player(P)
set heroIndex = storeHeroIndex
endif
set concealed = CONCEAL_HERO_PICKS_FROM_ENEMIES and localPlayerId != P and (TEAM_OF_PLAYER[localPlayerId] != TEAM_OF_PLAYER[P] or TEAM_OF_PLAYER[P] == 0)
//Random
if heroIndex == RANDOM_HERO then
set heroIndex = GetRandomHero(0, P)
if PICK_SOUND != null then
call PlaySoundLocal(PICK_SOUND , localPlayerId == P )
endif
set wasRandomSelect = true
static if CREATE_FOREGROUND_HERO then
if localPlayerId == P then
set modelPath = Hero.list[heroIndex].modelPath
else
set modelPath = ""
endif
// Added multiple selection
call DestroyEffect(selectedHero[P])
call BlzSetSpecialEffectPosition( selectedHero[P] , GARBAGE_DUMP_X , GARBAGE_DUMP_Y , 0 )
set selectedHero[P] = AddSpecialEffect( modelPath , localForegroundHeroX , localForegroundHeroY )
call BlzSetSpecialEffectYaw( selectedHero[P] , Deg2Rad(localHeroSelectionAngle) + bj_PI )
call BlzSetSpecialEffectScale( selectedHero[P] , Hero.list[heroIndex].scalingValue )
endif
else
call PlaySoundLocal( "Sound\\Interface\\BigButtonClick.flac" , localPlayerId == P )
if PICK_SOUND != null then
call PlaySoundLocal(PICK_SOUND , localPlayerId == P )
endif
set wasRandomSelect = false
endif
//Disable Buttons for selecting player.
set i = 1
loop
exitwhen i > SUGGEST_RANDOM
if localPlayerId == P then
call SetButtonTextures(i, true)
endif
set i = i + 1
endloop
if localPlayerId == P then
call BlzFrameSetEnable( heroAcceptButton , false )
call BlzFrameSetEnable( heroBanButton , false )
endif
//Disable button for other players that have pre-selected that hero.
static if CREATE_FOREGROUND_HERO then
static if not HERO_CAN_BE_PICKED_MULTIPLE_TIMES then
call SetButtonTextures(heroIndex, true)
set i = 1
loop
exitwhen i > numSelectingPlayers
if localPlayerId == playerNumberOfSlot[i] and preselectedHeroIndex[playerNumberOfSlot[i]] == heroIndex then
call BlzFrameSetEnable( heroAcceptButton , false )
call BlzFrameSetEnable( heroBanButton , false )
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
if playerNumberOfSlot[i] != P then
call BlzSetSpecialEffectAlpha(selectedHero[playerNumberOfSlot[i]], 128)
call BlzSetSpecialEffectColor(selectedHero[playerNumberOfSlot[i]], 0, 0, 0)
endif
endif
endif
set i = i + 1
endloop
endif
endif
//Set variables
set heroIndexWasPicked[heroIndex] = true
set pickedHeroIndex[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] = heroIndex
set heroIdOfPlayer[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] = Hero.list[heroIndex].unitId
set heroIconPathOfPlayer[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] = "ReplaceableTextures\\CommandButtons\\" + Hero.list[heroIndex].iconPath
set playerHasHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] = true
set playerHasBan[P] = false
set numPlayersWithHero = numPlayersWithHero + 1
// Added
set isRandomHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] = wasRandomSelect
//Text messages
if OTHER_PLAYER_HERO_PICK_SOUND != null then
call PlaySoundLocal(OTHER_PLAYER_HERO_PICK_SOUND, localPlayerId != P and (MESSAGE_EVEN_WHEN_CONCEALED or not concealed))
endif
set i = 1
loop
exitwhen i > numSelectingPlayers
set Q = playerNumberOfSlot[i]
if P != Q then
if CREATE_TEXT_MESSAGE_ON_PICK then
set message = ""
if (CONCEAL_HERO_PICKS_FROM_ENEMIES and (TEAM_OF_PLAYER[P] != TEAM_OF_PLAYER[Q] or TEAM_OF_PLAYER[P] == 0)) then
if MESSAGE_EVEN_WHEN_CONCEALED then
set message = message + coloredPlayerName[P] + " has selected a hero."
endif
else
if wasRandomSelect then
set message = message + coloredPlayerName[P] + " has randomly selected " + GetPickedHeroDisplayedName(heroIndex, false) + "."
else
set message = message + coloredPlayerName[P] + " has selected " + GetPickedHeroDisplayedName(heroIndex, false) + "."
endif
endif
static if INCLUDE_PROGRESSION_IN_MESSAGE then
set message = message + " " + I2S(numPlayersWithHero) + "/" + I2S(numSelectingPlayers) + " players have selected."
endif
if message != "" then
static if LIBRARY_NeatMessages then
call NeatMessageToPlayer(Player(Q), message)
else
call DisplayTextToPlayer(Player(Q), TEXT_MESSAGE_X_OFFSET, TEXT_MESSAGE_Y_OFFSET, message)
endif
endif
endif
else
if wasRandomSelect then
set message = "You randomly selected " + GetPickedHeroDisplayedName(heroIndex, false) + "."
else
set message = "You selected " + GetPickedHeroDisplayedName(heroIndex, false) + "."
endif
static if INCLUDE_PROGRESSION_IN_MESSAGE then
set message = message + " " + I2S(numPlayersWithHero) + "/" + I2S(numSelectingPlayers) + " players have selected."
endif
static if LIBRARY_NeatMessages then
call NeatMessageToPlayer(Player(P), message)
else
call DisplayTextToPlayer(Player(P), TEXT_MESSAGE_X_OFFSET, TEXT_MESSAGE_Y_OFFSET, message)
endif
endif
set i = i + 1
endloop
//Foreground hero
static if CREATE_FOREGROUND_HERO then
if PICK_EFFECT != null then
if localPlayerId == P then
set modelPath = PICK_EFFECT
else
set modelPath = ""
endif
endif
set pickEffect = AddSpecialEffect( modelPath , localForegroundHeroX , localForegroundHeroY )
call BlzSetSpecialEffectZ(pickEffect, GetLocZ(localForegroundHeroX, localForegroundHeroY) + FOREGROUND_HERO_Z)
call DestroyEffect(pickEffect)
set pickEffect = null
static if PLAY_ANIMATION_ON_PICK then
call PlayHeroAnimation(P, heroIndex, false)
endif
static if PLAY_EMOTE_ON_PICK then
call PlaySoundLocal( Hero.list[heroIndex].selectEmote , localPlayerId == P )
endif
if CREATE_BACKGROUND_HEROES or ESCAPE_PLAYER_AFTER_SELECTING or isRepicking[P] then
set t = CreateTimer()
call TimerStart( t , FOREGROUND_HERO_FADEOUT_DELAY , false , function FadeoutForegroundHero )
call SaveInteger( hash , GetHandleId(t) , 0 , 0 )
call SaveInteger( hash , GetHandleId(t) , 1 , P )
endif
endif
//Create Background Hero
static if CREATE_BACKGROUND_HEROES then
if concealed then
set modelPath = CONCEALED_HERO_EFFECT
else
set modelPath = Hero.list[heroIndex].modelPath
endif
set backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] = AddSpecialEffect( modelPath , BACKGROUND_HERO_X[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , BACKGROUND_HERO_Y[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] )
call BlzSetSpecialEffectScale( backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , Hero.list[heroIndex].scalingValue )
call BlzSetSpecialEffectAlpha( backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , 0 )
call BlzSetSpecialEffectYaw( backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , Atan2(localForegroundHeroY - BACKGROUND_HERO_Y[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] + Sin(Deg2Rad(localHeroSelectionAngle))*BACKGROUND_HERO_FACING_POINT_OFFSET , localForegroundHeroX - BACKGROUND_HERO_X[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] + Cos(Deg2Rad(localHeroSelectionAngle))*BACKGROUND_HERO_FACING_POINT_OFFSET) )
call BlzSetSpecialEffectColorByPlayer( backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , whichPlayer )
call BlzSetSpecialEffectColor( backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , Hero.list[heroIndex].red , Hero.list[heroIndex].green , Hero.list[heroIndex].blue)
if Hero.list[heroIndex].needsHeroGlow then
set backgroundHeroGlow[P] = AddSpecialEffect( "GeneralHeroGlow.mdx" , BACKGROUND_HERO_X[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , BACKGROUND_HERO_Y[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] )
call BlzSetSpecialEffectAlpha( backgroundHeroGlow[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]], 0 )
call BlzSetSpecialEffectColorByPlayer( backgroundHeroGlow[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , whichPlayer )
endif
if concealed then
call BlzSetSpecialEffectColor(backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]], 0, 0, 0)
endif
static if PLAY_ANIMATION_ON_BACKGROUND_HERO then
call PlayHeroAnimation(P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P], heroIndex, true)
endif
if BACKGROUND_HERO_FADEIN_EFFECT != null then
if localPlayerId != P then
set modelPath = BACKGROUND_HERO_FADEIN_EFFECT
else
set modelPath = ""
endif
call DestroyEffect(AddSpecialEffect(modelPath, BACKGROUND_HERO_X[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]], BACKGROUND_HERO_Y[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] ))
endif
static if PLAY_EMOTE_ON_BACKGROUND_HERO then
call PlaySoundLocal(Hero.list[heroIndex].selectEmote, localPlayerId != P and isInHeroSelection[localPlayerId] and not concealed)
endif
static if CREATE_SHADOWS then
if localPlayerId == P then
set id = NO_SHADOW_DESTRUCTABLE_ID
else
set id = SHADOW_DESTRUCTABLE_ID
endif
set backgroundHeroShadow[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] = CreateDestructable(id, BACKGROUND_HERO_X[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]], BACKGROUND_HERO_Y[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]], 0, 1, 0)
endif
if BACKGROUND_HERO_FADEIN_TIME > 0 then
set t = CreateTimer()
call TimerStart( t , BACKGROUND_HERO_FADEIN_TIME/50.0 , true , function FadeInBackgroundHero )
set id = GetHandleId(t)
call SaveInteger( hash , id , 0 , 0 )
call SaveInteger( hash , id , 1 , P )
call SaveBoolean( hash , id , 2 , concealed)
else
if concealed then
call BlzSetSpecialEffectAlpha( backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , 128 )
else
call BlzSetSpecialEffectAlpha( backgroundHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] , 255 )
endif
endif
endif
// Added multiple heroes selection
set playerHeroesNb[P] = playerHeroesNb[P] + 1
if playerHeroesNb[P] == NUMBER_OF_HEROES_PER_PLAYER then
if ESCAPE_PLAYER_AFTER_SELECTING or isRepicking[P] then
set t = CreateTimer()
call TimerStart( t , PLAYER_PICK_ESCAPE_DELAY , false , function EscapePlayerCaller)
call SaveInteger(hash, GetHandleId(t), 0, P)
endif
//End hero selection.
if not isRepicking[P] and numPlayersWithHero == numSelectingPlayers then
call onLast.evaluate()
if TIME_LIMIT > 0 then
call PauseTimer(countdownTimer)
endif
static if not ESCAPE_PLAYER_AFTER_SELECTING then
call TimerStart( CreateTimer() , LAST_PLAYER_SELECT_END_DELAY , false , function EndHeroSelectionCaller )
endif
endif
static if COMPUTER_AUTO_PICK_RANDOM_HERO then
if playerIsHuman[P] and not isRepicking[P] then
set allHumanPlayersHaveHeroes = true
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if playerIsHuman[P] and playerHeroesNb[P] != NUMBER_OF_HEROES_PER_PLAYER then
set allHumanPlayersHaveHeroes = false
exitwhen true
endif
set i = i + 1
endloop
if allHumanPlayersHaveHeroes then
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set j = 0
loop
exitwhen j > NUMBER_OF_HEROES_PER_PLAYER -1
if not playerIsHuman[P] and not playerHasHero[P + j * MAX_NUMBER_OF_PLAYERS] and isInHeroSelection[P] then
set isForcedSelect = true
set storePlayerIndex = P
set storeHeroIndex = RANDOM_HERO
call PickHero()
set isForcedSelect = false
endif
set j = j + 1
endloop
set i = i + 1
endloop
endif
endif
endif
call onPick.evaluate(whichPlayer, Hero.list[heroIndex])
else
call onPick.evaluate(whichPlayer, Hero.list[heroIndex])
call EnablePlayerHeroPreselection.evaluate(whichPlayer, true)
endif
//call onPick.evaluate(whichPlayer, Hero.list[heroIndex])
call NoCrash("PickHero")
set t = null
endfunction
//==========================================================================================================================================================
//Preselect Hero.
//==========================================================================================================================================================
private function PreselectRandomCycle takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer P = LoadInteger(hash , GetHandleId(t) , 0 )
local integer heroIndexShown = LoadInteger( hash , GetHandleId(t) , 1 )
local integer newHeroShown
call InitCrashCheck("PreselectRandomCycle")
if preselectedHeroIndex[P] == RANDOM_HERO and playerHeroesNb[P] != NUMBER_OF_HEROES_PER_PLAYER then
set newHeroShown = GetRandomHero(heroIndexShown, P)
call CreateNewForegroundHero(P, newHeroShown)
call SaveInteger( hash , GetHandleId(t) , 1 , newHeroShown )
else
call FlushChildHashtable( hash , GetHandleId(t) )
call DestroyTimer(t)
endif
call NoCrash("PreselectRandomCycle")
set t = null
endfunction
private function PreselectHero takes nothing returns nothing
local player whichPlayer = GetTriggerPlayer()
local integer P = GetPlayerId(whichPlayer)
local integer i
local framehandle whichFrame = BlzGetTriggerFrame()
local boolean isSuggest
local boolean isRandom
local integer heroIndex
local string modelPath = ""
local timer t
local integer oldHero = preselectedHeroIndex[P]
local integer id
local effect preselectEffect
local playerHeroHeroCallback onPreselect = HeroSelectionOnPreselect
local integer firstRandomHero
if heroPreselectionDisabledForPlayer[P] then
return
endif
call InitCrashCheck("PreselectHero")
call PlaySoundLocal( "Sound\\Interface\\BigButtonClick.flac" , localPlayerId == P )
if PRESELECT_EFFECT != null then
if localPlayerId == P then
set modelPath = PRESELECT_EFFECT
endif
set preselectEffect = AddSpecialEffect( modelPath , localForegroundHeroX , localForegroundHeroY )
call BlzSetSpecialEffectZ(preselectEffect, GetLocZ(localForegroundHeroX, localForegroundHeroY) + FOREGROUND_HERO_Z)
call DestroyEffect(preselectEffect)
set preselectEffect = null
endif
set i = 1
loop
exitwhen i > SUGGEST_RANDOM
if whichFrame == heroSelectionButton[i] then
set heroIndex = i
if heroIndex == RANDOM_HERO then
set isSuggest = false
set isRandom = true
elseif heroIndex == SUGGEST_RANDOM then
set isSuggest = true
set isRandom = false
else
set isSuggest = false
set isRandom = false
endif
exitwhen true
endif
set i = i + 1
endloop
set whichFrame = null
if isSuggest then
set heroIndex = GetRandomHero(preselectedHeroIndex[P], P)
endif
if localPlayerId == P then
if not isRandom then
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
if Hero.list[heroIndex].abilities[i] != 0 then
call BlzFrameSetTexture( heroSelectionAbility[i] , BlzGetAbilityIcon( Hero.list[heroIndex].abilities[i] ) , 0, true )
call BlzFrameSetText( heroSelectionAbilityTooltipTitle[i] , GetHeroAbilityName(Hero.list[heroIndex].abilities[i]) )
if Hero.list[heroIndex].isNonHeroAbility[i] then
call BlzFrameSetText( heroSelectionAbilityTooltipText[i] , BlzGetAbilityExtendedTooltip( Hero.list[heroIndex].abilities[i] , 0 ) )
else
call BlzFrameSetText( heroSelectionAbilityTooltipText[i] , BlzGetAbilityResearchExtendedTooltip( Hero.list[heroIndex].abilities[i] , 0 ) )
endif
call BlzFrameSetSize(heroSelectionAbilityTooltipText[i] , TOOLTIP_WIDTH - 0.01 , 0.0 )
call BlzFrameSetSize(heroSelectionAbilityTooltip[i] , TOOLTIP_WIDTH , BlzFrameGetHeight(heroSelectionAbilityTooltipText[i]) + TOOLTIP_BASE_HEIGHT)
call BlzFrameSetVisible( heroSelectionAbility[i] , true )
else
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
endif
set i = i + 1
endloop
else
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
set i = i + 1
endloop
endif
call BlzFrameSetEnable( heroAcceptButton , HeroCanBePicked(heroIndex) and playerHeroesNb[P] != NUMBER_OF_HEROES_PER_PLAYER and not heroSelectionDisabledForPlayer[P] )
call BlzFrameSetEnable( heroBanButton , HeroCanBePicked(heroIndex) and heroIndex != RANDOM_HERO and playerHasBan[P] )
endif
static if CREATE_FOREGROUND_HERO then
if not playerHasHero[P + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[P]] and preselectedHeroIndex[P] != heroIndex then
if not isRandom then
call CreateNewForegroundHero(P, heroIndex)
if HeroCanBePicked(heroIndex) and not heroSelectionDisabledForPlayer[P] then
static if PLAY_EMOTE_ON_PRESELECT then
call PlaySoundLocal( Hero.list[heroIndex].selectEmote , localPlayerId == P )
endif
static if PLAY_ANIMATION_ON_PRESELECT then
call PlayHeroAnimation(P, heroIndex, false)
endif
endif
elseif preselectedHeroIndex[P] != RANDOM_HERO then
static if RANDOM_SELECT_CYCLE_STYLE then
set firstRandomHero = GetRandomHero(0,P)
call CreateNewForegroundHero(P, firstRandomHero)
set t = CreateTimer()
call TimerStart( t , RANDOM_SELECT_CYCLE_INTERVAL , true , function PreselectRandomCycle )
call SaveInteger( hash , GetHandleId(t) , 0 , P )
call SaveInteger( hash , GetHandleId(t) , 1 , firstRandomHero )
set t = null
else
call CreateNewForegroundHero(P, RANDOM_HERO)
endif
endif
endif
static if CREATE_SHADOWS then
call RemoveDestructable(foregroundHeroShadow)
if preselectedHeroIndex[localPlayerId] == 0 or playerHasHero[localPlayerId + MAX_NUMBER_OF_PLAYERS * playerHeroesNb[localPlayerId]] then
set id = NO_SHADOW_DESTRUCTABLE_ID
else
set id = SHADOW_DESTRUCTABLE_ID
endif
set foregroundHeroShadow = CreateDestructable(id, localForegroundHeroX, localForegroundHeroY, 0, 1, 0)
endif
endif
if localPlayerId == P then
if not isRandom then
call BlzFrameSetPoint( heroSelectionButtonHighlight , FRAMEPOINT_TOPLEFT , heroSelectionButton[heroIndex] , FRAMEPOINT_TOPLEFT , 0.005*0.039/MENU_BUTTON_SIZE , -0.005*0.039/MENU_BUTTON_SIZE )
call BlzFrameSetPoint( heroSelectionButtonHighlight , FRAMEPOINT_BOTTOMRIGHT , heroSelectionButton[heroIndex] , FRAMEPOINT_BOTTOMRIGHT , -0.005*0.039/MENU_BUTTON_SIZE , 0.005*0.039/MENU_BUTTON_SIZE )
else
call BlzFrameSetPoint( heroSelectionButtonHighlight , FRAMEPOINT_TOPLEFT , heroSelectionButton[RANDOM_HERO] , FRAMEPOINT_TOPLEFT , 0.005*0.039/MENU_BUTTON_SIZE , -0.005*0.039/MENU_BUTTON_SIZE )
call BlzFrameSetPoint( heroSelectionButtonHighlight , FRAMEPOINT_BOTTOMRIGHT , heroSelectionButton[RANDOM_HERO] , FRAMEPOINT_BOTTOMRIGHT , -0.005*0.039/MENU_BUTTON_SIZE , 0.005*0.039/MENU_BUTTON_SIZE )
endif
call BlzFrameSetVisible( heroSelectionButtonHighlight , true )
endif
set preselectedHeroIndex[P] = heroIndex
call onPreselect.evaluate(whichPlayer, Hero.list[oldHero], Hero.list[heroIndex])
call NoCrash("PreselectHero")
endfunction
//==========================================================================================================================================================
//Init.
//==========================================================================================================================================================
private function InitMenu takes nothing returns nothing
local integer i
local integer j
local integer jLocal
local integer h
local integer k
local trigger trig
local real buttonSpacing = MENU_BUTTON_SIZE + MENU_BUTTON_BUTTON_GAP
local real menuWidth
local real menuHeight
local real Ystart
local real currentY
local real currentYLowest
local real widthDiff
local integer buttonsInRandomRow
local integer column
local integer heroesThisCategory
local integer array whichHeroes
local integer heroesThisCategoryLocal
local boolean newRow
local real xOffset
local real glueTextOffset = 0.005
local boolean hasNoCategoryHeroes = false
local boolean firstCategoryThisPage
local real pageCycleDownX
local real pageCycleUpX
local real pageCycleDownY
local real pageCycleUpY
local boolean pageCycleTextType
local real pageCycleButtonSpacing
local real pageCycleButtonSize
local real pageCycleScaleOffset
local real wideScreenAreaWidth
local real menuXLeftLocal
local real categoryScale
call InitCrashCheck("InitMenu")
call BlzLoadTOCFile("HeroSelectionTemplates.toc")
set wideScreenAreaWidth = 0.6*(BlzGetLocalClientWidth()/I2R(BlzGetLocalClientHeight()) - 4.0/3.0)/2.0
set menuXLeftLocal = RMaxBJ(0, MENU_X_LEFT + wideScreenAreaWidth)
set tooltipLeftXLocal = RMinBJ(TOOLTIP_LEFT_X + wideScreenAreaWidth, 0.8 + wideScreenAreaWidth - TOOLTIP_WIDTH)
set menuWidth = 2*MENU_LEFT_RIGHT_EDGE_GAP + MENU_NUMBER_OF_COLUMNS*MENU_BUTTON_SIZE + (MENU_NUMBER_OF_COLUMNS - 1)*MENU_BUTTON_BUTTON_GAP
if MENU_BORDER_TILE_SIZE > 0 then
set widthDiff = menuWidth
set menuWidth = R2I(menuWidth/MENU_BORDER_TILE_SIZE + 0.99)*MENU_BORDER_TILE_SIZE
set widthDiff = menuWidth - widthDiff
else
set widthDiff = 0
endif
set heroSelectionMenu = BlzCreateFrame("HeroSelectionMenu", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), 0, 0)
call BlzFrameSetPoint(heroSelectionMenu, FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal , MENU_Y_TOP )
call BlzFrameSetPoint(heroSelectionMenu, FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth , 0 )
set heroSelectionButtonTrigger = CreateTrigger()
call TriggerAddAction( heroSelectionButtonTrigger , function PreselectHero )
set Ystart = MENU_Y_TOP - MENU_TOP_EDGE_GAP
set currentYLowest = Ystart
//Hero buttons
set k = 1
loop
exitwhen k > NUMBER_OF_PAGES
set currentY = Ystart
set firstCategoryThisPage = true
set i = 0
loop
exitwhen i > NUMBER_OF_CATEGORIES
if PAGE_OF_CATEGORY[i] == k or (PAGE_OF_CATEGORY[i] == 0 and k == 1) then
if i > 0 then
if not firstCategoryThisPage or CATEGORY_NAMES[i] != null then
set currentY = currentY - MENU_CATEGORY_GAP
endif
if not firstCategoryThisPage then
set currentY = currentY - buttonSpacing
endif
if CATEGORY_NAMES[i] != null then
set categoryScale = MENU_CATEGORY_FONT_SIZE/10.
set heroSelectionCategory[i] = BlzCreateFrameByType("TEXT", CATEGORY_NAMES[i], heroSelectionMenu, "", 0)
call BlzFrameSetPoint(heroSelectionCategory[i] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal/categoryScale , (currentY + MENU_CATEGORY_GAP/2 + MENU_CATEGORY_TITLE_Y + 0.02)/categoryScale)
call BlzFrameSetPoint(heroSelectionCategory[i] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , (menuXLeftLocal + menuWidth)/categoryScale , (currentY + MENU_CATEGORY_GAP/2 + MENU_CATEGORY_TITLE_Y - 0.02)/categoryScale)
call BlzFrameSetTextAlignment(heroSelectionCategory[i] , TEXT_JUSTIFY_MIDDLE , TEXT_JUSTIFY_CENTER)
call BlzFrameSetScale(heroSelectionCategory[i], categoryScale)
call BlzFrameSetText(heroSelectionCategory[i] , CATEGORY_NAMES[i])
if k != 1 then
call BlzFrameSetVisible(heroSelectionCategory[i], false)
endif
endif
endif
set heroesThisCategory = 0
set heroesThisCategoryLocal = 0
set j = 1
loop
exitwhen j > NUMBER_OF_HEROES
if Hero.list[j].category == i then
set heroesThisCategory = heroesThisCategory + 1
set whichHeroes[heroesThisCategory] = j
if not Hero.list[j].unavailableToTeam[TEAM_OF_PLAYER[localPlayerId]] then
set heroesThisCategoryLocal = heroesThisCategoryLocal + 1
endif
endif
set j = j + 1
endloop
if i == 0 and heroesThisCategoryLocal > 0 then
set hasNoCategoryHeroes = true
endif
set column = 0
set newRow = true
set j = 1
set jLocal = 1
loop
exitwhen j > heroesThisCategory
if newRow then
if heroesThisCategoryLocal - (jLocal-1) < MENU_NUMBER_OF_COLUMNS then
set xOffset = buttonSpacing/2 * (MENU_NUMBER_OF_COLUMNS - (heroesThisCategoryLocal - (jLocal-1))) + widthDiff/2
else
set xOffset = widthDiff/2
endif
if jLocal != 1 then
set currentY = currentY - buttonSpacing
endif
endif
set h = whichHeroes[j]
set heroSelectionButton[h] = BlzCreateFrameByType("GLUETEXTBUTTON", "heroSelectionButton_" + I2S(h), heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[h] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset + column*buttonSpacing - glueTextOffset, currentY + glueTextOffset )
call BlzFrameSetPoint( heroSelectionButton[h] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset + column*buttonSpacing + MENU_BUTTON_SIZE + glueTextOffset, currentY - MENU_BUTTON_SIZE - glueTextOffset )
call SetButtonFrames(h)
call SetButtonTextures(h, not PRE_SELECT_BEFORE_ENABLED or Hero.list[h].unavailable)
if BlzGetAbilityExtendedTooltip(Hero.list[h].tooltipAbility, 0) != null then
if not Hero.list[h].unavailable then
call SetButtonTooltip(h, Hero.list[h].name, BlzGetAbilityExtendedTooltip(Hero.list[h].tooltipAbility, 0))
else
call SetButtonTooltip(h, Hero.list[h].name, BlzGetAbilityExtendedTooltip(Hero.list[h].tooltipAbility, 0) + "|n|n|cffff0000Not available.|r")
endif
else
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Tooltip missing or invalid for hero " + Hero.list[h].name + "...")
endif
endif
call BlzFrameSetVisible(heroSelectionButton[h], not Hero.list[h].unavailableToTeam[TEAM_OF_PLAYER[localPlayerId]] and k == 1)
call BlzTriggerRegisterFrameEvent( heroSelectionButtonTrigger, heroSelectionButton[h] , FRAMEEVENT_CONTROL_CLICK )
if not Hero.list[h].unavailableToTeam[TEAM_OF_PLAYER[localPlayerId]] then
set column = column + 1
if column == MENU_NUMBER_OF_COLUMNS then
set newRow = true
set column = 0
else
set newRow = false
endif
set jLocal = jLocal + 1
endif
set j = j + 1
endloop
if i > 0 or hasNoCategoryHeroes then
set firstCategoryThisPage = false
endif
endif
set i = i + 1
endloop
if currentY < currentYLowest then
set currentYLowest = currentY
endif
set k = k + 1
endloop
set currentY = currentYLowest
//Random
if MENU_INCLUDE_RANDOM_PICK or MENU_INCLUDE_SUGGEST_RANDOM or (NUMBER_OF_PAGES > 1 and PAGE_CYCLE_BUTTON_STYLE == "EnvelopRandomButton") then
set currentY = currentY - buttonSpacing - MENU_HEROES_RANDOM_GAP
set buttonsInRandomRow = 0
static if MENU_INCLUDE_RANDOM_PICK then
set buttonsInRandomRow = buttonsInRandomRow + 1
endif
static if MENU_INCLUDE_SUGGEST_RANDOM then
set buttonsInRandomRow = buttonsInRandomRow + 1
endif
if (NUMBER_OF_PAGES > 1 and PAGE_CYCLE_BUTTON_STYLE == "EnvelopRandomButton") then
set buttonsInRandomRow = buttonsInRandomRow + 2
endif
if HERO_SELECTION_ENABLE_DEBUG_MODE and MENU_NUMBER_OF_COLUMNS < buttonsInRandomRow then
call BJDebugMsg("|cffff0000Warning:|r Not enough columns set to accomodate all buttons in the random row...")
endif
endif
static if MENU_INCLUDE_RANDOM_PICK then
if MENU_INCLUDE_SUGGEST_RANDOM then
set xOffset = buttonSpacing/2 * (MENU_NUMBER_OF_COLUMNS - 2) + widthDiff/2
else
set xOffset = buttonSpacing/2 * (MENU_NUMBER_OF_COLUMNS - 1) + widthDiff/2
endif
set heroSelectionButton[RANDOM_HERO] = BlzCreateFrameByType("GLUETEXTBUTTON", "heroRandomButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[RANDOM_HERO] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset - glueTextOffset, currentY + glueTextOffset )
call BlzFrameSetPoint( heroSelectionButton[RANDOM_HERO] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset + MENU_BUTTON_SIZE + glueTextOffset, currentY - MENU_BUTTON_SIZE - glueTextOffset )
call SetButtonFrames(RANDOM_HERO)
call SetButtonTextures(RANDOM_HERO, not PRE_SELECT_BEFORE_ENABLED)
call SetButtonTooltip(RANDOM_HERO, "Select Random", RANDOM_HERO_TOOLTIP)
call BlzTriggerRegisterFrameEvent( heroSelectionButtonTrigger, heroSelectionButton[RANDOM_HERO] , FRAMEEVENT_CONTROL_CLICK )
endif
//Suggest
static if MENU_INCLUDE_SUGGEST_RANDOM then
if MENU_INCLUDE_RANDOM_PICK then
set xOffset = buttonSpacing/2 * MENU_NUMBER_OF_COLUMNS + widthDiff/2
else
set xOffset = buttonSpacing/2 * (MENU_NUMBER_OF_COLUMNS - 1) + widthDiff/2
endif
set heroSelectionButton[SUGGEST_RANDOM] = BlzCreateFrameByType("GLUETEXTBUTTON", "heroRandomButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[SUGGEST_RANDOM] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset - glueTextOffset, currentY + glueTextOffset )
call BlzFrameSetPoint( heroSelectionButton[SUGGEST_RANDOM] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset + MENU_BUTTON_SIZE + glueTextOffset, currentY - MENU_BUTTON_SIZE - glueTextOffset )
call SetButtonFrames(SUGGEST_RANDOM)
call SetButtonTextures(SUGGEST_RANDOM, not PRE_SELECT_BEFORE_ENABLED)
call SetButtonTooltip(SUGGEST_RANDOM, "Suggest Random", SUGGEST_RANDOM_TOOLTIP)
call BlzTriggerRegisterFrameEvent( heroSelectionButtonTrigger, heroSelectionButton[SUGGEST_RANDOM] , FRAMEEVENT_CONTROL_CLICK )
endif
//Set Bottom Corners
set menuHeight = MENU_Y_TOP - (currentY - buttonSpacing - MENU_BOTTOM_EDGE_GAP)
if MENU_BORDER_TILE_SIZE > 0 then
set menuHeight = R2I(menuHeight/MENU_BORDER_TILE_SIZE + 0.99)*MENU_BORDER_TILE_SIZE
endif
call BlzFrameSetPoint(heroSelectionMenu, FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth , MENU_Y_TOP - menuHeight )
//Page Cycle Buttons
if NUMBER_OF_PAGES > 1 then
set pageCycleTrigger = CreateTrigger()
call TriggerAddAction(pageCycleTrigger, function CyclePage)
set pageCycleButtonSpacing = buttonSpacing * MENU_PAGE_CYCLE_SCALE
set pageCycleButtonSize = MENU_BUTTON_SIZE * MENU_PAGE_CYCLE_SCALE
set pageCycleScaleOffset = (buttonSpacing - pageCycleButtonSpacing)/2
if PAGE_CYCLE_BUTTON_STYLE == "EnvelopRandomButton" then
set pageCycleDownX = menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + buttonSpacing/2*(MENU_NUMBER_OF_COLUMNS - buttonsInRandomRow) + widthDiff/2
set pageCycleUpX = menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + buttonSpacing/2*(MENU_NUMBER_OF_COLUMNS + buttonsInRandomRow - 2) + widthDiff/2
set pageCycleDownY = currentY
set pageCycleUpY = currentY
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftRightMenuButton" then
set pageCycleDownX = menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + widthDiff/2
set pageCycleUpX = menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + pageCycleButtonSpacing*(MENU_NUMBER_OF_COLUMNS - 1) + widthDiff/2
set pageCycleDownY = currentY
set pageCycleUpY = currentY
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "BelowRandomButton" then
set pageCycleDownX = MENU_LEFT_RIGHT_EDGE_GAP + buttonSpacing/2*(MENU_NUMBER_OF_COLUMNS - 2) + widthDiff/2
set pageCycleUpX = MENU_LEFT_RIGHT_EDGE_GAP + buttonSpacing/2*MENU_NUMBER_OF_COLUMNS + widthDiff/2
set currentY = currentY - pageCycleButtonSpacing - MENU_HEROES_RANDOM_GAP
set pageCycleDownY = currentY
set pageCycleUpY = currentY
set pageCycleTextType = false
set menuHeight = MENU_Y_TOP - (currentY - buttonSpacing - MENU_BOTTOM_EDGE_GAP)
if MENU_BORDER_TILE_SIZE > 0 then
set menuHeight = R2I(menuHeight/MENU_BORDER_TILE_SIZE + 0.99)*MENU_BORDER_TILE_SIZE
endif
call BlzFrameSetPoint(heroSelectionMenu, FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth , MENU_Y_TOP - menuHeight )
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftRightMiddleButton" then
set pageCycleDownX = menuXLeftLocal - pageCycleButtonSize/2
set pageCycleUpX = menuXLeftLocal + menuWidth - pageCycleButtonSize/2
set pageCycleDownY = MENU_Y_TOP - (menuHeight - pageCycleButtonSize)/2
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "RightVerticalButton" then
set pageCycleDownX = menuXLeftLocal + menuWidth - pageCycleButtonSize/2
set pageCycleUpX = menuXLeftLocal + menuWidth - pageCycleButtonSize/2
set pageCycleDownY = MENU_Y_TOP - menuHeight/2
set pageCycleUpY = MENU_Y_TOP - menuHeight/2 + pageCycleButtonSpacing
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "EnvelopAcceptButton" then
set pageCycleDownX = menuXLeftLocal + menuWidth/2 - MENU_PAGE_CYCLE_SCALE*SELECT_BUTTON_WIDTH - SELECT_BUTTON_WIDTH/2
set pageCycleUpX = menuXLeftLocal + menuWidth/2 + SELECT_BUTTON_WIDTH/2
set pageCycleDownY = MENU_Y_TOP - menuHeight
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = true
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftRightBottomButton" then
set pageCycleDownX = menuXLeftLocal
set pageCycleUpX = menuXLeftLocal + menuWidth - SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE
set pageCycleDownY = MENU_Y_TOP - menuHeight
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = true
elseif PAGE_CYCLE_BUTTON_STYLE == "TangentTopButton" then
set pageCycleDownX = menuXLeftLocal + menuWidth/2 - SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE
set pageCycleUpX = menuXLeftLocal + menuWidth/2
set pageCycleDownY = MENU_Y_TOP - 0.0175
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = true
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftRightTopButton" then
set pageCycleDownX = menuXLeftLocal
set pageCycleUpX = menuXLeftLocal + menuWidth - SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE
set pageCycleDownY = MENU_Y_TOP - 0.0175
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = true
elseif HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Unrecognized page cycle button style (" + PAGE_CYCLE_BUTTON_STYLE + ").")
endif
if pageCycleTextType then
set heroSelectionButton[PAGE_DOWN] = BlzCreateFrameByType("GLUETEXTBUTTON", "pageDownButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[PAGE_DOWN] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleDownX + MENU_PAGE_CYCLE_X_OFFSET , pageCycleDownY + 0.012 + 0.012*SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE + MENU_PAGE_CYCLE_Y_OFFSET )
call BlzFrameSetPoint( heroSelectionButton[PAGE_DOWN] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleDownX + MENU_PAGE_CYCLE_X_OFFSET + SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE , pageCycleDownY - 0.003 - 0.003*SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE + MENU_PAGE_CYCLE_Y_OFFSET )
call BlzFrameSetText( heroSelectionButton[PAGE_DOWN] , "Prev" )
call BlzFrameSetScale( heroSelectionButton[PAGE_DOWN] , SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE )
else
set heroSelectionButton[PAGE_DOWN] = BlzCreateFrameByType("GLUETEXTBUTTON", "pageDownButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[PAGE_DOWN] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleDownX + MENU_PAGE_CYCLE_X_OFFSET - glueTextOffset + pageCycleScaleOffset, pageCycleDownY + glueTextOffset + MENU_PAGE_CYCLE_Y_OFFSET - pageCycleScaleOffset )
call BlzFrameSetPoint( heroSelectionButton[PAGE_DOWN] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleDownX + MENU_PAGE_CYCLE_X_OFFSET + pageCycleButtonSize + glueTextOffset + pageCycleScaleOffset, pageCycleDownY - pageCycleButtonSize - glueTextOffset + MENU_PAGE_CYCLE_Y_OFFSET - pageCycleScaleOffset )
call SetButtonFrames(PAGE_DOWN)
call SetButtonTextures(PAGE_DOWN, not PRE_SELECT_BEFORE_ENABLED)
call SetButtonTooltip(PAGE_DOWN, "Page Down", "Go to the previous page.")
endif
call BlzTriggerRegisterFrameEvent( pageCycleTrigger, heroSelectionButton[PAGE_DOWN] , FRAMEEVENT_CONTROL_CLICK )
if pageCycleTextType then
set heroSelectionButton[PAGE_UP] = BlzCreateFrameByType("GLUETEXTBUTTON", "pageUpButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[PAGE_UP] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleUpX - MENU_PAGE_CYCLE_X_OFFSET , pageCycleUpY + 0.012 + 0.012*SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE + MENU_PAGE_CYCLE_Y_OFFSET )
call BlzFrameSetPoint( heroSelectionButton[PAGE_UP] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleUpX - MENU_PAGE_CYCLE_X_OFFSET + SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE , pageCycleUpY - 0.003 - 0.003*SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE + MENU_PAGE_CYCLE_Y_OFFSET )
call BlzFrameSetText( heroSelectionButton[PAGE_UP] , "Next" )
call BlzFrameSetScale( heroSelectionButton[PAGE_UP] , SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE )
else
set heroSelectionButton[PAGE_UP] = BlzCreateFrameByType("GLUETEXTBUTTON", "pageUpButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[PAGE_UP] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleUpX - MENU_PAGE_CYCLE_X_OFFSET - glueTextOffset + pageCycleScaleOffset , pageCycleUpY + glueTextOffset + MENU_PAGE_CYCLE_Y_OFFSET - pageCycleScaleOffset )
call BlzFrameSetPoint( heroSelectionButton[PAGE_UP] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleUpX - MENU_PAGE_CYCLE_X_OFFSET + pageCycleButtonSize + glueTextOffset + pageCycleScaleOffset , pageCycleUpY - pageCycleButtonSize - glueTextOffset + MENU_PAGE_CYCLE_Y_OFFSET - pageCycleScaleOffset )
call SetButtonFrames(PAGE_UP)
call SetButtonTextures(PAGE_UP, not PRE_SELECT_BEFORE_ENABLED)
call SetButtonTooltip(PAGE_UP, "Page Up", "Go to the next page.")
endif
call BlzTriggerRegisterFrameEvent( pageCycleTrigger, heroSelectionButton[PAGE_UP] , FRAMEEVENT_CONTROL_CLICK )
endif
//Highlight
set heroSelectionButtonHighlight = BlzCreateFrameByType("SPRITE", "SpriteName", heroSelectionMenu, "", 0)
call BlzFrameSetModel(heroSelectionButtonHighlight, "UI\\Feedback\\Autocast\\UI-ModalButtonOn.mdl", 0)
call BlzFrameSetScale(heroSelectionButtonHighlight, MENU_BUTTON_SIZE/0.039)
call BlzFrameSetVisible( heroSelectionButtonHighlight , false )
//Accept
set heroAcceptButton = BlzCreateFrameByType("GLUETEXTBUTTON", "heroAcceptButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroAcceptButton , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth/2 - SELECT_BUTTON_WIDTH/2 , MENU_Y_TOP - menuHeight + 0.012 + 0.012*SELECT_BUTTON_SCALE )
call BlzFrameSetPoint( heroAcceptButton , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth/2 + SELECT_BUTTON_WIDTH/2 , MENU_Y_TOP - menuHeight - 0.003 - 0.003*SELECT_BUTTON_SCALE )
call BlzFrameSetText( heroAcceptButton , SELECT_BUTTON_TEXT )
call BlzFrameSetScale( heroAcceptButton , SELECT_BUTTON_SCALE )
call BlzFrameSetEnable( heroAcceptButton , false )
set trig = CreateTrigger()
call BlzTriggerRegisterFrameEvent( trig , heroAcceptButton , FRAMEEVENT_CONTROL_CLICK )
call TriggerAddAction( trig, function PickHero )
set trig = null
//Ban
set heroBanButton = BlzCreateFrameByType("GLUETEXTBUTTON", "heroBanButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroBanButton , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth/2 - SELECT_BUTTON_WIDTH/2 , MENU_Y_TOP - menuHeight + 0.012 + 0.012*SELECT_BUTTON_SCALE )
call BlzFrameSetPoint( heroBanButton , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth/2 + SELECT_BUTTON_WIDTH/2 , MENU_Y_TOP - menuHeight - 0.003 - 0.003*SELECT_BUTTON_SCALE )
call BlzFrameSetText( heroBanButton , SELECT_BUTTON_TEXT )
call BlzFrameSetText( heroBanButton , "Ban" )
call BlzFrameSetEnable( heroBanButton , false )
call BlzFrameSetVisible( heroBanButton , false)
set trig = CreateTrigger()
call BlzTriggerRegisterFrameEvent( trig , heroBanButton , FRAMEEVENT_CONTROL_CLICK )
call TriggerAddAction( trig, function BanHero )
//Ability Buttons
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
set heroSelectionAbility[i] = BlzCreateFrameByType("BACKDROP", "heroSelectionAbility_" + I2S(i), heroSelectionMenu, "", 0)
static if ABILITY_BUTTON_HORIZONTAL_LAYOUT then
call BlzFrameSetPoint(heroSelectionAbility[i], FRAMEPOINT_TOPLEFT, heroSelectionMenu , FRAMEPOINT_TOPRIGHT, HERO_ABILITY_PREVIEW_BUTTON_X + HERO_ABILITY_PREVIEW_BUTTON_SIZE*(i-1), HERO_ABILITY_PREVIEW_BUTTON_Y)
else
call BlzFrameSetPoint(heroSelectionAbility[i], FRAMEPOINT_TOPLEFT, heroSelectionMenu , FRAMEPOINT_TOPRIGHT, HERO_ABILITY_PREVIEW_BUTTON_X, HERO_ABILITY_PREVIEW_BUTTON_Y - HERO_ABILITY_PREVIEW_BUTTON_SIZE*(i-1))
endif
call BlzFrameSetSize(heroSelectionAbility[i], HERO_ABILITY_PREVIEW_BUTTON_SIZE, HERO_ABILITY_PREVIEW_BUTTON_SIZE)
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
set heroSelectionAbilityHover[i] = BlzCreateFrameByType("FRAME" , "heroIconFrameHover" , heroSelectionAbility[i] , "" , 0)
call BlzFrameSetAllPoints( heroSelectionAbilityHover[i] , heroSelectionAbility[i] )
set heroSelectionAbilityTooltip[i] = BlzCreateFrame("CustomTooltip", heroSelectionAbilityHover[i], 0, 0)
if TOOLTIP_LOCK_TOP then
call BlzFrameSetPoint( heroSelectionAbilityTooltip[i] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , tooltipLeftXLocal , TOOLTIP_Y )
else
call BlzFrameSetPoint( heroSelectionAbilityTooltip[i] , FRAMEPOINT_BOTTOMLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , tooltipLeftXLocal , TOOLTIP_Y )
endif
call BlzFrameSetTooltip( heroSelectionAbilityHover[i] , heroSelectionAbilityTooltip[i] )
call BlzFrameSetSize( heroSelectionAbilityTooltip[i] , TOOLTIP_WIDTH , 0.0 )
set heroSelectionAbilityTooltipTitle[i] = BlzFrameGetChild( heroSelectionAbilityTooltip[i],0)
set heroSelectionAbilityTooltipText[i] = BlzFrameGetChild( heroSelectionAbilityTooltip[i],1)
set i = i + 1
endloop
call BlzFrameSetVisible( heroSelectionMenu, false)
call NoCrash("InitMenu")
endfunction
//==========================================================================================================================================================
//API
//==========================================================================================================================================================
function EveryoneHasHero takes nothing returns boolean
return numPlayersWithHero == numSelectingPlayers
endfunction
function NoOneInHeroSelection takes nothing returns boolean
return numPlayersInSelection == 0
endfunction
function EnablePlayerHeroSelection takes player whichPlayer, boolean enable returns nothing
local integer i
local integer P = GetPlayerId(whichPlayer)
if not isInHeroSelection[P] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to enable hero selection for player who is not in hero selection...")
endif
return
endif
call InitCrashCheck("EnablePlayerHeroSelection")
if enable then
if localPlayerId == P then
call BlzFrameSetVisible(heroSelectionMenu, true)
set i = 1
loop
exitwhen i > PAGE_UP
call SetButtonTextures(i, false)
set i = i + 1
endloop
endif
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
call BlzSetSpecialEffectColor( selectedHero[localPlayerId] , 255 , 255 , 255 )
call BlzSetSpecialEffectAlpha( selectedHero[localPlayerId] , 255 )
endif
else
if localPlayerId == P then
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
set i = i + 1
endloop
set i = 1
loop
exitwhen i > PAGE_UP
call SetButtonTextures(i, true)
set i = i + 1
endloop
call BlzFrameSetEnable(heroAcceptButton, false)
endif
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
call BlzSetSpecialEffectColor( selectedHero[localPlayerId] , 0 , 0 , 0 )
call BlzSetSpecialEffectAlpha( selectedHero[localPlayerId] , 128 )
endif
set preselectedHeroIndex[P] = 0
endif
set heroSelectionDisabledForPlayer[P] = not enable
call NoCrash("EnablePlayerHeroSelection")
endfunction
function EnablePlayerHeroPreselection takes player whichPlayer, boolean enable returns nothing
local integer i
local integer P = GetPlayerId(whichPlayer)
if not isInHeroSelection[P] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to enable hero preselection for player who is not in hero selection...")
endif
return
endif
call InitCrashCheck("EnablePlayerHeroPreselection")
if enable then
if GetLocalPlayer() == whichPlayer then
set i = 1
loop
exitwhen i > PAGE_UP
call SetButtonTextures(i, false)
set i = i + 1
endloop
endif
else
if GetLocalPlayer() == whichPlayer then
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
set i = i + 1
endloop
set i = 1
loop
exitwhen i > PAGE_UP
call SetButtonTextures(i, true)
set i = i + 1
endloop
call BlzFrameSetVisible( heroSelectionButtonHighlight, false)
call BlzFrameSetEnable(heroAcceptButton, false)
endif
call BlzSetSpecialEffectPosition(selectedHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DestroyEffect(selectedHero[P])
call DestroyEffect(selectedHeroGlow[P])
set preselectedHeroIndex[P] = 0
endif
set heroPreselectionDisabledForPlayer[P] = not enable
call NoCrash("EnablePlayerHeroPreselection")
endfunction
function HeroSelectionPlayerForceSelect takes player whichPlayer, Hero whichHero returns nothing
if not isInHeroSelection[GetPlayerId(whichPlayer)] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force select on a player who is not in hero selection...")
endif
return
endif
if playerHasHero[GetPlayerId(whichPlayer)] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force select on a player who already selected a hero...")
endif
return
endif
set isForcedSelect = true
set storePlayerIndex = GetPlayerId(whichPlayer)
set storeHeroIndex = whichHero.index
call PickHero()
set isForcedSelect = false
endfunction
function HeroSelectionPlayerForceRandom takes player whichPlayer returns nothing
if not isInHeroSelection[GetPlayerId(whichPlayer)] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force random on a player who is not in hero selection...")
endif
return
endif
if playerHasHero[GetPlayerId(whichPlayer) + MAX_NUMBER_OF_PLAYERS*playerHeroesNb[GetPlayerId(whichPlayer)]] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force random on a player who already selected a hero...")
endif
return
endif
set isForcedSelect = true
set storePlayerIndex = GetPlayerId(whichPlayer)
set storeHeroIndex = RANDOM_HERO
call PickHero()
set isForcedSelect = false
endfunction
function HeroSelectionAllRandom takes nothing returns nothing
local integer i = 1
local integer j
local integer P
// Corrected callback name -- game was crashing
local noArgCallback onAllRandom = HeroSelectionOnAllRandom
if numPlayersInSelection == 0 then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force all random, but no one is in hero selection right now...")
endif
return
endif
//set storeHeroIndex = RANDOM_HERO
//set isForcedSelect = true
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
// Added loop to support multiple heroes selection
set j = 0
loop
exitwhen j > NUMBER_OF_HEROES_PER_PLAYER -1
if not playerHasHero[P + j * MAX_NUMBER_OF_PLAYERS] then
//set storePlayerIndex = P
//call PickHero()
//Changed to fix crash
call HeroSelectionPlayerForceRandom(Player(P))
endif
set j = j + 1
endloop
set i = i + 1
endloop
call onAllRandom.evaluate()
//set isForcedSelect = false
endfunction
function HeroSelectionForceEnd takes nothing returns nothing
local integer i = 1
local integer j
local integer P
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set j = 0
loop
exitwhen j > NUMBER_OF_HEROES_PER_PLAYER -1
if not playerHasHero[P + j * MAX_NUMBER_OF_PLAYERS] then
call HeroSelectionPlayerForceRandom(Player(P))
endif
set j = j + 1
endloop
set i = i + 1
endloop
endfunction
function PlayerEscapeHeroSelection takes player whichPlayer returns nothing
if not isInHeroSelection[GetPlayerId(whichPlayer)] then
return
endif
call EscapePlayer(whichPlayer)
endfunction
function BanHeroFromSelection takes Hero whichHero, boolean disable returns nothing
call ExecuteBan(whichHero, disable)
endfunction
function HeroSelectionSetTimeRemaining takes real time returns nothing
call TimerStart(countdownTimer, time, false, function TimeExpires)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
call BlzFrameSetVisible(timerFrame, isInHeroSelection[localPlayerId])
endfunction
function HeroSelectionAddTimeRemaining takes real time returns nothing
call TimerStart(countdownTimer, TimerGetRemaining(countdownTimer) + time, false, function TimeExpires)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
call BlzFrameSetVisible(timerFrame, isInHeroSelection[localPlayerId])
endfunction
function EnableHeroSelection takes nothing returns nothing
local integer i
local integer P
local noArgCallback onEnable = HeroSelectionOnEnable
if numPlayersInSelection == 0 then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to enable hero selection, but no one is in hero selection right now...")
endif
return
endif
call InitCrashCheck("EnableHeroSelection")
if TIME_LIMIT != 0 then
call BlzFrameSetVisible(timerFrame, isInHeroSelection[localPlayerId])
call TimerStart(countdownTimer, TIME_LIMIT, false, function TimeExpires)
call TimerStart(countdownUpdateTimer, 1.0, true, function UpdateTimerFrame)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
endif
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if isInHeroSelection[P] then
set heroSelectionDisabledForPlayer[P] = false
set heroPreselectionDisabledForPlayer[P] = false
set playerHasBan[P] = false
if localPlayerId == P then
call BlzFrameSetVisible(heroSelectionMenu, true)
endif
endif
static if PLAYER_TEXT_TAGS then
set backgroundHeroTextTag[P] = CreateTextTag()
call SetTextTagText(backgroundHeroTextTag[P], coloredPlayerName[P] , 0.023)
call SetTextTagPos(backgroundHeroTextTag[P], BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P], 25)
call SetTextTagVisibility(backgroundHeroTextTag[P], true)
endif
set i = i + 1
endloop
call BlzFrameSetVisible(heroAcceptButton, true )
call BlzFrameSetVisible(heroBanButton, false )
call BlzFrameSetEnable( heroBanButton, false )
set captionAlphaMultiplier = 1
if HERO_SELECTION_CAPTION != null then
call BlzFrameSetVisible(captionFrame, isInHeroSelection[localPlayerId])
if CAPTION_COLOR_1 != CAPTION_COLOR_2 or CAPTION_ALPHA_1 != CAPTION_ALPHA_2 then
call AnimateCaption()
endif
endif
if HeroCanBePicked(preselectedHeroIndex[localPlayerId]) then
call BlzFrameSetEnable( heroAcceptButton, true )
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
call BlzSetSpecialEffectColor( selectedHero[localPlayerId] , 255 , 255 , 255 )
call BlzSetSpecialEffectAlpha( selectedHero[localPlayerId] , 255 )
endif
endif
set inBanPhase = false
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
call SetButtonTextures(i, not HeroCanBePicked(i))
set i = i + 1
endloop
call SetButtonTextures(RANDOM_HERO, false)
call SetButtonTextures(SUGGEST_RANDOM, false)
call SetButtonTextures(PAGE_DOWN, false)
call SetButtonTextures(PAGE_UP, false)
call onEnable.evaluate()
call NoCrash("EnableHeroSelection")
endfunction
function StartHeroSelectionTimer takes real timeout, code callback returns nothing
call TimerStart( countdownTimer, timeout, false, callback)
call BlzFrameSetVisible(timerFrame, isInHeroSelection[localPlayerId])
call TimerStart(countdownUpdateTimer, 1.0, true, function UpdateTimerFrame)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
endfunction
function StartHeroBanPhase takes real timeLimit returns nothing
local integer i = 1
local integer P
if numPlayersInSelection == 0 then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to start hero ban phase, but no one is in hero selection right now...")
endif
return
endif
call InitCrashCheck("StartHeroBanPhase")
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set playerHasBan[P] = true
set heroPreselectionDisabledForPlayer[P] = false
if localPlayerId == P then
call BlzFrameSetVisible(heroSelectionMenu, true)
endif
set i = i + 1
endloop
set inBanPhase = true
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
call SetButtonTextures(i, not HeroCanBePicked(i))
set i = i + 1
endloop
call SetButtonTextures(SUGGEST_RANDOM, false)
call SetButtonTextures(PAGE_DOWN, false)
call SetButtonTextures(PAGE_UP, false)
call BlzFrameSetVisible(heroAcceptButton, false)
call BlzFrameSetVisible(heroBanButton, true)
call BlzFrameSetEnable( heroBanButton , HeroCanBePicked(preselectedHeroIndex[localPlayerId]) and preselectedHeroIndex[P] != RANDOM_HERO )
call StartHeroSelectionTimer(timeLimit, function EnableHeroSelection)
call NoCrash("StartHeroBanPhase")
endfunction
function PlayerReturnToHeroSelection takes player whichPlayer returns nothing
local integer i
local integer P = GetPlayerId(whichPlayer)
local integer id
local playerCallback onReturn = HeroSelectionOnReturn
if heroSelectionMenu == null then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to return player to hero selection, but hero selection was not initialized...")
endif
return
endif
call InitCrashCheck("PlayerReturnToHeroSelection")
set playerHasHero[P] = false
set isInHeroSelection[P] = true
set isRepicking[P] = true
set numPlayersWithHero = numPlayersWithHero - 1
set numPlayersInSelection = numPlayersInSelection + 1
set heroIndexWasPicked[pickedHeroIndex[P]] = false
set pickedHeroIndex[P] = 0
// Added
set playerHeroesNb[P] = 0
static if not DELETE_BACKGROUND_HEROES_AFTER_END then
// Added loop to support multiple heroes selection
set i = 0
loop
set i = i + 1
exitwhen i > playerHeroesNb[P]
call BlzSetSpecialEffectPosition(backgroundHero[P + i*MAX_NUMBER_OF_PLAYERS], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DeleteBackgroundHero(P + i*MAX_NUMBER_OF_PLAYERS)
endloop
endif
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
call SetButtonTextures(i, not HeroCanBePicked(Hero.list[i]))
set i = i + 1
endloop
call SetButtonTextures(RANDOM_HERO, false)
call SetButtonTextures(SUGGEST_RANDOM, false)
call SetButtonTextures(PAGE_DOWN, false)
call SetButtonTextures(PAGE_UP, false)
if localPlayerId == P then
call ClearSelection()
call BlzFrameSetVisible(heroSelectionMenu, true)
call BlzFrameSetVisible( heroSelectionButtonHighlight , false )
static if HIDE_GAME_UI then
call BlzHideOriginFrames(true)
call BlzFrameSetAllPoints(BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0))
call BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), false)
endif
endif
static if CREATE_SHADOWS then
call RemoveDestructable(foregroundHeroShadow)
if preselectedHeroIndex[localPlayerId] == 0 or playerHasHero[localPlayerId] then
set id = NO_SHADOW_DESTRUCTABLE_ID
else
set id = SHADOW_DESTRUCTABLE_ID
endif
set foregroundHeroShadow = CreateDestructable(id, localForegroundHeroX, localForegroundHeroY, 0, 1, 0)
endif
static if ENFORCE_CAMERA then
call TimerStart(lockCameraTimer, 0.01, true, function LockSelecterCamera)
endif
call onReturn.evaluate(whichPlayer)
call NoCrash("PlayerReturnToHeroSelection")
endfunction
function BeginHeroSelection takes nothing returns nothing
local integer i = 1
local integer P
local noArgCallback onBegin = HeroSelectionOnBegin
if heroSelectionMenu == null then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to begin hero selection, but hero selection was not initialized...")
endif
return
endif
call InitCrashCheck("BeginHeroSelection")
static if HIDE_GAME_UI then
call BlzHideOriginFrames(true)
call BlzFrameSetAllPoints(BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0))
call BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), false)
endif
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set isInHeroSelection[P] = true
set numPlayersInSelection = numPlayersInSelection + 1
if localPlayerId == P then
call ClearSelection()
endif
set i = i + 1
endloop
static if CREATE_SHADOWS then
set foregroundHeroShadow = CreateDestructable(NO_SHADOW_DESTRUCTABLE_ID, localForegroundHeroX, localForegroundHeroY, 0, 1, 0)
endif
static if SHOW_MENU_BEFORE_ENABLED then
call BlzFrameSetVisible(heroSelectionMenu, true)
endif
call BlzFrameSetVisible( heroSelectionButtonHighlight , false )
static if ENFORCE_CAMERA then
call TimerStart(lockCameraTimer, 0.01, true, function LockSelecterCamera)
endif
call onBegin.evaluate()
call NoCrash("BeginHeroSelection")
endfunction
function RestartHeroSelection takes nothing returns nothing
local integer i = 1
local integer j
local integer P
local noArgCallback onRestart = HeroSelectionOnRestart
if heroSelectionMenu == null then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to restart hero selection, but hero selection was not initialized...")
endif
return
endif
if numPlayersInSelection > 0 then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to restart hero selection while there are players in hero selection...")
endif
return
endif
call InitCrashCheck("RestartHeroSelection")
call onRestart.evaluate()
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set playerHasHero[P] = false
set playerHasBan[P] = false
set isInHeroSelection[P] = false
set heroSelectionDisabledForPlayer[P] = true
set pickedHeroIndex[P] = 0
// Added multiple heroes selection
set playerHeroesNb[P] = 0
static if CREATE_BACKGROUND_HEROES and not DELETE_BACKGROUND_HEROES_AFTER_END then
// Added
set j = 0
loop
exitwhen j > playerHeroesNb[P] -1
call BlzSetSpecialEffectPosition(backgroundHero[P + j*MAX_NUMBER_OF_PLAYERS], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DeleteBackgroundHero(P + j*MAX_NUMBER_OF_PLAYERS)
set j = j+1
endloop
endif
set i = i + 1
endloop
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
set heroIndexWasPicked[i] = false
set heroIndexWasBanned[i] = false
set i = i + 1
endloop
set numPlayersWithHero = 0
set numPlayersInSelection = 0
call NoCrash("RestartHeroSelection")
call BeginHeroSelection()
endfunction
function InitHeroSelection takes nothing returns nothing
local integer i = 0
local integer j
local integer P
local trigger leaveTrigger = CreateTrigger()
local integer array teamSize
local integer array slotInTeam
local location tempLoc
local real array cameraTargetPositionX
local real array cameraTargetPositionY
local real cameraEyePositionX
local real cameraEyePositionY
local string p_name
call InitCrashCheck("InitHeroSelection")
call InitFullScreenParents()
set GARBAGE_DUMP_X = GetRectMaxX(GetPlayableMapRect())
set GARBAGE_DUMP_Y = GetRectMaxY(GetPlayableMapRect())
set localPlayerId = GetPlayerId(GetLocalPlayer())
call HeroSelectionInitPlayers()
call HeroSelectionSetPlayerColors()
//Player List
set i = 0
loop
exitwhen i > 23
static if not AUTO_SET_SELECTING_PLAYERS then
if PLAYER_SELECTS_HERO[i] and GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
set numSelectingPlayers = numSelectingPlayers + 1
set playerNumberOfSlot[numSelectingPlayers] = i
set p_name = GetPlayerName(Player(i))
set coloredPlayerName[i] = TruncatedPlayerName(Player(i))
set heroPreselectionDisabledForPlayer[i] = not PRE_SELECT_BEFORE_ENABLED
set heroSelectionDisabledForPlayer[i] = true
if GetPlayerController(Player(i)) == MAP_CONTROL_USER then
set playerIsHuman[i] = true
set numSelectingHumans = numSelectingHumans + 1
call TriggerRegisterPlayerEvent(leaveTrigger, Player(i), EVENT_PLAYER_LEAVE)
endif
endif
else
if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i)) == MAP_CONTROL_USER then
set numSelectingPlayers = numSelectingPlayers + 1
set playerNumberOfSlot[numSelectingPlayers] = i
set coloredPlayerName[i] = PLAYER_COLOR[i] + GetPlayerName(Player(i)) + "|r"
set heroPreselectionDisabledForPlayer[i] = not PRE_SELECT_BEFORE_ENABLED
set heroSelectionDisabledForPlayer[i] = true
set playerIsHuman[i] = true
set numSelectingHumans = numSelectingHumans + 1
call TriggerRegisterPlayerEvent(leaveTrigger, Player(i), EVENT_PLAYER_LEAVE)
endif
endif
set i = i + 1
endloop
set NUMBER_OF_TEAMS = 0
set i = 1
loop
exitwhen i > numSelectingPlayers
set NUMBER_OF_TEAMS = IMaxBJ(NUMBER_OF_TEAMS, TEAM_OF_PLAYER[playerNumberOfSlot[i]])
set i = i + 1
endloop
//Foreground hero locations are asynchronous. Background hero locations are not.
static if SEPARATE_LOCATIONS_FOR_EACH_TEAM then
call HeroSelectionInitArrays()
set localForegroundHeroX = TEAM_FOREGROUND_HERO_X[TEAM_OF_PLAYER[localPlayerId]]
set localForegroundHeroY = TEAM_FOREGROUND_HERO_Y[TEAM_OF_PLAYER[localPlayerId]]
set localHeroSelectionAngle = TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[localPlayerId]]
set i = 1
loop
exitwhen i > NUMBER_OF_TEAMS
set cameraTargetPositionX[i] = TEAM_FOREGROUND_HERO_X[i] + Cos(Deg2Rad(TEAM_FOREGROUND_HERO_X[i]))*CAMERA_TARGET_OFFSET + Sin(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[i]))*CAMERA_PERPENDICULAR_OFFSET
set cameraTargetPositionY[i] = TEAM_FOREGROUND_HERO_Y[i] + Sin(Deg2Rad(TEAM_FOREGROUND_HERO_Y[i]))*CAMERA_TARGET_OFFSET - Cos(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[i]))*CAMERA_PERPENDICULAR_OFFSET
set i = i + 1
endloop
else
set localForegroundHeroX = FOREGROUND_HERO_X
set localForegroundHeroY = FOREGROUND_HERO_Y
set localHeroSelectionAngle = HERO_SELECTION_ANGLE
set i = 1
loop
exitwhen i > NUMBER_OF_TEAMS
set cameraTargetPositionX[i] = FOREGROUND_HERO_X + Cos(Deg2Rad(localHeroSelectionAngle))*CAMERA_TARGET_OFFSET + Sin(Deg2Rad(HERO_SELECTION_ANGLE))*CAMERA_PERPENDICULAR_OFFSET
set cameraTargetPositionY[i] = FOREGROUND_HERO_Y + Sin(Deg2Rad(localHeroSelectionAngle))*CAMERA_TARGET_OFFSET - Cos(Deg2Rad(HERO_SELECTION_ANGLE))*CAMERA_PERPENDICULAR_OFFSET
set i = i + 1
endloop
endif
call TriggerAddAction(leaveTrigger, function OnPlayerLeave)
call SetCategories()
call HeroDeclaration()
set NUMBER_OF_HEROES = Hero.numHeroes
set RANDOM_HERO = NUMBER_OF_HEROES + 1
set SUGGEST_RANDOM = NUMBER_OF_HEROES + 2
set PAGE_DOWN = NUMBER_OF_HEROES + 3
set PAGE_UP = NUMBER_OF_HEROES + 4
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
set j = 0
loop
set j = j + 1
exitwhen Hero.list[i].abilities[j] == 0
endloop
if j - 1 > NUMBER_OF_ABILITY_FRAMES then
set NUMBER_OF_ABILITY_FRAMES = j - 1
endif
if Hero.list[i].category > NUMBER_OF_CATEGORIES then
set NUMBER_OF_CATEGORIES = Hero.list[i].category
endif
call Hero.list[i].GetValues()
set i = i + 1
endloop
set i = 1
loop
exitwhen i > NUMBER_OF_CATEGORIES
set NUMBER_OF_PAGES = IMaxBJ(NUMBER_OF_PAGES, PAGE_OF_CATEGORY[i])
set i = i + 1
endloop
//Camera
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_ZOFFSET , CAMERA_Z_OFFSET , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_ANGLE_OF_ATTACK , 360 - CAMERA_PITCH , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_TARGET_DISTANCE , CAMERA_DISTANCE , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_ROLL , 0.0 , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_FIELD_OF_VIEW , CAMERA_FIELD_OF_VIEW , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_FARZ , 7500.0 , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_ROTATION , localHeroSelectionAngle , 0.0)
call CameraSetupSetDestPosition(heroSelectionCamera , cameraTargetPositionX[TEAM_OF_PLAYER[localPlayerId]] , cameraTargetPositionY[TEAM_OF_PLAYER[localPlayerId]] , 0.0)
//Background Heroes
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set teamSize[TEAM_OF_PLAYER[P]] = teamSize[TEAM_OF_PLAYER[P]] + 1
set slotInTeam[P] = teamSize[TEAM_OF_PLAYER[P]]
set i = i + 1
endloop
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
// loop added for multiple heroes selection
set j = 0
loop
exitwhen j > NUMBER_OF_HEROES_PER_PLAYER - 1
set tempLoc = GetPlayerBackgroundHeroLocation(P + j * MAX_NUMBER_OF_PLAYERS, i, numSelectingPlayers, TEAM_OF_PLAYER[P], slotInTeam[P], teamSize[TEAM_OF_PLAYER[P]])
static if SEPARATE_LOCATIONS_FOR_EACH_TEAM then
set cameraEyePositionX = cameraTargetPositionX[TEAM_OF_PLAYER[P]] - Cos(Deg2Rad(CAMERA_PITCH))*Cos(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*CAMERA_DISTANCE
set cameraEyePositionY = cameraTargetPositionY[TEAM_OF_PLAYER[P]] - Cos(Deg2Rad(CAMERA_PITCH))*Sin(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*CAMERA_DISTANCE
set BACKGROUND_HERO_X[P + j * MAX_NUMBER_OF_PLAYERS] = cameraEyePositionX - Sin(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*GetLocationX(tempLoc) + Cos(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*GetLocationY(tempLoc)
set BACKGROUND_HERO_Y[P + j * MAX_NUMBER_OF_PLAYERS] = cameraEyePositionY + Cos(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*GetLocationX(tempLoc) + Sin(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*GetLocationY(tempLoc)
else
set cameraEyePositionX = cameraTargetPositionX[TEAM_OF_PLAYER[P]] - Cos(Deg2Rad(CAMERA_PITCH))*Cos(Deg2Rad(HERO_SELECTION_ANGLE))*CAMERA_DISTANCE
set cameraEyePositionY = cameraTargetPositionY[TEAM_OF_PLAYER[P]] - Cos(Deg2Rad(CAMERA_PITCH))*Sin(Deg2Rad(HERO_SELECTION_ANGLE))*CAMERA_DISTANCE
set BACKGROUND_HERO_X[P + j * MAX_NUMBER_OF_PLAYERS] = cameraEyePositionX - Sin(Deg2Rad(HERO_SELECTION_ANGLE))*GetLocationX(tempLoc) + Cos(Deg2Rad(HERO_SELECTION_ANGLE))*GetLocationY(tempLoc)
set BACKGROUND_HERO_Y[P + j * MAX_NUMBER_OF_PLAYERS] = cameraEyePositionY + Cos(Deg2Rad(HERO_SELECTION_ANGLE))*GetLocationX(tempLoc) + Sin(Deg2Rad(HERO_SELECTION_ANGLE))*GetLocationY(tempLoc)
endif
call RemoveLocation(tempLoc)
set j = j + 1
endloop
set i = i + 1
endloop
set tempLoc = null
//Caption
set captionFrame = BlzCreateFrameByType("TEXT", "captionFrame", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
call BlzFrameSetVisible(captionFrame , false)
call BlzFrameSetEnable(captionFrame , false)
call BlzFrameSetSize(captionFrame, 0.4, 0.05)
call BlzFrameSetAbsPoint(captionFrame , FRAMEPOINT_CENTER , CAPTION_X , CAPTION_Y)
call BlzFrameSetText(captionFrame, CAPTION_COLOR_1 + HERO_SELECTION_CAPTION + "|r")
call BlzFrameSetTextAlignment(captionFrame , TEXT_JUSTIFY_MIDDLE , TEXT_JUSTIFY_CENTER)
call BlzFrameSetScale(captionFrame, CAPTION_FONT_SIZE/10.)
call BlzFrameSetAlpha(captionFrame , CAPTION_ALPHA_1)
//Timer
set timerFrame = BlzCreateFrameByType("TEXT", "timerFrame", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
call BlzFrameSetVisible(timerFrame , false)
call BlzFrameSetEnable(timerFrame , false)
call BlzFrameSetSize(timerFrame, 0.15, 0.05)
call BlzFrameSetAbsPoint(timerFrame , FRAMEPOINT_CENTER , TIMER_X , TIMER_Y)
call BlzFrameSetTextAlignment(timerFrame , TEXT_JUSTIFY_MIDDLE , TEXT_JUSTIFY_CENTER)
call BlzFrameSetScale(timerFrame, TIMER_FONT_SIZE/10.)
call NoCrash("InitHeroSelection")
call InitMenu()
endfunction
endlibrary
library XHSSelection initializer Init requires HeroSelection
globals
private integer loopInt = 0
constant real HERO_SPAWN_X = -930
constant real HERO_SPAWN_Y = -9000
constant real SECONDARY_HERO_SPAWN_X = -4600
constant real SECONDARY_HERO_SPAWN_Y = -5900
constant integer PLAYER_GOLD_EASY_NORMAL = 100000
constant integer PLAYER_GOLD_EASY_RANDOM = 125000
constant integer PLAYER_GOLD_NORMAL = 1000
constant integer PLAYER_GOLD_RANDOM = 5000
NeatWindow centerWindow
effect array circles
endglobals
private function MusicFadeOutLoop takes nothing returns nothing
set loopInt = loopInt + 1
if loopInt < 100 then
call SetMusicVolumeBJ(100 - loopInt)
else
call DestroyTimer(GetExpiredTimer())
call ClearMapMusic()
call StopMusic(false)
call SetMusicVolumeBJ(100)
endif
endfunction
function MusicSlowFadeOut takes nothing returns nothing
call TimerStart(CreateTimer(), 0.05, true , function MusicFadeOutLoop)
set loopInt = 0
endfunction
function BeginGame takes nothing returns nothing
call PlayMusic("Sound\\Music\\mp3Music\\NightElf3.mp3")
call DisplayTextToForce(GetPlayersAll(), "Let the battle begin!")
endfunction
private function Init takes nothing returns nothing
// local trigger trig = CreateTrigger()
// call TriggerAddAction(trig, function Vendor)
// call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_PICKUP_ITEM)
endfunction
struct XHS_Selection
static method InitSelection takes nothing returns nothing
call InitHeroSelection()
endmethod
static method Start takes nothing returns nothing
call SetTimeOfDay(0)
call SetTimeOfDayScale(0)
call SetSkyModel("Starsphere.mdx")
call SetDoodadAnimationRect(GetPlayableMapRect(), 'D00O', "stand lumber", true)
set centerWindow = NeatWindow.create(TEXT_MESSAGE_X_POSITION, 0.22, TEXT_MESSAGE_BLOCK_WIDTH, 0.2, 1, false)
//call InitHeroSelection()
call BeginHeroSelection()
endmethod
static method Enable takes nothing returns nothing
set HERO_CAN_BE_PICKED_MULTIPLE_TIMES = udg_ModeSame
if udg_ModeDual then
set NUMBER_OF_HEROES_PER_PLAYER = 2
else
set NUMBER_OF_HEROES_PER_PLAYER = 1
endif
call EnableHeroSelection()
if udg_ModeRandom then
call HeroSelectionAllRandom()
endif
endmethod
endstruct
endlibrary
library PlayerNameDisplay initializer init
globals
string array players_color
endglobals
private function init takes nothing returns nothing
// Init variables
set players_color[0] = "|cffff0402"
set players_color[1] = "|cff1052ff"
set players_color[2] = "|cff1BE6BA"
set players_color[3] = "|cff8530b1"
set players_color[4] = "|cfffffc00"
set players_color[5] = "|cffff8a0d"
set players_color[6] = "|cff20bf00"
set players_color[7] = "|cffE35BAF"
set players_color[8] = "|cff949697"
set players_color[9] = "|cff7EBFF1"
set players_color[10] = "|cff106247"
set players_color[11] = "|cff4F2B05"
set players_color[12] = "|cff9C0000"
set players_color[13] = "|cff0000C2"
set players_color[14] = "|cff00EBEB"
set players_color[15] = "|cffBE00FF"
set players_color[16] = "|cffECCC86"
set players_color[17] = "|cffF7A48B"
set players_color[18] = "|cffBFFF80"
set players_color[19] = "|cffDBB8EC"
set players_color[20] = "|cff4F4F55"
set players_color[21] = "|cffECF0FF"
set players_color[22] = "|cff00781E"
set players_color[23] = "|cffA46F34"
endfunction
private function FindCharacter takes string whichstring, string character returns integer
local integer length = StringLength(whichstring)
local integer i = 0
loop
exitwhen i > length
if(SubString(whichstring, i -1, i) == character)then
return i
endif
set i = i +1
endloop
return -1
endfunction
function TruncatedPlayerName takes player whichplayer returns string
local integer hashtag_pos
local string name = GetPlayerName(whichplayer)
set hashtag_pos = FindCharacter(name, "#")
if hashtag_pos == -1 then
return players_color[GetPlayerId(whichplayer)] + name + "|r"
endif
return players_color[GetPlayerId(whichplayer)] + SubString(name, 0, hashtag_pos -1) + "|r"
endfunction
endlibrary
////////////////////////////////////////////////////////////////////
// Boss Ultimate Spellpack Channel V1.02 //
// Author: Tank-Commander //
// Purpose: Handles spell channelling within the spellpack //
// Used for: Soul Release, Scathe, Sheer Force, Energy Spike //
// //
// Notes: //
// - Read the readme before you try modifying the config //
// - Use the "Helpful files" to help you import the system //
// //
// Credits: //
// - (Dummy.mdl) Vexorian //
// //
// If you have used this spellpack in your map, you are required //
// to give credits to Tank-Commander for the creation of it //
// If you would like to use snippets of code from this for //
// whatever, getting permission and crediting the source/linking //
// would be much appreciated. //
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// README: //
// Before modifying this spell a few things need to be //
// understood and read, this is one of those things, while //
// most modification can be considered intuitive, it still //
// helps to read through these intstructions, as they will //
// inform you about how to configure this spell to your //
// desire. //
//----------------------------------------------------------------//
// Initial importing: The variable creator trigger can be //
// imported first and if you have the correct settings (file, //
// preferences, General, automatically create unknown variables //
// checked, then when you paste in the variable creator it //
// will automatically give you all the variables you need for //
// this spell //
// //
// While the remaining object editor based data is not required //
// to function (provided they're replaced with equivelents) //
// it's recommended that they are also imported, if their data //
// value are not the same as listed in the configuration, those //
// configurables will need to be changed to work correctly //
//----------------------------------------------------------------//
// CONFIGURABLE INFORMATION/HELP: //
// //
// - Viewing data values: To see data values in the editor you //
// need to press Ctrl + D, to shift back to normal viewing //
// press it again //
// //
// - Effects: Pathnames for effects used in the spells should //
// have two "\"s throughout or the effect will not work (the //
// WE progress bar will not go away when saving, however if //
// fixed afterwards the save will still work, but the progress //
// bar will still remain until the WE is closed) //
// e.g. "units\\human\\Footman\\Footman" //
// //
// - Effect Scaling: Some effects have scale values below them //
// the scale determines the size of the effect and is expressed //
// as a real percentage (1.00 = 100%) //
// //
// - Removing Effects: to remove an effect you don't want from //
// the ability, set the model path to that of Dummy.mdl //
// //
// - Timer: Some configurables have PerSecond values, the code //
// automatically accounts for changes to the timer as to //
// maintain consistency with what the user has chosen //
// All times in the system are expressions of seconds //
// //
//----------------------------------------------------------------//
// TimerSpeed: This is the amount of time in seconds between //
// each iteration of the channel loop function //
constant function BUSCR_TimerSpeed takes nothing returns real
return 0.031250000
endfunction
//----------------------------------------------------------------//
// DummyId: This is the raw data value of the dummy unit used //
// for creating the channeling effects it's advised this unit //
// uses Dummy.mdl for optimal usage, have death type set to //
// "can't raise does not decay" and a death time long enough //
// to play all effects (about 5.00 should be long enough) for //
// all effects) //
constant function BUSCR_DummyId takes nothing returns integer
return 'N012'
endfunction
//----------------------------------------------------------------//
// AttachmentPoint: This is the location on the dummy unit where //
// effects will be placed //
constant function BUSCR_AttachmentPoint takes nothing returns string
return "origin"
endfunction
//----------------------------------------------------------------//
// GroundMinAngle: This is the minimum angle off the floor that //
// all effects are made when the caster is a ground unit //
constant function BUSCR_GroundMinAngle takes nothing returns real
return 0.1
endfunction
//----------------------------------------------------------------//
// DummyPlayer: This is the player that is given ownership of //
// all the dummy units used by the system //
constant function BUSCR_DummyPlayer takes nothing returns player
return Player(10)
endfunction
//----------------------------------------------------------------//
// END OF CONFIGURATION //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// RegisterChannel: This is the function used to register a new //
// type of channel (calling this function after setting up the //
// new channel will register it and allowit to be used) //
////////////////////////////////////////////////////////////////////
function BUS_RegisterChannel takes integer target returns nothing
local integer Id
if (target == 0) then
set udg_BUSCR_Counter = udg_BUSCR_Counter + 1
set Id = udg_BUSCR_Counter
else
set Id = target
endif
set udg_BUSCR_Name[Id] = StringCase(udg_BUSCR_Name[0], true)
set udg_BUSCR_Effect[Id] = udg_BUSCR_Effect[0]
set udg_BUSCR_MinAOE[Id] = udg_BUSCR_MinAOE[0]
set udg_BUSCR_MaxAOE[Id] = udg_BUSCR_MaxAOE[0]
set udg_BUSCR_AbsorbAOE[Id] = udg_BUSCR_AbsorbAOE[0]
set udg_BUSCR_MinSize[Id] = udg_BUSCR_MinSize[0]
set udg_BUSCR_MaxSize[Id] = udg_BUSCR_MaxSize[0]
set udg_BUSCR_SpawnRate[Id] = udg_BUSCR_SpawnRate[0]
set udg_BUSCR_SpawnCount[Id] = udg_BUSCR_SpawnCount[0]
set udg_BUSCR_Power[Id] = udg_BUSCR_Power[0]
set udg_BUSCR_HueRed[Id] = udg_BUSCR_HueRed[0]
set udg_BUSCR_HueGreen[Id] = udg_BUSCR_HueGreen[0]
set udg_BUSCR_HueBlue[Id] = udg_BUSCR_HueBlue[0]
set udg_BUSCR_HueAlpha[Id] = udg_BUSCR_HueAlpha[0]
set udg_BUSCR_HueSpeed[Id] = udg_BUSCR_HueSpeed[0]
endfunction
////////////////////////////////////////////////////////////////////
// GetChannelByName: This returns the Id number of the channel //
// which shares the name with the passed parameter, it can be //
// used to consistently refer to a channel without knowing the //
// order by which it was declared (allowing dynamic introduction //
// of channels) //
////////////////////////////////////////////////////////////////////
function BUS_GetChannelByName takes string name returns integer
local integer iLoop = 0
set name = StringCase(name, true)
loop
set iLoop = iLoop + 1
exitwhen iLoop > udg_BUSCR_Counter
if (name == udg_BUSCR_Name[iLoop]) then
return iLoop
endif
endloop
return 0
endfunction
////////////////////////////////////////////////////////////////////
// GetZ: Used to get the Z height of a given location, used to //
// make sure that entities going over cliffs aren't incorrectly //
// moved/placed on the z plane //
////////////////////////////////////////////////////////////////////
function BUS_GetZ takes real x, real y returns real
call MoveLocation(udg_BUS_Loc, x, y)
return GetLocationZ(udg_BUS_Loc)
endfunction
////////////////////////////////////////////////////////////////////
// Function used to prevent two casts of the same channel being //
// done simultaneously //
////////////////////////////////////////////////////////////////////
function BUS_CheckChannel takes unit u returns integer
local integer Node = 0
loop
set Node = udg_BUSC_NextNode[Node]
exitwhen (udg_BUSC_Unit[Node] == u) or (Node == 0)
endloop
return Node
endfunction
////////////////////////////////////////////////////////////////////
// ChannelLoop: The function used to control the channel this //
// includes creating entities and moving them toward the caster //
// checking if they are still casting the channel and activating //
// the channel effects when it is completed //
////////////////////////////////////////////////////////////////////
function BUS_ChannelLoop takes nothing returns nothing
local integer Node = 0
local integer TempNode = 0
local integer iLoop = 0
local integer EffectCount = 0
local real x
local real x2
local real x3
local real y
local real y2
local real y3
local real z
local real z2
local real dy
local real dx
local real Angle
local real Angle2
local real Distance
local real TempReal
loop
exitwhen TempNode == 0
set TempNode = udg_BUSC_PNextNode[TempNode]
endloop
loop
set Node = udg_BUSC_NextNode[Node]
exitwhen Node == 0
set x = GetUnitX(udg_BUSC_Unit[Node])
set y = GetUnitY(udg_BUSC_Unit[Node])
set z = BUS_GetZ(x, y) + GetUnitFlyHeight(udg_BUSC_Unit[Node])
set TempNode = 0
if (udg_BUSC_CDuration[Node] > 0) then
set udg_BUSC_CDuration[Node] = udg_BUSC_CDuration[Node] - BUSCR_TimerSpeed()
if (udg_BUSC_CDelay[Node] <= BUSCR_TimerSpeed()) then
set udg_BUSC_CDelay[Node] = udg_BUSC_Delay[Node]
set iLoop = 0
loop
set iLoop = iLoop + 1
exitwhen iLoop > udg_BUSCR_SpawnCount[udg_BUSC_ChannelId[Node]]
if (udg_BUSC_PRecyclableNodes == 0) then
set udg_BUSC_PNodeNumber = udg_BUSC_PNodeNumber + 1
set TempNode = udg_BUSC_PNodeNumber
else
set udg_BUSC_PRecyclableNodes = udg_BUSC_PRecyclableNodes - 1
set TempNode = udg_BUSC_PRecycleNodes[udg_BUSC_PRecyclableNodes]
endif
set udg_BUSC_PNextNode[TempNode] = 0
set udg_BUSC_PNextNode[udg_BUSC_PPrevNode[0]] = TempNode
set udg_BUSC_PPrevNode[TempNode] = udg_BUSC_PPrevNode[0]
set udg_BUSC_PPrevNode[0] = TempNode
set Angle = GetRandomReal(0, bj_PI * 2)
if (IsUnitType(udg_BUSC_Unit[Node], UNIT_TYPE_GROUND)) then
set Angle2 = GetRandomReal(BUSCR_GroundMinAngle(), bj_PI - BUSCR_GroundMinAngle())
else
set Angle2 = GetRandomReal(-bj_PI, bj_PI)
endif
set TempReal = GetRandomReal(udg_BUSCR_MinAOE[udg_BUSC_ChannelId[Node]], udg_BUSCR_MaxAOE[udg_BUSC_ChannelId[Node]])
set Distance = Cos(Angle2) * TempReal
if Distance < 0 then
set Distance = Distance * -1
endif
set x2 = x + Distance * Cos(Angle)
set y2 = y + Distance * Sin(Angle)
set z2 = Sin(Angle2) * TempReal + z
set TempReal = GetRandomReal(udg_BUSCR_MinSize[udg_BUSC_ChannelId[Node]], udg_BUSCR_MaxSize[udg_BUSC_ChannelId[Node]])
set udg_BUSC_PCaster[TempNode] = udg_BUSC_Unit[Node]
set udg_BUSC_PXVelocity[TempNode] = 0
set udg_BUSC_PYVelocity[TempNode] = 0
set udg_BUSC_PZVelocity[TempNode] = 0
set udg_BUSC_PCurrentZ[TempNode] = z2
set udg_BUSC_PUnit[TempNode] = CreateUnit(BUSCR_DummyPlayer(), BUSCR_DummyId(), x2, y2, Angle * bj_RADTODEG + 180)
if UnitAddAbility(udg_BUSC_PUnit[TempNode], 'Amrf') and UnitRemoveAbility(udg_BUSC_PUnit[TempNode], 'Amrf') then
endif
set udg_BUSC_PCurrentEffect[TempNode] = AddSpecialEffectTarget(udg_BUSCR_Effect[udg_BUSC_ChannelId[Node]], udg_BUSC_PUnit[TempNode], BUSCR_AttachmentPoint())
call SetUnitScale(udg_BUSC_PUnit[TempNode], TempReal, 0.00, 0.00)
call SetUnitFlyHeight(udg_BUSC_PUnit[TempNode], udg_BUSC_PCurrentZ[TempNode] - BUS_GetZ(x2,y2), 0.00)
endloop
else
set udg_BUSC_CDelay[Node] = udg_BUSC_CDelay[Node] - BUSCR_TimerSpeed()
endif
if (udg_BUSC_HueChange[Node]) then
set udg_BUSC_CHueRed[Node] = udg_BUSC_CHueRed[Node] + udg_BUSC_HueChangeRed[Node]
set udg_BUSC_CHueGreen[Node] = udg_BUSC_CHueGreen[Node] + udg_BUSC_HueChangeGreen[Node]
set udg_BUSC_CHueBlue[Node] = udg_BUSC_CHueBlue[Node] + udg_BUSC_HueChangeBlue[Node]
set udg_BUSC_CHueAlpha[Node] = udg_BUSC_CHueAlpha[Node] + udg_BUSC_HueChangeAlpha[Node]
call SetUnitVertexColor(udg_BUSC_Unit[Node], udg_BUSC_CHueRed[Node], udg_BUSC_CHueGreen[Node], udg_BUSC_CHueBlue[Node], udg_BUSC_CHueAlpha[Node])
if (udg_BUSC_CChangeHueDelay[Node] < 0) then
set udg_BUSC_CChangeHueDelay[Node] = udg_BUSCR_HueSpeed[udg_BUSC_ChannelId[Node]]
set udg_BUSC_HueChangeRed[Node] = udg_BUSC_HueChangeRed[Node] * -1
set udg_BUSC_HueChangeGreen[Node] = udg_BUSC_HueChangeGreen[Node] * -1
set udg_BUSC_HueChangeBlue[Node] = udg_BUSC_HueChangeBlue[Node] * -1
set udg_BUSC_HueChangeAlpha[Node] = udg_BUSC_HueChangeAlpha[Node] * - 1
else
set udg_BUSC_CChangeHueDelay[Node] = udg_BUSC_CChangeHueDelay[Node] - BUSCR_TimerSpeed()
endif
endif
elseif (udg_BUSC_Running[Node]) then
set udg_BUS_Unit = udg_BUSC_Unit[Node]
set udg_BUS_Target = udg_BUSC_Target[Node]
set udg_BUS_X = udg_BUSC_TargetX[Node]
set udg_BUS_Y = udg_BUSC_TargetY[Node]
set udg_BUSC_Running[Node] = false
set udg_BUSC_Event = 0
set udg_BUSC_Event = udg_BUSC_Ability[Node]
endif
set TempNode = 0
set EffectCount = 0
loop
set TempNode = udg_BUSC_PNextNode[TempNode]
exitwhen TempNode == 0
if (udg_BUSC_PCaster[TempNode] == udg_BUSC_Unit[Node]) then
set x2 = GetUnitX(udg_BUSC_PUnit[TempNode])
set y2 = GetUnitY(udg_BUSC_PUnit[TempNode])
set z2 = BUS_GetZ(x2, y2) + GetUnitFlyHeight(udg_BUSC_PUnit[TempNode])
set Distance = SquareRoot((x - x2) * (x - x2) + (y - y2) * (y - y2) + (z - z2) * (z - z2))
if ((Distance < udg_BUSCR_AbsorbAOE[udg_BUSC_ChannelId[Node]]) or (not(GetUnitCurrentOrder(udg_BUSC_Unit[Node]) == udg_BUSC_Order[Node]) or udg_BUSC_Cancel[Node])) then
call DestroyEffect(udg_BUSC_PCurrentEffect[TempNode])
call KillUnit(udg_BUSC_PUnit[TempNode])
set udg_BUSC_PRecycleNodes[udg_BUSC_PRecyclableNodes] = TempNode
set udg_BUSC_PRecyclableNodes = udg_BUSC_PRecyclableNodes + 1
set udg_BUSC_PNextNode[udg_BUSC_PPrevNode[TempNode]] = udg_BUSC_PNextNode[TempNode]
set udg_BUSC_PPrevNode[udg_BUSC_PNextNode[TempNode]] = udg_BUSC_PPrevNode[TempNode]
if (udg_BUSC_NextNode[0] + udg_BUSC_NextNode[0] == 0) then
call PauseTimer(udg_BUSC_Timer)
endif
else
set EffectCount = EffectCount + 1
set dy = y - y2
set dx = x - x2
set Angle = Atan2(dy, dx)
set Angle2 = Atan2(z - z2, SquareRoot((dx * dx) + (dy * dy)))
set TempReal = udg_BUSCR_Power[udg_BUSC_ChannelId[Node]] / Distance
set udg_BUSC_PZVelocity[TempNode] = udg_BUSC_PZVelocity[TempNode] + TempReal * Sin(Angle2)
set udg_BUSC_PXVelocity[TempNode] = udg_BUSC_PXVelocity[TempNode] + TempReal * Cos(Angle) * Cos(Angle2)
set udg_BUSC_PYVelocity[TempNode] = udg_BUSC_PYVelocity[TempNode] + TempReal * Sin(Angle) * Cos(Angle2)
set udg_BUSC_PCurrentZ[TempNode] = udg_BUSC_PCurrentZ[TempNode] + udg_BUSC_PZVelocity[TempNode]
set x3 = x2 + udg_BUSC_PXVelocity[TempNode]
set y3 = y2 + udg_BUSC_PYVelocity[TempNode]
if ((udg_BUS_MapMinX <= x3) and (x3 <= udg_BUS_MapMaxX) and (udg_BUS_MapMinY <= y3)and (y3 <= udg_BUS_MapMaxY)) then
call SetUnitX(udg_BUSC_PUnit[TempNode], x3)
call SetUnitY(udg_BUSC_PUnit[TempNode], y3)
endif
call SetUnitFlyHeight(udg_BUSC_PUnit[TempNode], udg_BUSC_PCurrentZ[TempNode] - BUS_GetZ(x3,y3), 0.00)
call SetUnitAnimationByIndex(udg_BUSC_PUnit[TempNode], R2I(Atan2(udg_BUSC_PZVelocity[TempNode], SquareRoot((udg_BUSC_PXVelocity[TempNode] * udg_BUSC_PXVelocity[TempNode]) + (udg_BUSC_PYVelocity[TempNode] * udg_BUSC_PYVelocity[TempNode]))) * bj_RADTODEG + 0.5) + 90)
endif
endif
endloop
if (EffectCount == 0) and ((udg_BUSC_CDuration[Node] <= 0) or not(GetUnitCurrentOrder(udg_BUSC_Unit[Node]) == udg_BUSC_Order[Node] or udg_BUSC_Cancel[Node])) then
if (udg_BUSC_HueChange[Node]) then
call SetUnitVertexColor(udg_BUSC_Unit[Node], 255, 255, 255, 255)
endif
set udg_BUSC_RecycleNodes[udg_BUSC_RecyclableNodes] = Node
set udg_BUSC_RecyclableNodes = udg_BUSC_RecyclableNodes + 1
set udg_BUSC_NextNode[udg_BUSC_PrevNode[Node]] = udg_BUSC_NextNode[Node]
set udg_BUSC_PrevNode[udg_BUSC_NextNode[Node]] = udg_BUSC_PrevNode[Node]
if (udg_BUSC_NextNode[0] + udg_BUSC_NextNode[0] == 0) then
call PauseTimer(udg_BUSC_Timer)
endif
endif
endloop
endfunction
////////////////////////////////////////////////////////////////////
// StartChannel: Used to start a channeling effect on a spell //
// Calling this function, passing the ability Id, the unit, any //
// target unit or point, the duration of the channel and the //
// duration of the channel (as well as if the channel will //
// include a hue change) //
////////////////////////////////////////////////////////////////////
function BUS_StartChannel takes integer Id, unit u, unit Target, real x, real y, real Duration, real Event, boolean Hue returns nothing
local integer Node = 0
local real TempReal = 0
set udg_BUSC_Cancel[BUS_CheckChannel(u)] = true
if (udg_BUSC_RecyclableNodes == 0) then
set udg_BUSC_NodeNumber = udg_BUSC_NodeNumber + 1
set Node = udg_BUSC_NodeNumber
else
set udg_BUSC_RecyclableNodes = udg_BUSC_RecyclableNodes - 1
set Node = udg_BUSC_RecycleNodes[udg_BUSC_RecyclableNodes]
endif
set udg_BUSC_NextNode[Node] = 0
set udg_BUSC_NextNode[udg_BUSC_PrevNode[0]] = Node
set udg_BUSC_PrevNode[Node] = udg_BUSC_PrevNode[0]
set udg_BUSC_PrevNode[0] = Node
set udg_BUSC_ChannelId[Node] = Id
set udg_BUSC_Ability[Node] = Event
set udg_BUSC_Unit[Node] = u
set udg_BUSC_Target[Node] = Target
set udg_BUSC_TargetX[Node] = x
set udg_BUSC_TargetY[Node] = y
set udg_BUSC_Order[Node] = GetUnitCurrentOrder(u)
set udg_BUSC_Cancel[Node] = false
set udg_BUSC_Running[Node] = true
set udg_BUSC_CDuration[Node] = Duration
set udg_BUSC_Delay[Node] = 1. / udg_BUSCR_SpawnRate[Id]
set udg_BUSC_CDelay[Node] = udg_BUSC_Delay[Node]
set udg_BUSC_HueChange[Node] = Hue
set udg_BUSC_CHueRed[Node] = 255
set udg_BUSC_CHueGreen[Node] = 255
set udg_BUSC_CHueBlue[Node] = 255
set udg_BUSC_CHueAlpha[Node] = 255
set TempReal = (1 / udg_BUSCR_HueSpeed[Id]) * BUSCR_TimerSpeed()
set udg_BUSC_HueChangeRed[Node] = R2I((udg_BUSCR_HueRed[Id] - udg_BUSC_CHueRed[Node]) * TempReal)
set udg_BUSC_HueChangeGreen[Node] = R2I((udg_BUSCR_HueGreen[Id] - udg_BUSC_CHueGreen[Node]) * TempReal)
set udg_BUSC_HueChangeBlue[Node] = R2I((udg_BUSCR_HueBlue[Id] - udg_BUSC_CHueBlue[Node]) * TempReal)
set udg_BUSC_HueChangeAlpha[Node] = R2I((udg_BUSCR_HueAlpha[Id] - udg_BUSC_CHueAlpha[Node]) * TempReal)
set udg_BUSC_CChangeHueDelay[Node] = udg_BUSCR_HueSpeed[Id]
if (udg_BUSC_PrevNode[Node] == 0) then
call TimerStart(udg_BUSC_Timer, BUSCR_TimerSpeed(), true, function BUS_ChannelLoop)
endif
endfunction
////////////////////////////////////////////////////////////////////
// InitTrig BUS Channel: Sets up the map bounds and the location //
// used for getting Z locations and the timer used to run the //
// loop //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Channel takes nothing returns nothing
set udg_BUS_Loc = Location(0,0)
set udg_BUSC_Timer = CreateTimer()
set udg_BUS_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set udg_BUS_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
set udg_BUS_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set udg_BUS_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction
////////////////////////////////////////////////////////////////////
// End of the system //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Register_Channels takes nothing returns nothing
set udg_BUSCR_Name[0] = "Arthas Light"
set udg_BUSCR_Effect[0] = "Abilities\\Weapons\\PriestMissile\\PriestMissile.mdl"
set udg_BUSCR_MinAOE[0] = 300.0
set udg_BUSCR_MaxAOE[0] = 500.0
set udg_BUSCR_AbsorbAOE[0] = 50.0
set udg_BUSCR_MinSize[0] = 0.1
set udg_BUSCR_MaxSize[0] = 1.5
set udg_BUSCR_SpawnRate[0] = 30
set udg_BUSCR_SpawnCount[0] = 2
set udg_BUSCR_Power[0] = 60
set udg_BUSCR_HueRed[0] = 60
set udg_BUSCR_HueGreen[0] = 60
set udg_BUSCR_HueBlue[0] = 60
set udg_BUSCR_HueAlpha[0] = 255
set udg_BUSCR_HueSpeed[0] = 0.50
call BUS_RegisterChannel(0)
//Next Channel
set udg_BUSCR_Name[0] = "Arthas Dark"
set udg_BUSCR_Effect[0] = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
set udg_BUSCR_MinAOE[0] = 300.0
set udg_BUSCR_MaxAOE[0] = 500.0
set udg_BUSCR_AbsorbAOE[0] = 50.0
set udg_BUSCR_MinSize[0] = 0.1
set udg_BUSCR_MaxSize[0] = 1.5
set udg_BUSCR_SpawnRate[0] = 30
set udg_BUSCR_SpawnCount[0] = 2
set udg_BUSCR_Power[0] = 60
set udg_BUSCR_HueRed[0] = 60
set udg_BUSCR_HueGreen[0] = 60
set udg_BUSCR_HueBlue[0] = 255
set udg_BUSCR_HueAlpha[0] = 255
set udg_BUSCR_HueSpeed[0] = 0.50
call BUS_RegisterChannel(0)
//Next Channel
endfunction
JeffQ's Basic Chain System v2.00f
Created Date: 22 January 2010
Author: JeffQ a.k.a JeffreyQ, SmileyJeff
**Official version will only be destributed by ME (JeffQ, JeffreyQ. SmileyJeff). Anyone else who upload this is edited.
I have been working on my AoS map and decided to create a chain system that can do pretty much anything.
Because my AoS have alot of chain spells that are in GUI.
So here it is. After 3 days of hardworking and understanding indexing. I present you JeffQ's Basic Chain System (MUI/GUI using Indexing).
======================
Change Log
======================
v2.00f, 24 Feb 2010
- Chain should no longer jump to buildings/structures.
- Some demo ability can still target buildings, this is because i didn't limit the target allowed at the ability editor. (Nothing to do with the system)
v2.00e, 31 Jan 2010
- Fix lightning height to match cliff or raised terrain.
- Added a raised terrain and cliff to test out.
v2.00d, 25 Jan 2010
- Rearrange Folder for easier implementing in other maps.
- Updated "How To Use" in the README to give a step by step tutorial on how to implement. Please do give it a read.
v2.00c, 24 Jan 2010
- Chain now gets unit height +25. Meaning that if you target a flying unit, the chain height will change accordingly.
- Added a few flying units to let you try out the dynamic chain height.
- Added Chain Globals Trigger to store Locust.
- Chain should now only jump to units that doesn't have locust (This should remove the limitation of where you need to only base all other ability dummies on my dummy as long as your custom dummies have locust).
v2.00b, 23 Jan 2010
- Lightning effect now won't stick to ground.
v2.00a, 23 Jan 2010
- Fix 2 major bugs that make the chain not MUI for chain effect and Stun effect.
- Due to setting the wrong index. Now it has been fixed and should no longer containt any more bugs! Thanks to my sis for testing out this with me [:
v2.00, 22 Jan 2010
- Revamp the chain effect system.
- Remove dummy type. You still have to copy the dummy to your map though.
- Remove finger of death limitations. Now you can choose your chain effect freely.
- Added a new Chain Effect Trigger.
- This allows the chain to follow the unit if it is moving.
- Polish code.
- Fixed a few bugs in code.
- Added Implementation Details Trigger and tidy up the Example 1-8 Implementations.
- Removed all (SFX) ability as now you can use the QJC_ChainSFX to set your lightning effect.
v1.14, 22 Jan 2010
- Added 2 new abilities chain sheep and chain invisible to show the flexibility of this system and how to play around with stun/slow effect to your advantages.
v1.13a, 22 Jan 2010
- Added a sleep ability in the implementation after Kingz asked me what if he wanted to create a chain sleep. (No additional system code added).
- Fix several bugs.
v1.12, 21 Jan 2010
- Periodic Time change to 0.03seconds. And System Time update + 0.03 from 0.01.
v1.11a, 21 Jan 2010
- Fixed Implementation Example Comment for Chain Effect.
v1.11, 21 Jan 2010
- Initial Release.
======================
Customizable Features
======================
- Damage (Boolean: Only damage enemies nomatter what. If you want to damage ally use health and set ally = true)
- Heal (Boolean: Only heal Allies nomatter what)
- Health (Boolean: Make enemy lose health depending on Amount of Damage Type)
- Mana (Boolean: Make enemy lose mana depending on Amount of Damage Type)
- Gold (Boolean: Make enemy lose gold depending on Amount of Damage Type)
- Leech (Boolean: Only affects Health, Mana, Gold. If true, it will transfer Health/Mana/Gold over to the caster)
- Slow (Boolean: If true, it will slow units depending on slow effect)
- Slow Effect (Ability: The ability of slow effect, BASE ALL SLOW ON HUMAN SORCERESS SLOW!!)
- Stun (Boolean: If true, it will stun units depending on stun effect)
- Stun Effect (Ability: The ability of stun effect, BASE ALL STUN ON STORM BOLT!!)
- AoE (Boolean: If true, it will do AoE damage.)
- AoE Radius (Real: Set the radius of AoE Effect)
- Target Effect (String: Lets you insert your effect path)
- Chain Effect (LightningType: Feel free to choose your lightning effect for the chain)
- Amount of Damage Type (Real: Damage, Heal, Health, Mana, Gold)
- Amount Reduction (Real: Each jump reduce a percentage of amount of damage type)
- Jump Count (Integer: How many units it will jump)
- Jump Radius (Real: The range of how far the chain can jump between units)
- Jump Delay (Real: Supports the chain jump delay seconds. 0.10 is the effect of chain lightning, 0.00 is "ALMOST" instant)
- One Per Unit (Boolean: Chain will only hit the unit once if set to true)
- Priority (Boolean: Hit lowest health 1st)
- Ally (Boolean: Hit Ally if set to true, if enemy is also true, it will hit both)
- Enemy (Boolean: Hit Enemy if set to true, if elly is also true, it will hit both)
- Target Unit (Unit: Target Unit, set to No unit if no target or have a target point)
- Point (Point: Target Point, if has a target unit or no target disable this)
- No Target (Boolean: If true, disable point and set unit to no unit)
- Attack (Attack Type: Customize your own attack type)
- Damage Type (Damage Type: Customize your own damage type)
======================
Rules
======================
They are some rules applied to use this system. Is more of a technical limitation.
I will talk more about this after letting you know how to use.
1. Do not touch anything in Chain Create, Chain Jump and Chain Effect Trigger. Those are the system.
2. For implementation examples. Please look at the 8 examples of how flexible you can change the chain to what you want.
3. This system needs you to know the usage of Unit Editor and Ability/Buff Editor.
4. QJC_VarName, This is the only variables you can set.
5. QJCS_VarName are for system variables. So don't even touch it unless you know what you are doing.
======================
How to Use?
======================
If your map is a New Map without any triggers:
1. Open JeffQ's Basic Chain System map.
2. Open Trigger Editor.
3. File > Export Triggers.
4. Open your map.
5. Open Trigger Editor.
6. File > Import Triggers.
7. Remember to copy the Dummy over too.
If your map has existing triggers:
1. Open JeffQ's Basic Chain System map.
2. Copy JeffQ's Chain System folder by right-clicking it and select Copy.
3. Open your map.
4. File > Preference > Check "Automatically create unknown variables while pasting trigger data".
5. Open Object Editor. Create a Dummy base on anything, name it Dummy and give it locust and remove its model. Or just copy the dummy from my map.
6. Open Trigger Editor.
7. Right click on the Map Name, or any other trigger/category/comments, and select Paste.
8. Create a variable by pressing CTRL+B and name it QJCS_ChainZ of type Real, array size of 1.
9. Go to "Chain Create" Trigger, find any Disabled Actions, enable it by right clicking and check Enable.
10.Go to "Chain Jump" Trigger, find any Disabled Actions, enable it by right clicking and check Enable.
11.Go to "Chain effect" Trigger, find any Disabled Actions, enable it by right clicking and check Enable.
======================
Vars you need to set
======================
QJC_Caster : Triggering Unit
- Your caster.
QJC_TargetUnit : Target unit of ability being cast
- Set this to No unit if your spell is target point or instant cast.
QJC_TargetPoint : Target point of ability being cast
- Disable this if you have a target unit or instant cast.
QJC_NoTarget : True/False
- Set this to true if your spell is instant cast.
- Remember to disable QJC_TargetPoint and set QJC_TargetUnit to No Unit.
QJC_Priority : True/False
- If true, it will find the lowest health unit in range first.
QJC_OnePerUnit : True/False
- If true, it will only hit the target once.
QJC_Damage : True/False
- If true, this chain will do damage to enemy depending on QJC_Amount.
QJC_Heal : True/False
- If true, this chain will heal ally depending on QJC_Amount.
QJC_Health : True/False
- If true, it will make enemy lose health.
- If leech is on, it will transfer health over to the caster.
QJC_Mana : True/False
- If true, it will make enemy lose mana.
- If leech is on, it will transfer mana over to the caster.
QJC_Gold : True/False
- If true, it will make enemy lose gold.
- If leech is on, it will transfer gold over to the caster.
QJC_Leech : True/False
- If true, it will apply leeching effect to Health/Mana/Gold type.
QJC_Ally : True/False
- If true, it will hit ally.
- If enemy is also true, it will hit both.
QJC_Enemy : True/False
- If true, it will hit enemy.
- If ally is also true, it will hit both.
QJC_Amount : Real
- The amount of damage type you want this chain to do.
- Eg: QJCGold = true, QJC_Amount = 100, it will make enemy lose 100gold.
QJC_ChainSFX : Lightning Type
- Feel free to choose the lightning type for your chain.
QJC_TargetSFX : String
- Lets you set the special effect for unit when hit by chain.
QJC_AttackType : Attack Type
- Nothing much to say about this.
QJC_DamageType : Damage Type
- Nothing much to say about this.
QJC_AmountReduce: Real
- This is base on percentage.
- If it is 0.00, each chain jump won't reduce QJC_Amount
- If it is 0.10, each chain jump will decreament QJC_Amount by 10%.
- Least is 0.00, Max is 1.00.
QJC_JumpCount : Integer
- Set the amount of jumps for the chain.
QJC_JumpDelayTime: Real
- This is base on seconds.
- Least is 0.00, Max is 1.00
- Set to 0.10 to get the effect of Blizzard Chain Lightning
QJC_JumpRadius : Real
- How far can the chain jump between points.
QJC_Slow : Boolean
- Set to true to slow enemies.
- You have to set QJC_SlowEffect if you want this to work.
QJC_SlowEffect : Ability
- Disable this if Slow Effect is False.
- Slow effect must base on HUMAN SLOW. NO BUTS!
- For example, go to "Ability Editor > Custom Abilities > Neutral Hostile > Units".
- Slow effect will have a (Slow) behind the name.
QJC_Stun : Boolean
- Set to true to stun enemies.
- You have to set QJC_StunEffect if you want this to work.
QJC_StunEffect : Ability
- Disable this if stun effect is false.
- Stun effect must base on STORM BOLT. NO BUTS!
- For example, go to "Ability Editor > Custom Abilities > Neutral Hostile > Units".
- Stun effect will have a (Stun) behind the name)
QJC_AoE : Boolean
- Set to true to do AoE.
- You have to set QJC_AoERadius to make this work. If not the radius will be 0.00.
QJC_AoERadius : Real
- The AoE range you want.
Trigger - Run Chain Create <gen> (checking conditions)
- This must always be included in the trigger of your spell. If not the system won't run.
- Must check conditions.
======================
Limitations
======================
Q: Why i need to base Chain Effect on Finger of Death?
A: I order the dummy unit to cast fingerofdeath order command. And also finger of death let's you do chain effects while channel can't for some reason.
Q: Why i need to base Slow on Human Sorceress Slow?
A: I order the dummy unit to cast Human Sorceress - Slow when i do this. If you base it on other spells, it just won't work.
Q: Why i need to base Stun on Storm Bolt?
A: I order the dummy unit to cast Human Mountain King - Storm Bolt.
Q: Why must dummy be invulnerable and not Locust? And why must it have no model?
A: Because i order the dummy to finger of death the next dummy. If it is locust, it can't be finger of death for the chain effect. No model makes it unselectable on map and won't display health if you hold down ALT key.
Q: Why not ask the dummy to chain the target? Is so much simpler.
A: No. After 3 days of testing. If the target dies the dummy won't be fast enough to cast the chain, and thus, no chain effect.
Q: So where did you hide that dummy? Where can i find it to copy it to my map?
A: It's at the Object Editor > Unit > Custom Units > Night Elf > Melee > Units. There you can find Dummy. Say hi.
======================
Flexibility
======================
1. If you fully know how to use this system. It is quite flexible as you can set the Slow and Stun effect by changing the buffs of the Abilities.
2. Alot of things stack except for a few limitations. You can set all to true and see what happens [:
3. You can target point, target unit or No Target!
======================
Bug Reports & Feedback
======================
Please send an email to [email protected] if you find a bug. Or post it on the comment at Hive Work Shop.
======================
Credits
======================
Thanks to the below who shed some light on me about indexing and leaks.
- Kingz
- YourNameHere
- baassee
- Adiktuz
- Maker
- TitanHex
- D4RK_G4ND4LF
- And other people if i miss out.
Special Thanks to Kingz who show me his simple lightning system. I learned alot from it and after understanding i made 1 my own to fit my system.
***Enjoy this system [:
:+++++++`
..........ymmmmmmm-...............
.NNNNNNNNNN:```````mNNNNNNNNNNNNNNh
+dddddddddh----------` .--------------/ddddddd+
oooooooooooooooooooo+ .oooooooooooooooooooo:
.--hddddddddd/ /ddddddddddddo--`
sNN`````````` ````````````+NN.
sMM` +MM.
-++ss/ sso++`
NMy -:- -:- `MMs
NMy hMm`` ``mMh `MMs
NMy hMMmm+ +mmMMh `MMs
NMy hMMMM+ +MMMMh `MMs
NMy hMMMMy// //yMMMMh `MMs
NMy hMMMMMMM` `MMMMMMMh `MMs
NMy hMMMMMMM` `MMMMMMMh `MMs
NMy hMMMMMMM` `MMMMMMMh `MMs
NMy hMMMMMMM+/: `MMMMMMMh -//yy/
NMy ymmMMMMMMMy .......................` `MMMMMMMh yMN
NMy ``-MMMMMMMy mNNNNNNNNNNNNNNNNNNNNNNo `MMMMMMMh yMN
-:+hh- .MMMMMMMNhhhhhhhhhhMMMMMMMMMMMMMMMMMMMMMMMmhhhhhhhhhdMMMMMMMh yMN
/MM: .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMys+ yMN
/MM: .ddNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNdd. yMN
/MM: ``sMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMs`` yMN
/MM: .----oMMMMMMMs----+MMMMMMMMMMMMMMM+----sMMMMMMMMMN--. yMN
.oooo+ .oooosMMo .oomMMMMMMMMMmoo. oMMsooooooo .ooooo
hMm .MMs-- hMMMMMMMMMh --sMM. :MM/
hNd`` .NNNMM.`` hMMMMMMMMMh ``.MMNNN. ``:NN/
.-:dd/ --yMMmds hMMMMMMMMMh sdmMMy-- hdy--`
.MM+ -++++ossmMMMMMMMMMmssmMN++- mMh
.MM+ :MMMMMMMMMMMMMMMmhh .::yhs
.NNo`` :MMMMMMMMMMMMMMM: ``oNN.
..omm` :MMMMMMMMMMMMMMM: `mmo..
sMM` .//dMMMMMMMMMMMM: `MMs
/hh/:- hMMhhhhhMMMMM: -:/hh/
mmy.. hMN NMMMM: ..ymm
`./mm- hMN NMMMM: -mm/.`
.//sys hMN NMd//` :MM/
hMm hMN/////NMh //+yy-
ymd..... ymNMMMMMNmy `..dmy
``.NNNNN` ``.MMMMM.`` +NN.``
:::::hho ::::: ohhhho::
oso++++/ .++ossss:
:mmmmd......./mm:
`NNNNNNNy
-------.
`-------` `----. `-- `--` ----- --` `-------` .---------` `----- `-------`
/MMMNMMM+ ``/NNMMm sMM``/MM: .MMMNN` `.MMo`` :MMMNMMMo mMMMMMMMMMs `:NNMMN +MMMNMMM/
/MM/.:MM+ dms..dMm sMMmmmMM: .MMs.. smmMMNmm` :MM+.-MMo mMMMMMMMMMs hmy..hMN +MM:./MM/
/MMysyMM+ NMy hMm sMMMMMMM: .MMdss` hMN++hMM` :MMhssMMo mMMMMMMMMMs mMh yMN +MMysyMM/
/MMdhdMM+ NMy hMm sMMMMMMM: .MMmhh` hMN::yMM` :MMmhdMMo mMNhhNMMMMs mMh yMN +MMdhdMM/
/MM- -MM+ NMy dMm sMMMMMMM: .MMo hMMMMMMM` :MM: `MMo mMh yMMMMs mMd yMN +MM. -MM/
/MMNNNMM+ NMMNNMMm sMM.`/MM: .MMNNN` hMN``sMM` :MM: `MMo mMh yMMMMs mMMNNMMN +MM. -MM/
.///////. //////// -// .//` `///// :// -//` .//. //- //: :////- //////// .//` `//.
Import Bone Armor in your map!
Step 1 - Allow creation of unknown variables - (Understand that this is absolutely nessesary)
1A - Under Preferences, make sure you have "Automatically create unknown variables while pasting trigger data" checked
Step 2 - Install Bribe's Damage Engine into your map:
2A - Copy Pasta Bribe's Damage Engine Folder into your trigger editor
Step 3 - Install Bone Armor
4A Copy Pasta the Bone Armor folder into your trigger editor
4B Copy the Bone Armor Ability from Object Editor, and set it to the variable BAmr_SPELL
4C Copy the Bone Armor Buff from Object Editor, and set it to the variable BArmr_BUFF
function castBoneArmorOnAllies takes nothing returns nothing
local unit sourceUnit = GetTriggerUnit()
local unit targetUnit = GetEnumUnit()
local unit dummyUnit
local player sourcePlayer = GetOwningPlayer(sourceUnit)
local location targetLocation
if ( IsUnitAlly(targetUnit, sourcePlayer) == true ) then
set targetLocation = GetUnitLoc(targetUnit)
call CreateNUnitsAtLoc(1, 'n012', sourcePlayer, targetLocation, bj_UNIT_FACING)
set dummyUnit = GetLastCreatedUnit()
call ModifyHeroStat( bj_HEROSTAT_STR, dummyUnit, bj_MODIFYMETHOD_SET, GetHeroStatBJ(bj_HEROSTAT_STR, sourceUnit, true) )
call ModifyHeroStat( bj_HEROSTAT_AGI, dummyUnit, bj_MODIFYMETHOD_SET, GetHeroStatBJ(bj_HEROSTAT_AGI, sourceUnit, true) )
call ModifyHeroStat( bj_HEROSTAT_INT, dummyUnit, bj_MODIFYMETHOD_SET, GetHeroStatBJ(bj_HEROSTAT_INT, sourceUnit, true) )
call UnitAddAbilityBJ( 'A04Z', dummyUnit )
call IssueTargetOrderBJ( dummyUnit, "innerfire", targetUnit )
call RemoveUnit(dummyUnit)
call RemoveLocation(targetLocation)
endif
endfunction
function Trig_BoneArmorAOE_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'A00B' ) ) then
return false
endif
return true
endfunction
function Trig_BoneArmorAOE_Actions takes nothing returns nothing
local unit sourceUnit = GetTriggerUnit()
local location sourceLocation = GetUnitLoc(sourceUnit)
local group unitsInRange = GetUnitsInRangeOfLocAll(500.00, sourceLocation)
call ForGroupBJ( unitsInRange, function castBoneArmorOnAllies )
call DestroyGroup(unitsInRange)
endfunction
//===========================================================================
function InitTrig_BoneArmorAOE_JASS takes nothing returns nothing
set gg_trg_BoneArmorAOE_JASS = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_BoneArmorAOE_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_BoneArmorAOE_JASS, Condition( function Trig_BoneArmorAOE_Conditions ) )
call TriggerAddAction( gg_trg_BoneArmorAOE_JASS, function Trig_BoneArmorAOE_Actions )
endfunction
function onBlzEndUnitAbilityCooldown takes unit whichUnit, integer abilCode returns nothing
local integer playerIndex = GetConvertedPlayerId(GetOwningPlayer(whichUnit))
local integer loopIndex = 0
if ( (IsTriggerEnabled(gg_trg_DualHeroChange) == true) and (playerIndex <=8) and (whichUnit == udg_LightHeroes[playerIndex] ) and (udg_LightHeroes[playerIndex+8] != null) ) then
// Only reset cooldown of ability for 2nd hero if it is an item ability
loop
if ( abilCode == udg_ItemAbilities[loopIndex] ) then
call BlzEndUnitAbilityCooldown(udg_LightHeroes[playerIndex+8], abilCode)
exitwhen true
endif
exitwhen loopIndex > udg_ItemAbilityCount
endloop
endif
endfunction
hook BlzEndUnitAbilityCooldown onBlzEndUnitAbilityCooldown
function onBlzSetUnitAbilityCooldown takes unit whichUnit, integer abilId, integer level, real cooldown returns nothing
local integer playerIndex = GetConvertedPlayerId(GetOwningPlayer(whichUnit))
local integer loopIndex = 0
if ( (IsTriggerEnabled(gg_trg_DualHeroChange) == true) and (playerIndex <=8) and (whichUnit == udg_LightHeroes[playerIndex] ) and (udg_LightHeroes[playerIndex+8] != null) ) then
// Only modify cooldown of ability for 2nd hero if it is an item ability
loop
if ( abilId == udg_ItemAbilities[loopIndex] ) then
call BlzSetUnitAbilityCooldown(udg_LightHeroes[playerIndex+8], abilId, level, cooldown)
exitwhen true
endif
exitwhen loopIndex > udg_ItemAbilityCount
endloop
endif
endfunction
hook BlzSetUnitAbilityCooldown onBlzSetUnitAbilityCooldown
function onUnitResetCooldown takes unit whichUnit returns nothing
local integer playerIndex = GetConvertedPlayerId(GetOwningPlayer(whichUnit))
if ( (IsTriggerEnabled(gg_trg_DualHeroChange) == true) and (playerIndex <=8) and (whichUnit == udg_LightHeroes[playerIndex] ) and (udg_LightHeroes[playerIndex+8] != null) ) then
// Also reset all cooldowns of 2nd hero
call UnitResetCooldown(udg_LightHeroes[playerIndex+8])
endif
endfunction
hook UnitResetCooldown onUnitResetCooldown
function Trig_StormBolt_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AHtb' ) ) then
return false
endif
if ( not ( GetUnitAbilityLevelSwapped('AHtb', GetTriggerUnit()) == 4 ) ) then
return false
endif
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'Hmkg' ) ) then
return false
endif
if ( not ( GetUnitLevel(GetTriggerUnit()) >= 20 ) ) then
return false
endif
return true
endfunction
function Trig_StormBolt_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local unit targetUnit = GetSpellTargetUnit()
local player casterPlayer = GetOwningPlayer(casterUnit)
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'AHtb', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'A02V', casterPlayer )
call IssueTargetOrderBJ( casterUnit, "creepthunderbolt", targetUnit )
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'A02V', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'AHtb', casterPlayer )
set casterPlayer = null
set targetUnit = null
set casterUnit = null
endfunction
//===========================================================================
function InitTrig_StormBolt_JASS takes nothing returns nothing
set gg_trg_StormBolt_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_StormBolt_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_StormBolt_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_StormBolt_JASS, Condition( function Trig_StormBolt_Conditions ) )
call TriggerAddAction( gg_trg_StormBolt_JASS, function Trig_StormBolt_Actions )
endfunction
function Trig_ThunderClap_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AHtc' ) ) then
return false
endif
if ( not ( GetUnitAbilityLevelSwapped('AHtc', GetTriggerUnit()) == 4 ) ) then
return false
endif
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'Hmkg' ) ) then
return false
endif
if ( not ( GetUnitLevel(GetTriggerUnit()) >= 20 ) ) then
return false
endif
return true
endfunction
function Trig_ThunderClap_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local player casterPlayer = GetOwningPlayer(casterUnit)
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'AHtc', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'A04P', casterPlayer )
call IssueImmediateOrderBJ( casterUnit, "creepthunderclap" )
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'A04P', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'AHtc', casterPlayer )
set casterPlayer = null
set casterUnit = null
endfunction
//===========================================================================
function InitTrig_ThunderClap_JASS takes nothing returns nothing
set gg_trg_ThunderClap_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_ThunderClap_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_ThunderClap_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_ThunderClap_JASS, Condition( function Trig_ThunderClap_Conditions ) )
call TriggerAddAction( gg_trg_ThunderClap_JASS, function Trig_ThunderClap_Actions )
endfunction
function ConvertPolarCoordToLocation takes location tL,real TL,real uL returns location
return Location(GetLocationX(tL)+TL*Cos(uL*bj_DEGTORAD),GetLocationY(tL)+TL*Sin(uL*bj_DEGTORAD))
endfunction
function RealmOfSoulPeriodic_Actions takes nothing returns nothing
local trigger realmOfSoulPeriodicTrigger = GetTriggeringTrigger()
local unit dummyUnit = LoadUnitHandle(udg_RosHashtable,GetHandleId(realmOfSoulPeriodicTrigger),StringHashBJ("DummyUnit"))
local location dummyUnitLocation = GetUnitLoc(dummyUnit)
local location targetLocation = ConvertPolarCoordToLocation(dummyUnitLocation,100.,GetRandomReal(0,360))
call DebugLog(LVL_DEBUG, "RealmOfSoulPeriodic_Actions")
call IssuePointOrderLocBJ( dummyUnit, "carrionswarm", targetLocation )
call RemoveLocation(dummyUnitLocation)
call RemoveLocation(targetLocation)
set targetLocation = null
set dummyUnitLocation = null
set dummyUnit = null
set realmOfSoulPeriodicTrigger = null
endfunction
function Trig_RealmOfSoul_Conditions takes nothing returns boolean
return( GetSpellAbilityId()=='A04X' )
endfunction
function Trig_RealmOfSoul_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local real spellDuration = (5.+(I2R(GetUnitAbilityLevelSwapped('A04X', casterUnit))*5.))
local unit dummyUnit
local integer dummyUnitId
local location casterUnitLocation = GetUnitLoc(casterUnit)
local trigger realmOfSoulPeriodicTrigger = CreateTrigger()
call DebugLog(LVL_DEBUG, "RealOfSoul_Actions")
// Create a dummy unit
call CreateNUnitsAtLoc( 1, 'n010', GetOwningPlayer(casterUnit), casterUnitLocation, bj_UNIT_FACING )
set dummyUnit = bj_lastCreatedUnit
set dummyUnitId = GetHandleId(dummyUnit)
call SetUnitAbilityLevelSwapped( 'A04Y', dummyUnit, GetUnitAbilityLevelSwapped('A04X', casterUnit) )
// Save the data required for the periodic and end triggers
call SaveUnitHandle(udg_RosHashtable,GetHandleId(dummyUnit),StringHashBJ("CasterUnit"),casterUnit) // Caster unit
call SaveUnitHandle(udg_RosHashtable,GetHandleId(realmOfSoulPeriodicTrigger),StringHashBJ("DummyUnit"),dummyUnit) // Dummy unit
// Execute the periodic trigger
call TriggerRegisterTimerEvent(realmOfSoulPeriodicTrigger,0.2,true)
call TriggerAddCondition(realmOfSoulPeriodicTrigger,Condition(function RealmOfSoulPeriodic_Actions))
//call TriggerDebugAutomation_TriggerIsReady(realmOfSoulPeriodicTrigger, "realmOfSoulPeriodicTrigger")
call TriggerEvaluate(realmOfSoulPeriodicTrigger)
// Wait for its end then clean variables
call TriggerSleepAction(spellDuration)
call DisableTrigger(realmOfSoulPeriodicTrigger)
call RemoveSavedHandle(udg_RosHashtable,GetHandleId(dummyUnit),StringHashBJ("CasterUnit"))
call RemoveSavedHandle(udg_RosHashtable,GetHandleId(realmOfSoulPeriodicTrigger),StringHashBJ("DummyUnit"))
call RemoveUnit(dummyUnit)
call DestroyTrigger(realmOfSoulPeriodicTrigger)
call RemoveLocation(casterUnitLocation)
set realmOfSoulPeriodicTrigger = null
set casterUnitLocation = null
set dummyUnit = null
set casterUnit = null
endfunction
function Trig_RealmOfSoulDummyCastFinished_Conditions takes nothing returns boolean
return( GetSpellAbilityId()=='A04Y' )
endfunction
function Trig_RealmOfSoulDummyCastFinished_Actions takes nothing returns nothing
local unit dummyUnit = GetSpellAbilityUnit()
local unit casterUnit = LoadUnitHandle(udg_RosHashtable,GetHandleId(dummyUnit),StringHashBJ("CasterUnit"))
local location casterUnitLocation = GetUnitLoc(casterUnit)
call DebugLog(LVL_DEBUG, "Trig_RealmOfSoulDummyCastFinished_Actions")
call SetUnitPositionLoc(dummyUnit,casterUnitLocation)
call RemoveLocation(casterUnitLocation)
set casterUnitLocation = null
set casterUnit = null
set dummyUnit = null
endfunction
//===========================================================================
function InitTrig_RealmOfSoul takes nothing returns nothing
local trigger Trig_RealmOfSoulDummyCastFinished = CreateTrigger()
set gg_trg_RealmOfSoul = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( gg_trg_RealmOfSoul, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_RealmOfSoul, Condition(function Trig_RealmOfSoul_Conditions) )
call TriggerAddAction( gg_trg_RealmOfSoul, function Trig_RealmOfSoul_Actions )
call TriggerRegisterAnyUnitEventBJ( Trig_RealmOfSoulDummyCastFinished ,EVENT_PLAYER_UNIT_SPELL_FINISH )
call TriggerAddCondition( Trig_RealmOfSoulDummyCastFinished, Condition(function Trig_RealmOfSoulDummyCastFinished_Conditions))
call TriggerAddAction( Trig_RealmOfSoulDummyCastFinished, function Trig_RealmOfSoulDummyCastFinished_Actions )
//call TriggerDebugAutomation_TriggerIsReady(Trig_RealmOfSoulDummyCastFinished, "Trig_RealmOfSoulDummyCastFinished")
endfunction
function Trig_Hex_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'A03F' ) ) then
return false
endif
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'Oshd' ) ) then
return false
endif
if ( not ( GetHeroLevel(GetTriggerUnit()) >= 20 ) ) then
return false
endif
if ( not ( IsUnitPausedBJ(GetSpellTargetUnit()) == false ) ) then
return false
endif
return true
endfunction
function Trig_Hex_Actions takes nothing returns nothing
local unit target = GetSpellTargetUnit()
local location targetLoc = GetUnitLoc(target)
local location tempLoc
local player sourcePlayer = GetOwningPlayer(GetTriggerUnit())
local integer index = 1
loop
exitwhen index > 6
call TriggerSleepAction( 0.10 )
set tempLoc = PolarProjectionBJ(targetLoc, 200.00, ( 60.00 * I2R(index) ))
call CreateNUnitsAtLocFacingLocBJ( 1, 'o000', sourcePlayer, tempLoc, targetLoc )
call RemoveLocation(tempLoc)
call UnitApplyTimedLifeBJ( 8.00, 'Bhwd', GetLastCreatedUnit() )
set index = index + 1
endloop
call RemoveLocation(targetLoc)
set sourcePlayer = null
set tempLoc = null
set targetLoc = null
set target = null
endfunction
//===========================================================================
function InitTrig_Hex_JASS takes nothing returns nothing
set gg_trg_Hex_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_Hex_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Hex_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_Hex_JASS, Condition( function Trig_Hex_Conditions ) )
call TriggerAddAction( gg_trg_Hex_JASS, function Trig_Hex_Actions )
endfunction
function Trig_ShockWave_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AOsh' ) ) then
return false
endif
if ( not ( GetUnitAbilityLevelSwapped('AOsh', GetTriggerUnit()) == 4 ) ) then
return false
endif
if ( not ( (GetUnitTypeId(GetTriggerUnit()) == 'Otch') or (GetUnitTypeId(GetTriggerUnit()) == 'O008') ) ) then
return false
endif
if ( not ( GetUnitLevel(GetTriggerUnit()) >= 20 ) ) then
return false
endif
if ( not ( UnitHasBuffBJ(GetTriggerUnit(), 'B00N') == true ) ) then
return false
endif
return true
endfunction
function Trig_ShockWave_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local player casterPlayer = GetOwningPlayer(casterUnit)
local unit targetUnit = GetSpellTargetUnit()
local location targetLocation = GetSpellTargetLoc()
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'AOsh', GetOwningPlayer(GetTriggerUnit()) )
call SetPlayerAbilityAvailableBJ( true, 'A04Q', GetOwningPlayer(GetTriggerUnit()) )
if ( targetUnit != null ) then
call IssueTargetOrderBJ( casterUnit, "carrionswarm", targetUnit)
elseif ( targetLocation != null ) then
call IssuePointOrderLocBJ( casterUnit, "carrionswarm", targetLocation)
endif
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'A04Q', GetOwningPlayer(GetTriggerUnit()) )
call SetPlayerAbilityAvailableBJ( true, 'AOsh', GetOwningPlayer(GetTriggerUnit()) )
set targetLocation = null
set targetUnit = null
set casterPlayer = null
set casterUnit = null
endfunction
//===========================================================================
function InitTrig_ShockWave_JASS takes nothing returns nothing
set gg_trg_ShockWave_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_ShockWave_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_ShockWave_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_ShockWave_JASS, Condition( function Trig_ShockWave_Conditions ) )
call TriggerAddAction( gg_trg_ShockWave_JASS, function Trig_ShockWave_Actions )
endfunction
function Trig_WarStomp_Func009C takes nothing returns boolean
if ( ( GetUnitTypeId(GetTriggerUnit()) == 'Otch' ) ) then
return true
endif
if ( ( GetUnitTypeId(GetTriggerUnit()) == 'O008' ) ) then
return true
endif
return false
endfunction
function Trig_WarStomp_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AOws' ) ) then
return false
endif
if ( not ( GetUnitAbilityLevelSwapped('AOws', GetTriggerUnit()) == 4 ) ) then
return false
endif
if ( not Trig_WarStomp_Func009C() ) then
return false
endif
if ( not ( GetUnitLevel(GetTriggerUnit()) >= 20 ) ) then
return false
endif
if ( not ( UnitHasBuffBJ(GetTriggerUnit(), 'B00N') == true ) ) then
return false
endif
return true
endfunction
function Trig_WarStomp_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local player casterPlayer = GetOwningPlayer(casterUnit)
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'AOws', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'A01O', casterPlayer )
call IssueImmediateOrderBJ( casterUnit, "creepthunderclap" )
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'A01O', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'AOws', casterPlayer )
set casterPlayer = null
set casterUnit = null
endfunction
//===========================================================================
function InitTrig_WarStomp_JASS takes nothing returns nothing
set gg_trg_WarStomp_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_WarStomp_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_WarStomp_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_WarStomp_JASS, Condition( function Trig_WarStomp_Conditions ) )
call TriggerAddAction( gg_trg_WarStomp_JASS, function Trig_WarStomp_Actions )
endfunction
function Trig_FanOfKnives_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AEfk' ) ) then
return false
endif
if ( not ( GetUnitAbilityLevelSwapped('AEfk', GetTriggerUnit()) == 4 ) ) then
return false
endif
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'Ewar' ) ) then
return false
endif
if ( not ( GetUnitLevel(GetTriggerUnit()) >= 20 ) ) then
return false
endif
return true
endfunction
function Trig_FanOfKnives_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local player casterPlayer = GetOwningPlayer(casterUnit)
call TriggerSleepAction( 1.00 )
call SetPlayerAbilityAvailableBJ( false, 'AEfk', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'A075', casterPlayer )
call IssueImmediateOrderBJ( casterUnit, "fanofknives" )
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'A075', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'AEfk', casterPlayer )
endfunction
//===========================================================================
function InitTrig_FanOfKnives_JASS takes nothing returns nothing
set gg_trg_FanOfKnives_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_FanOfKnives_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_FanOfKnives_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_FanOfKnives_JASS, Condition( function Trig_FanOfKnives_Conditions ) )
call TriggerAddAction( gg_trg_FanOfKnives_JASS, function Trig_FanOfKnives_Actions )
endfunction
function Trig_Polymorph_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'A023' ) ) then
return false
endif
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'Ewar' ) ) then
return false
endif
if ( not ( GetUnitLevel(GetTriggerUnit()) >= 20 ) ) then
return false
endif
return true
endfunction
function Trig_Polymorph_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local player casterPlayer = GetOwningPlayer(casterUnit)
local unit targetUnit = GetSpellTargetUnit()
local location targetLoc = GetUnitLoc(targetUnit)
local location tempLoc
local integer index = 1
local integer indexEnd = 6
call TriggerSleepAction( 0.10 )
call PauseUnitBJ( true, targetUnit )
call DisableTrigger( GetTriggeringTrigger() )
call DisableTrigger( gg_trg_ShadowStrike_JASS )
loop
exitwhen index > indexEnd
call AddSpellEffectTargetById('AEbl', EFFECT_TYPE_AREA_EFFECT, casterUnit, "origin")
set tempLoc = PolarProjectionBJ(targetLoc, 350.00, ( 60.00 * I2R(index) ))
call SetUnitPositionLocFacingLocBJ( casterUnit, tempLoc, targetLoc )
call RemoveLocation(tempLoc)
call SetPlayerAbilityAvailableBJ( false, 'AEsh', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'A078', casterPlayer )
call IssueTargetOrderBJ( casterUnit, "shadowstrike", targetUnit )
call TriggerSleepAction( 1.0 )
call SetPlayerAbilityAvailableBJ( false, 'A078', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'AEsh', casterPlayer )
set index = index + 1
endloop
call RemoveLocation(targetLoc)
call EnableTrigger( gg_trg_ShadowStrike_JASS )
call EnableTrigger( GetTriggeringTrigger() )
call PauseUnitBJ( false, targetUnit )
set tempLoc = null
set targetLoc = null
set targetUnit = null
set casterPlayer = null
set casterUnit = null
endfunction
//===========================================================================
function InitTrig_Polymorph_JASS takes nothing returns nothing
set gg_trg_Polymorph_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_Polymorph_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Polymorph_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_Polymorph_JASS, Condition( function Trig_Polymorph_Conditions ) )
call TriggerAddAction( gg_trg_Polymorph_JASS, function Trig_Polymorph_Actions )
endfunction
function Trig_ShadowStrike_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AEsh' ) ) then
return false
endif
if ( not ( GetUnitAbilityLevelSwapped('AEsh', GetTriggerUnit()) == 4 ) ) then
return false
endif
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'Ewar' ) ) then
return false
endif
if ( not ( GetUnitLevel(GetTriggerUnit()) >= 20 ) ) then
return false
endif
return true
endfunction
function Trig_ShadowStrike_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local unit targetUnit = GetSpellTargetUnit()
local player casterPlayer = GetOwningPlayer(casterUnit)
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'AEsh', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'A078', casterPlayer )
call IssueTargetOrderBJ( casterUnit, "shadowstrike", targetUnit )
call TriggerSleepAction( 0.10 )
call SetPlayerAbilityAvailableBJ( false, 'A078', casterPlayer )
call SetPlayerAbilityAvailableBJ( true, 'AEsh', casterPlayer )
set casterPlayer = null
set targetUnit = null
set casterUnit = null
endfunction
//===========================================================================
function InitTrig_ShadowStrike_JASS takes nothing returns nothing
set gg_trg_ShadowStrike_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_ShadowStrike_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_ShadowStrike_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_ShadowStrike_JASS, Condition( function Trig_ShadowStrike_Conditions ) )
call TriggerAddAction( gg_trg_ShadowStrike_JASS, function Trig_ShadowStrike_Actions )
endfunction
library BanehallowSpells initializer init requires LightningSystem
globals
private group tmpG = CreateGroup()
private integer clocklight_call = 0
private real final_boss_x = 0
private real final_boss_y = 0
private integer banehallow_phase = 0
endglobals
private function ClockLightLoop takes nothing returns nothing
if udg_Banehallow == null or not IsUnitAliveBJ(udg_Banehallow) then
return
endif
// Corners of final boss zone: -4100,7000 / -4100,11900 / 3700,11900 / 3700,7000
if clocklight_call == 0 then
call Lightning.unitToPointExZ(udg_Banehallow, -4100.0,-4100, 7000.0,11900.0, 100.0,0.0, 400.0, 3.0, "LEAS", 1)
if banehallow_phase == 2 then
call Lightning.unitToPointExZ(udg_Banehallow, 3700,3700, 11900.0,7000.0, 100.0,0.0, 400.0, 3.0, "LEAS", 1)
endif
elseif clocklight_call == 1 then
call Lightning.unitToPointExZ(udg_Banehallow, -4100.0,3700, 11900.0,11900.0, 100.0, 0.0, 400.0, 3.0, "LEAS", 1)
if banehallow_phase == 2 then
call Lightning.unitToPointExZ(udg_Banehallow, 3700,-4100, 7000.0,7000.0, 100.0, 0.0, 400.0, 3.0, "LEAS", 1)
endif
elseif clocklight_call == 2 then
call Lightning.unitToPointExZ(udg_Banehallow, 3700,3700, 11900.0,7000.0, 100.0, 0.0, 400.0, 3.0, "LEAS", 1)
if banehallow_phase == 2 then
call Lightning.unitToPointExZ(udg_Banehallow, -4100.0,-4100, 7000.0,11900.0, 100.0, 0.0, 400.0, 3.0, "LEAS", 1)
endif
elseif clocklight_call == 3 then
call Lightning.unitToPointExZ(udg_Banehallow, 3700,-4100, 7000.0,7000.0, 100.0, 0.0, 400.0, 3.0 ,"LEAS",1)
if banehallow_phase == 2 then
call Lightning.unitToPointExZ(udg_Banehallow, -4100.0,3700, 11900.0,11900.0, 100.0, 0.0, 400.0, 3.0, "LEAS",1)
endif
endif
set clocklight_call = clocklight_call +1
if clocklight_call > 3 then
set clocklight_call = 0
endif
endfunction
private function ClockLightDamage takes nothing returns nothing
local unit u = null
local real dmg = 0
local real diff = I2R(udg_Difficulty)
local real tdy
local real tdx
local real dist
if udg_Banehallow == null or not IsUnitAliveBJ(udg_Banehallow) then
return
endif
call LightningUtils.enumUnits(tmpG, Lightning.instance, 100.0)
loop
set u = FirstOfGroup(tmpG)
exitwhen u == null
if u != Lightning.instance.u1 and u != Lightning.instance.u2 and not IsUnitAlly(u, GetOwningPlayer(udg_Banehallow)) then
set dmg = (RMinBJ(20000, 2000.0 * diff) + RMinBJ(0.01, 0.001 * diff) * BlzGetUnitMaxHP(u)) * T32_PERIOD
// If unit is in melee range, half damage (way harder to dodge and might take damage from multiple beams)
set tdx = GetUnitX(u)-GetUnitX(udg_Banehallow)
set tdy = GetUnitY(u)-GetUnitY(udg_Banehallow)
set dist = Pow(tdx*tdx + tdy*tdy, 0.5)
if dist <= 200.0 then
set dmg = dmg / 2
endif
call Lightning.unitToPoint(u, GetUnitX(u), GetUnitY(u), 0.0, 400, true, 0.03, "AFOD", 0)
call UnitDamageTarget(udg_Banehallow, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
endif
call GroupRemoveUnit(tmpG, u)
endloop
set u = null
endfunction
private function ClockLightStart takes nothing returns nothing
call TimerStart(GetExpiredTimer(), 3.0, true, function ClockLightLoop)
endfunction
private function LightningCageLoop takes nothing returns nothing
local integer i
local real angle = 0.0
local real nb_of_points = 32
local real array current_x
local real array current_y
local real array new_x
local real array new_y
local real array offset
set offset[1] = 550.0
set offset[2] = 750.0
set offset[3] = 950.0
set i = 0
loop
set i = i+1
exitwhen i > banehallow_phase
set current_x[i] = final_boss_x + offset[i]*Cos(angle*bj_DEGTORAD)
set current_y[i] = final_boss_y + offset[i]*Sin(angle*bj_DEGTORAD)
endloop
loop
set angle = angle + 360.0/nb_of_points
exitwhen angle > 360.0
set i = 0
loop
set i = i +1
exitwhen i > banehallow_phase
set new_x[i] = final_boss_x + offset[i]*Cos(angle*bj_DEGTORAD)
set new_y[i] = final_boss_y + offset[i]*Sin(angle*bj_DEGTORAD)
call Lightning.pointToPoint(current_x[i],current_y[i], new_x[i],new_y[i], 100.0,100.0, true, 5.0, "LEAS", 2)
set current_x[i] = new_x[i]
set current_y[i] = new_y[i]
endloop
endloop
endfunction
private function LightningCageDamage takes nothing returns nothing
local unit u = null
local real dmg = 0
local real diff = I2R(udg_Difficulty)
if udg_Banehallow == null or not IsUnitAliveBJ(udg_Banehallow) then
return
endif
// Get list of targets
call LightningUtils.enumUnits(tmpG, Lightning.instance, 100.0)
loop
set u = FirstOfGroup(tmpG)
exitwhen u == null
if not IsUnitAlly(u, GetOwningPlayer(udg_Banehallow)) then
// Compute damage
set dmg = (RMinBJ(20000.0, 2000.0 * Pow(diff, 0.5)) + RMinBJ(0.05, 0.005 * Pow(diff, 0.5)) * BlzGetUnitMaxHP(u)) * T32_PERIOD
call Lightning.unitToPoint(u, GetUnitX(u), GetUnitY(u), 0.0, 400, true, 0.03, "AFOD", 0)
call UnitDamageTarget(udg_Banehallow, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
endif
call GroupRemoveUnit(tmpG,u)
endloop
set u = null
endfunction
private function LightningCageStart takes nothing returns nothing
call TimerStart(GetExpiredTimer(), 7.0, true, function LightningCageLoop)
endfunction
private function TimedRainAoEDamage takes nothing returns nothing
local unit u = null
local real dmg = 0
local real diff = I2R(udg_Difficulty)
if udg_Banehallow == null or not IsUnitAliveBJ(udg_Banehallow) then
return
endif
call LightningUtils.enumUnits(tmpG, Lightning.instance, 100.0)
loop
set u = FirstOfGroup(tmpG)
exitwhen u == null
if not IsUnitAlly(u, GetOwningPlayer(udg_Banehallow)) then
set dmg = (RMinBJ(20000.0, 2000.0 * Pow(diff, 0.5)) + RMinBJ(0.2, 0.02 * Pow(diff, 0.5)) * BlzGetUnitMaxHP(u)) //* T32_PERIOD
call UnitDamageTarget(udg_Banehallow, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
endif
call GroupRemoveUnit(tmpG,u)
endloop
set u = null
endfunction
private function init takes nothing returns nothing
//call LightningUtils.registerGradientKey(1)
call Lightning.registerUpdateEvent(1, function ClockLightDamage)
call Lightning.registerUpdateEvent(2, function LightningCageDamage)
call Lightning.registerUpdateEvent(3, function TimedRainAoEDamage)
endfunction
struct Banehallowphase
static method phase1 takes nothing returns nothing
set final_boss_x = GetUnitX(udg_Banehallow)
set final_boss_y = GetUnitY(udg_Banehallow)
set banehallow_phase = 1
call TimerStart(CreateTimer(), 0.1, false, function LightningCageStart)
call TimerStart(CreateTimer(), 10.0, false, function ClockLightStart)
endmethod
static method phase2 takes nothing returns nothing
set banehallow_phase = 2
endmethod
static method phase3 takes nothing returns nothing
set banehallow_phase = 3
endmethod
endstruct
endlibrary
function Trig_MuradinClap_Conditions takes nothing returns boolean
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'Hmbr' ) ) then
return false
endif
if ( not ( GetSpellAbilityId() == 'AHtc' ) ) then
return false
endif
return true
endfunction
function Trig_MuradinClap_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
call PlaySoundOnUnitBJ( gg_snd_MuradinDoubleClap, 100, casterUnit )
call TriggerSleepAction( 0.10 )
call UnitAddAbilityBJ( 'A04P', casterUnit )
call IssueImmediateOrder(casterUnit,"creepthunderclap")
call TriggerSleepAction( 0.50 )
call UnitRemoveAbilityBJ( 'A04P', casterUnit )
set casterUnit = null
endfunction
//===========================================================================
function InitTrig_MuradinClap_JASS takes nothing returns nothing
set gg_trg_MuradinClap_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_MuradinClap_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_MuradinClap_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_MuradinClap_JASS, Condition( function Trig_MuradinClap_Conditions ) )
call TriggerAddAction( gg_trg_MuradinClap_JASS, function Trig_MuradinClap_Actions )
endfunction
function Trig_MuradinBolt_Conditions takes nothing returns boolean
if ( not ( GetUnitTypeId(GetTriggerUnit()) == 'Hmbr' ) ) then
return false
endif
if ( not ( GetSpellAbilityId() == 'AHtb' ) ) then
return false
endif
return true
endfunction
function Trig_MuradinBolt_Actions takes nothing returns nothing
local unit casterUnit = GetTriggerUnit()
local unit targetUnit = GetSpellTargetUnit()
call PlaySoundOnUnitBJ( gg_snd_MuradinDoubleBolt, 100, casterUnit )
call TriggerSleepAction( 0.10 )
call UnitAddAbilityBJ( 'A058', casterUnit )
call IssueTargetOrderBJ(casterUnit,"creepthunderbolt",targetUnit)
call TriggerSleepAction( 0.50 )
call UnitRemoveAbilityBJ( 'A058', casterUnit )
set targetUnit = null
set casterUnit = null
endfunction
//===========================================================================
function InitTrig_MuradinBolt_JASS takes nothing returns nothing
set gg_trg_MuradinBolt_JASS = CreateTrigger( )
call DisableTrigger( gg_trg_MuradinBolt_JASS )
call TriggerRegisterAnyUnitEventBJ( gg_trg_MuradinBolt_JASS, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_MuradinBolt_JASS, Condition( function Trig_MuradinBolt_Conditions ) )
call TriggerAddAction( gg_trg_MuradinBolt_JASS, function Trig_MuradinBolt_Actions )
endfunction