// Import non-standard native function to make life easier
native UnitAlive takes unit id returns boolean
// ==============================================================
// UTILITY FUNCTIONS
// Below follows utility functions, which can be called from all
// triggers. These are designed to make the life easier for the
// map makers.
// ==============================================================
//==================================================================================================================
// SOUND
//==================================================================================================================
//
// Called when the PlayTimedSound sound timer elapses.
// Plays the sound at input position and then queues up another timer to destroy the sound after it has finished playing.
//
function PlayTimedSound_TimerElapsed takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer timer_handle = GetHandleId(t)
local string file_name = LoadStr(udg_timer_hashtable, timer_handle, 0)
local location position = LoadLocationHandle(udg_timer_hashtable, timer_handle, 1)
local sound sound_handle = CreateSound(file_name, false, true, true, 10, 10, "")
local integer sound_duration_msec = GetSoundFileDuration(file_name)
local real sound_duration_sec = (sound_duration_msec/1000.0)
call SetSoundDistances(sound_handle, 600.00, 10000.00)
call SetSoundDistanceCutoff(sound_handle, 3000.00)
call SetSoundPosition(sound_handle, GetLocationX(position), GetLocationY(position), 0 )
call SetSoundDuration(sound_handle, sound_duration_msec)
call SetSoundVolume(sound_handle, 127)
call StartSound(sound_handle)
call KillSoundWhenDone(sound_handle)
call RemoveLocation(position)
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_timer_hashtable, timer_handle)
set position = null
set t = null
set sound_handle = null
endfunction
//
// Takes a sound file name, location and a delay value and creates a timer
// that will play the entered sound at the target location after the delay has passed.
//
function PlayTimedSound takes string file_name, location position, real delay returns nothing
local timer sound_timer = CreateTimer()
local integer timer_handle = GetHandleId(sound_timer)
call SaveStr(udg_timer_hashtable, timer_handle, 0, file_name)
call SaveLocationHandle(udg_timer_hashtable, timer_handle, 1, position)
call TimerStart(sound_timer, delay, false, function PlayTimedSound_TimerElapsed)
set sound_timer = null
endfunction
//==================================================================================================================
// DAMAGE
//==================================================================================================================
//
// Called when the AOETimedDamage timer elapses.
// Will take units nearby input position and cause damage to them.
//
function AOETimedDamage_TimerElapsed takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer timer_handle = GetTimerData(t)
local location position = LoadLocationHandle(udg_timer_hashtable, timer_handle, 1) // Position
local unit attacker = LoadUnitHandle(udg_timer_hashtable, timer_handle, 0) // Attacker
local real radius = LoadReal(udg_timer_hashtable, timer_handle, 2) // Radius
local real damage = LoadReal(udg_timer_hashtable, timer_handle, 3) // Damage
local group targets_group = GetUnitsInRangeOfLocAll(radius, position)
local unit u
local attacktype attack_type = ConvertAttackType(LoadInteger(udg_timer_hashtable, timer_handle, 4))
local damagetype damage_type = ConvertDamageType(LoadInteger(udg_timer_hashtable, timer_handle, 5))
local boolean friendly_fire = LoadBoolean(udg_timer_hashtable, timer_handle, 6) // Friendly Fire
local boolean friendly_building_fire = LoadBoolean(udg_timer_hashtable, timer_handle, 7) // Friendly Building Fire
local player attacker_owner = GetOwningPlayer(attacker)
loop
set u = FirstOfGroup(targets_group)
exitwhen u == null
if UnitAlive(u) and (friendly_fire or IsUnitEnemy(u, attacker_owner)) and (friendly_building_fire or not (IsUnitAlly(u, attacker_owner) and IsUnitType(u, UNIT_TYPE_STRUCTURE))) then
call UnitDamageTarget(attacker, u, damage, true, false, attack_type, damage_type, null)
endif
call GroupRemoveUnit(targets_group,u)
endloop
call DestroyGroup(targets_group)
call RemoveLocation(position)
call ReleaseTimer(t)
call FlushChildHashtable(udg_timer_hashtable, timer_handle)
set t = null
set position = null
set targets_group = null
set attacker = null
set attack_type = null
set damage_type = null
set attacker_owner = null
endfunction
//
// Will cause damage to an area after input amount of time has passed.
//
function AOETimedDamage takes unit attacker, location position, real radius, real delay, real damage, integer attack_type, integer damage_type, boolean friendly_fire, boolean friendly_building_fire returns nothing
local timer damage_timer = NewTimer()
local integer timer_handle = GetHandleId(damage_timer)
call SetTimerData(damage_timer, timer_handle)
call SaveUnitHandle(udg_timer_hashtable, timer_handle, 0, attacker)
call SaveLocationHandle(udg_timer_hashtable, timer_handle, 1, position)
call SaveReal(udg_timer_hashtable, timer_handle, 2, radius)
call SaveReal(udg_timer_hashtable, timer_handle, 3, damage)
call SaveInteger(udg_timer_hashtable, timer_handle, 4, attack_type)
call SaveInteger(udg_timer_hashtable, timer_handle, 5, damage_type)
call SaveBoolean(udg_timer_hashtable, timer_handle, 6, friendly_fire)
call SaveBoolean(udg_timer_hashtable, timer_handle, 7, friendly_building_fire)
call TimerStart(damage_timer, delay, false, function AOETimedDamage_TimerElapsed)
set damage_timer = null
endfunction
//==================================================================================================================
// DEBUG
//==================================================================================================================
//
// Prints a debug message to all players using the input text if input boolean should_print and udg_is_testing
// are true.
//
function DebugPrint takes string text, string title, string color, boolean should_print returns nothing
if udg_is_testing and should_print then
if title != null and title != "" then
set text = "[" + title + "] " + text
endif
if color != null and color != "" then
set text = "|" + color + text + "|r"
endif
call DisplayTextToForce(GetPlayersAll(), text)
endif
endfunction
//==================================================================================================================
// String Utils
//==================================================================================================================
//Prefix with zeroes
function Int2Str2Digits takes integer intToString returns string
return SubString(I2S(intToString + 100), 1, 3)
endfunction
function Int2Str9Digits takes integer intToString returns string
return SubString(I2S(intToString + 1000000000), 1, 10)
endfunction
function base64CharFromIndex takes integer index returns string
return SubString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", index, index + 1)
endfunction
function base64IndexOfChar takes string input returns integer
local integer i = 0
loop
exitwhen base64CharFromIndex(i) == input
set i = i + 1
endloop
return i
endfunction
function intToBase64 takes integer input, integer capResultChars returns string
local integer currentValue = input
local string result = ""
loop
//call BJDebugMsg("current result=" + result + ", currentValue=" + I2S(currentValue))
set result = base64CharFromIndex(ModuloInteger(currentValue, 64)) + result
set currentValue = currentValue / 64
exitwhen currentValue == 0
endloop
//call BJDebugMsg("Final result=" + result + ", currentValue=" + I2S(currentValue))
return SubString(result + "====", 0, capResultChars)
endfunction
function fromBase64 takes string input, integer maxLength returns integer
local integer result = 0
local integer i = 0
local string currentChar
loop
set currentChar = SubString(input, i, i + 1)
exitwhen i > (maxLength - 1) or currentChar == "="
set i = i + 1
set result = result * 64 + base64IndexOfChar(currentChar)
endloop
return result
endfunction
//==================================================================================================================
// Multiboard Utils
//==================================================================================================================
function PlayerToMultboardRow takes player p returns integer
local integer i = 0
local integer result = 0
local player tempPlayer
loop
set tempPlayer = Player(i)
if GetPlayerSlotState(tempPlayer) != PLAYER_SLOT_STATE_EMPTY then
set result = result + 1
endif
set i = i + 1
exitwhen i == 4 or tempPlayer == p
endloop
set tempPlayer = null
return 3 + result
endfunction
Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
al_cavalry_direction | real | No | |
al_cavalry_fx_alpha | integer | No | |
al_cavalry_knights_fx | effect | Yes | |
al_cavalry_points | location | Yes | |
al_charge_group | group | No | |
al_charge_remaining_dist | real | No | |
al_charge_target | unit | No | |
al_hero | unit | No | |
al_leadership_units | group | No | |
al_retailiation_floating_text | texttag | No | |
al_slash_execute_talent | boolean | No | |
al_slash_vfx | effect | No | |
al_stance_change | abilcode | Yes | |
al_stance_index | integer | No | -1 |
al_stance_order_ids | integer | Yes | |
al_stance_specific_real | real | No | |
al_stance_specific_real2 | real | No | |
al_stance_specific_timer | timer | No | |
al_stance_used_abilities | abilcode | Yes | |
AOEDamageEvent | real | No | |
AOEDamageSource | unit | No | |
armor_penetration_flat | real | Yes | |
armor_penetration_percent | real | Yes | |
ARMOR_TYPE_ETHEREAL | integer | No | |
ARMOR_TYPE_FLESH | integer | No | |
ARMOR_TYPE_METAL | integer | No | |
ARMOR_TYPE_NONE | integer | No | |
ARMOR_TYPE_STONE | integer | No | |
ARMOR_TYPE_WOOD | integer | No | |
ArmorTypeDebugStr | string | Yes | |
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 | |
auto_gold_share_player_group | force | No | |
auto_hero_selection | boolean | Yes | true |
battle_arena_flag | effect | Yes | |
battle_arena_init_counter | integer | No | |
battle_arena_initialize | boolean | No | |
battle_arena_ongoing | boolean | No | |
battle_arena_rune_list | itemcode | Yes | |
battle_arena_rune_spawn_points | rect | Yes | |
battle_arena_team_east | group | No | |
battle_arena_team_west | group | No | |
battle_arena_timer | timer | No | |
battle_arena_visibility_modify | fogmodifier | Yes | |
battle_arena_wave_music_name | string | No | |
blocked_amount | real | No | |
bomb | abilcode | Yes | A05T |
castle_warning_hps | real | Yes | |
castle_warning_index | integer | No | 6 |
center_player_spawn_point | location | No | |
CenterPoint | location | No | |
cinematic_multiboard | multiboard | No | |
ClearDamageEvent | trigger | No | |
COLOR_BLUE | string | No | c000000FF |
COLOR_GREEN | string | No | c0000FF00 |
COLOR_RED | string | No | c00FF0000 |
COLOR_YELLOW | string | No | c00FFFF00 |
CONVERTED_ATTACK_TYPE | attacktype | Yes | |
CONVERTED_DAMAGE_TYPE | damagetype | Yes | |
CP_HiddenItems | item | Yes | |
CP_HiddenItemsIndex | integer | No | |
CP_Item | item | No | |
CP_Point | location | No | |
CP_PointIsWalkable | boolean | No | |
CP_Rect | rect | No | |
crit_chance | real | Yes | |
crit_damage | real | Yes | 0.00 |
current_arena | rect | No | |
current_human_players | force | No | |
current_human_players_count | integer | No | |
current_wave_nr | integer | No | |
current_wave_unit_group | group | No | |
current_weather | weathereffect | No | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DAMAGE_TYPE_ACID | integer | No | |
DAMAGE_TYPE_COLD | integer | No | |
DAMAGE_TYPE_DEATH | integer | No | |
DAMAGE_TYPE_DEFENSIVE | integer | No | |
DAMAGE_TYPE_DEMOLITION | integer | No | |
DAMAGE_TYPE_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 | |
DamageBlockingAbility | abilcode | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventArmorPierced | real | No | |
DamageEventArmorT | integer | No | |
DamageEventAttackT | integer | No | |
DamageEventDamageT | integer | No | |
DamageEventDefenseT | integer | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageEventWeaponT | integer | No | |
DamageFilterAttackT | integer | No | |
DamageFilterDamageT | integer | No | |
DamageFilterMinAmount | real | No | |
DamageFilterSource | unit | No | |
DamageFilterSourceB | integer | No | |
DamageFilterSourceT | integer | No | |
DamageFilterTarget | unit | No | |
DamageFilterTargetB | integer | No | |
DamageFilterTargetT | integer | No | |
DamageFilterType | integer | No | |
DamageModifierEvent | real | 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 | |
dbg_dialog | dialog | No | |
dbg_dialog_close_btn | button | No | |
dbg_dialog_crit_print_btn | button | No | |
dbg_dialog_crit_print_strings | string | Yes | |
dbg_dialog_crit_print_values | boolean | Yes | |
dbg_dialog_dmg_print_btn | button | No | |
dbg_dialog_dmg_print_strings | string | Yes | |
dbg_dialog_dmg_print_values | boolean | Yes | |
dbg_dialog_hndl_print_btn | button | No | |
dbg_dialog_hndl_print_strings | string | Yes | |
dbg_dialog_hndl_print_values | boolean | Yes | |
dbg_dialog_indices | integer | Yes | |
dbg_dialog_p_rdy_print_btn | button | No | |
dbg_dialog_p_rdy_print_strings | string | Yes | |
dbg_dialog_p_rdy_print_values | boolean | Yes | |
debug_fog_value | real | No | |
debug_print_crit | boolean | No | |
debug_print_damage | boolean | No | |
debug_print_handle | boolean | No | |
debug_print_player_ready | boolean | No | |
DEFENSE_TYPE_DIVINE | integer | No | |
DEFENSE_TYPE_FORTIFIED | integer | No | |
DEFENSE_TYPE_HEAVY | integer | No | |
DEFENSE_TYPE_HERO | integer | No | |
DEFENSE_TYPE_LIGHT | integer | No | |
DEFENSE_TYPE_MEDIUM | integer | No | |
DEFENSE_TYPE_NORMAL | integer | No | |
DEFENSE_TYPE_UNARMORED | integer | No | |
DefenseTypeDebugStr | string | Yes | |
difficulty_dmg_factor | real | No | 1.00 |
difficulty_hp_factor | real | No | 1.00 |
difficulty_player_count | integer | No | |
difficulty_starting_h_pot | integer | No | |
difficulty_starting_life | integer | No | |
difficulty_starting_m_pot | integer | No | |
DmgEvBracers | itemcode | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
dv_choke_drain_force_talent | boolean | No | |
dv_choke_target | unit | No | |
dv_choke_timer | timer | No | |
dv_exploit_weakness_talent | boolean | No | |
dv_force_pull | boolean | No | |
dv_force_push_min_range | real | No | 325.00 |
dv_force_push_scaling_range | real | No | 325.00 |
dv_hero | unit | No | |
dv_lightning_shock_talent | boolean | No | |
EnhancedDamageTarget | unit | No | |
exhume_corpse_based | ability | Yes | |
extra_life | integer | Yes | |
g_bool1 | boolean | No | |
g_group1 | group | No | |
g_group2 | group | No | |
g_group3 | group | No | |
g_int1 | integer | No | |
g_loc1 | location | No | |
g_player1 | player | No | |
g_real1 | real | No | |
g_real2 | real | No | |
g_real3 | real | No | |
g_real4 | real | No | |
g_text | texttag | Yes | |
g_unit1 | unit | No | |
g_unit2 | unit | No | |
hero_select_index | integer | No | |
hero_select_regions | rect | Yes | |
hero_select_units | unit | Yes | |
heroes_hash | hashtable | No | |
heroes_unit_group | group | No | |
hh_ability_damage_modifier | real | No | 1.00 |
hh_double_strike_shurik_talent | boolean | No | |
hh_hero | unit | No | |
hh_is_init | boolean | No | |
hh_living_shadow_mirror_percen | real | No | 0.50 |
hh_living_shadow_time_modifier | real | No | 0.00 |
hh_living_shadows | group | No | |
hh_living_shadows_timers | timer | Yes | |
hh_ninja_strike_status | integer | No | |
hh_ninja_strike_target | location | No | |
hh_tripple_shadow_talent | boolean | No | |
HideDamageFrom | boolean | Yes | |
hit_is_crit | boolean | No | |
hit_is_super_crit | boolean | No | |
huge_slime_sizes | unitcode | Yes | |
is_hero_taken | boolean | Yes | |
is_testing | boolean | No | false |
IsDamageAttack | boolean | No | |
IsDamageCode | boolean | No | |
IsDamageMelee | boolean | No | |
IsDamageRanged | boolean | No | |
IsDamageSpell | boolean | No | |
IsUnitBeingKnockedBack | boolean | Yes | |
item_hash | hashtable | No | |
K2DAmphibious | boolean | Yes | |
K2DAngle | real | Yes | |
K2DBounce | boolean | Yes | |
K2DCollision | real | Yes | |
K2DCos | real | Yes | |
K2DCosD1 | real | Yes | |
K2DCosD2 | real | Yes | |
K2DCosH | real | Yes | |
K2DDebrisKiller | unit | No | |
K2DDestRadius | real | Yes | |
K2DDistanceLeft | real | Yes | |
K2DFlying | boolean | Yes | |
K2DFreeze | boolean | Yes | |
K2DFriction | real | Yes | |
K2DFXModel | string | Yes | |
K2DFXRate | real | Yes | |
K2DFXTimeLeft | real | Yes | |
K2DHeight | real | Yes | |
K2DHeightThreshold | real | Yes | |
K2DImpact | trigger | Yes | |
K2DItem | item | No | |
K2DItemOffset | boolean | No | |
K2DItemsFound | boolean | No | |
K2DKillTrees | boolean | Yes | |
K2DLastX | real | Yes | |
K2DLastY | real | Yes | |
K2DMaxDestRadius | real | No | |
K2DMaxX | real | No | |
K2DMaxY | real | No | |
K2DMinX | real | No | |
K2DMinY | real | No | |
K2DNext | integer | Yes | |
K2DOverride | boolean | Yes | |
K2DPause | boolean | Yes | |
K2DPrev | integer | Yes | |
K2DRadius | integer | Yes | |
K2DRegion | rect | No | |
K2DSimple | boolean | Yes | |
K2DSin | real | Yes | |
K2DSinD1 | real | Yes | |
K2DSinD2 | real | Yes | |
K2DSinH | real | Yes | |
K2DSource | unit | Yes | |
K2DTimeLeft | real | Yes | |
K2DTimeout | real | No | |
K2DTimer | timer | No | |
K2DUnbiasedCollision | boolean | Yes | |
K2DVelocity | real | Yes | |
K2DX | real | No | |
K2DY | real | No | |
kh_amplify_magic_index | integer | No | |
kh_blizzard_acceleration | real | No | |
kh_blizzard_area | rect | No | |
kh_blizzard_base_accelerate_ps | real | No | 3.00 |
kh_blizzard_base_shards_ps | real | No | 3.50 |
kh_blizzard_is_channeling | boolean | No | |
kh_blizzard_manacost | real | No | |
kh_blizzard_manacost_inc_ps | real | No | 3.00 |
kh_blizzard_manacost_text | texttag | No | |
kh_blizzard_ramp_down_counter | integer | No | -1 |
kh_blizzard_shards_per_second | real | No | |
kh_blizzard_slow_unit | unit | No | |
kh_blizzard_timer | timer | No | |
kh_hero | unit | No | |
Knockback2DAmphibious | boolean | No | |
Knockback2DAngle | real | No | |
Knockback2DBounces | boolean | No | |
Knockback2DCollision | real | No | |
Knockback2DDefaultBounce | boolean | No | |
Knockback2DDefaultDestRadius | real | No | |
Knockback2DDefaultFriction | real | No | |
Knockback2DDefaultFX | string | No | |
Knockback2DDefaultFXRate | real | No | |
Knockback2DDefaultGravity | real | No | |
Knockback2DDefaultKillTrees | boolean | No | |
Knockback2DDefaultPause | boolean | No | |
Knockback2DDestRadius | real | No | |
Knockback2DDistance | real | No | |
Knockback2DFriction | real | No | |
Knockback2DFXRate | real | No | |
Knockback2DGravity | real | No | |
Knockback2DHeight | real | No | |
Knockback2DKillTrees | boolean | No | |
Knockback2DLoopFX | string | No | |
Knockback2DOnImpact | trigger | No | |
Knockback2DOverride | boolean | No | |
Knockback2DPause | boolean | No | |
Knockback2DRobustPathing | integer | No | |
Knockback2DSimple | boolean | No | |
Knockback2DSource | unit | No | |
Knockback2DTime | real | No | |
Knockback2DTreeOrDebris | string | No | |
Knockback2DUnbiasedCollision | boolean | No | |
Knockback2DUnit | unit | No | |
last_market_exp | integer | No | |
last_market_tot_gold | integer | No | |
last_market_wave | integer | No | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
LethalDamageEvent | real | No | |
LethalDamageHP | real | No | |
load_string | string | No | |
LoopInt | integer | No | |
mammoth_tank_hero | unit | No | UnitNull |
mc_active_targeting_talent | boolean | No | |
mc_available_weapon_bit_array | integer | No | |
mc_backup_weapon_fx | effect | No | |
mc_backup_weapon_index | integer | No | -2 |
mc_current_weapon_index | integer | No | -1 |
mc_fresh_weapon_talent | integer | No | |
mc_hero | unit | No | |
mc_random_sound_variation | boolean | No | |
mc_weapon_damage | real | No | |
mc_weapon_fx | effect | No | |
mc_weapons_abilities | abilcode | Yes | |
medivh_obelisk_region | rect | Yes | |
morph_level | integer | No | |
mt_baderbomber | unit | No | |
mt_motherland_dmgp | real | No | |
mt_motherland_hps | real | No | |
mt_motherland_hpsmhp | real | No | |
mt_motherland_unitgroup | group | No | |
mt_parabomb_timer | timer | No | |
mt_parabombs_done | boolean | No | |
mt_parabombs_dropped | integer | No | |
mt_parabombs_move_angle | real | No | |
mt_parabombs_mt_point | location | No | |
mt_parabombs_target | location | No | |
mt_tusk_cooldown_timer | timer | No | |
mt_tusk_radius | real | No | 125.00 |
mt_tusk_range | real | No | 600.00 |
mt_tusk_target | unit | No | |
mt_v2_krak_talent | boolean | No | |
mt_v2_tusk_targeting_talent | boolean | No | |
NextDamageIsAttack | boolean | No | |
NextDamageIsMelee | boolean | No | |
NextDamageIsRanged | boolean | No | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
NextDamageWeaponT | integer | No | |
noDeathOption | boolean | No | |
on_kill_health_stone | real | Yes | |
on_kill_mana_stone | real | Yes | |
orc_juggernaught_barrage_vm | fogmodifier | Yes | |
orc_juggernaught_target_point | location | No | |
play_intro | boolean | No | |
player_is_ready | boolean | Yes | true |
player_kills | integer | Yes | |
player_to_player_group | force | Yes | |
point_of_samus | location | No | |
Radians_QuarterPi | real | No | |
Radians_QuarterTurn | real | No | |
Radians_Turn | real | No | |
sa_bomb_mana_cost_reset | timer | No | |
sa_bomb_mana_cost_value | integer | No | 50 |
sa_bomb_shielding_talent | boolean | No | |
sa_charge_beam_fired | boolean | No | |
sa_charge_beam_floating_text | texttag | No | |
sa_charge_beam_real_target | unit | No | |
sa_charge_beam_tick | integer | No | |
sa_charge_beam_timer | timer | No | |
sa_charge_sfx_1 | effect | No | |
sa_charge_up_bomb_talent | boolean | No | |
sa_charged_up_weapon_talent | boolean | No | |
sa_overcharge_missiles_talent | boolean | No | |
sa_remnant_energy_active | boolean | No | |
samus_boost_angle | real | No | |
samus_boost_distance_left | real | No | |
samus_boost_sfx | effect | No | |
samus_boost_target_units | group | No | |
samus_charge_counter | integer | No | |
samus_charge_sfx_2 | effect | No | |
samus_hero | unit | No | |
samus_point_of_ability | location | No | |
setup_wave_index | integer | No | |
spawner_area | rect | Yes | |
spawner_area_allowed_index | integer | No | |
spell_power | integer | Yes | |
spell_power_multiplier | real | Yes | 0.00 |
SpellDamageAbility | abilcode | No | |
stackable_items | itemcode | Yes | pghe |
target_unit_samus | unit | No | |
temp_ability | ability | No | |
temp_bool | boolean | No | |
temp_destructible_type | destructablecode | No | |
temp_integer1 | integer | No | |
temp_integer2 | integer | No | |
temp_integer3 | integer | No | |
temp_integer4 | integer | No | |
temp_integer5 | integer | No | |
temp_item | item | No | |
temp_itemtype | itemcode | No | |
temp_lightning | lightning | No | |
temp_move_point1 | location | No | |
temp_move_point2 | location | No | |
temp_move_point3 | location | No | |
temp_move_point4 | location | No | |
temp_player | player | No | |
temp_player_group | force | No | |
temp_point | location | No | |
temp_real1 | real | No | |
temp_real2 | real | No | |
temp_real3 | real | No | |
temp_real4 | real | No | |
temp_real5 | real | No | |
temp_real6 | real | No | |
temp_region | rect | No | |
temp_special_effect | effect | No | |
temp_string | string | No | |
temp_string2 | string | No | |
temp_terrain_type | terraintype | No | |
temp_timer | timer | No | |
temp_unit | unit | No | |
temp_unit2 | unit | No | |
temp_unit3 | unit | No | |
temp_unit_group | group | No | |
temp_unit_group2 | group | No | |
temp_unit_type | unitcode | No | |
timer_hashtable | hashtable | No | |
total_gold_earned | integer | No | |
translocator_unit | unit | Yes | |
ts_hero | unit | No | |
ts_psiassault_mana_per_bonus | integer | No | 550 |
ts_psiassault_projectiles_base | integer | No | 6 |
ts_psiassault_psi_surge_talent | boolean | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitDamageRegistered | boolean | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes | |
wave_completed | boolean | No | |
wave_current | real | No | 0.00 |
wave_gold_reward | integer | Yes | |
wave_multiboard | multiboard | No | |
wave_spawn_pos | location | Yes | |
wave_specific_bool1 | boolean | No | |
wave_specific_group | group | No | |
wave_specific_hashtable | hashtable | No | |
wave_specific_int1 | integer | No | |
wave_specific_int2 | integer | No | |
wave_specific_real1 | real | No | |
wave_specific_timer | timer | No | |
wave_specific_timer2 | timer | No | |
wave_specific_unit | unit | No | |
wave_specific_vfx | effect | No | |
wave_unit_amount_var1 | integer | Yes | |
wave_unit_amount_var2 | integer | Yes | |
wave_unit_amount_var3 | integer | Yes | |
wave_unit_diabolic_count | integer | Yes | |
wave_unit_type_var1 | unitcode | Yes | |
wave_unit_type_var2 | unitcode | Yes | |
wave_unit_type_var3 | unitcode | 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 | |
ws_ints1 | integer | Yes | |
ws_reals1 | real | Yes | |
ws_timers1 | timer | Yes | |
ws_vfxs1 | effect | Yes | |
x | real | No | |
y | real | No |
// Conditions: Only allow repick if the game has not started yet.
function Trig_RepickHero_Conditions takes nothing returns boolean
return not ( udg_player_is_ready[1] and udg_player_is_ready[2] and udg_player_is_ready[3] and udg_player_is_ready[4] )
endfunction
function Trig_RepickHero_HeroFilter takes nothing returns boolean
return ( IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) == true )
endfunction
// Creates a Hero Selector unit, removes the previous hero and pans the camera to the start location.
function Trig_RepickHero_RemoveHeroAndSpawnSelector takes unit to_show, unit to_remove, integer hero_index returns nothing
local location start_point = GetPlayerStartLocationLoc(GetTriggerPlayer())
local player curr_player = GetTriggerPlayer()
call GroupRemoveUnit(udg_heroes_unit_group, to_remove)
call RemoveUnit( to_remove )
call ShowUnitShow( to_show )
call CreateNUnitsAtLoc( 1, 'ewsp', curr_player, start_point, bj_UNIT_FACING )
call PanCameraToTimedLocForPlayer( curr_player, start_point, 0.50 )
set udg_is_hero_taken[hero_index] = false
call DisplayTextToForce( GetPlayersAll(), "|cffffcc00" + GetPlayerName(curr_player) + " chose to repick hero|r" )
call RemoveLocation(start_point)
set start_point = null
set curr_player = null
endfunction
// Does appropiate actions depending on unit type.
function Trig_RepickHero_Repick takes nothing returns nothing
local integer unit_type_id = GetUnitTypeId(GetEnumUnit())
local unit enum_unit = GetEnumUnit()
// DARTH
if ( unit_type_id == 'O000' ) then
call Trig_RepickHero_RemoveHeroAndSpawnSelector( gg_unit_O000_0019, enum_unit, 0 )
set udg_dv_hero = null
// SAMUS
elseif( unit_type_id == 'E000' or unit_type_id == 'E001' ) then
call SetPlayerAbilityAvailable( GetTriggerPlayer(), 'A005', true )
call SetPlayerAbilityAvailable( GetTriggerPlayer(), 'A00H', true )
call SetPlayerAbilityAvailable( GetTriggerPlayer(), 'A00N', true )
call Trig_RepickHero_RemoveHeroAndSpawnSelector( gg_unit_E000_0247, enum_unit, 1 )
set udg_samus_hero = null
// MASTER CHIEF (has one unit per weapon type)
elseif( unit_type_id == 'H00A' or unit_type_id == 'H005' or unit_type_id == 'H009' or unit_type_id == 'H006' or unit_type_id == 'H007' or unit_type_id == 'H008' ) then
call Trig_RepickHero_RemoveHeroAndSpawnSelector( gg_unit_H005_0018, enum_unit, 2 )
set udg_mc_hero = null
// MAMMOTH TANK
elseif( unit_type_id == 'H00F' ) then
call Trig_RepickHero_RemoveHeroAndSpawnSelector( gg_unit_H00F_0073, enum_unit, 3 )
set udg_mammoth_tank_hero = null
// KHADGAR
elseif( unit_type_id == 'H00O' ) then
call Trig_RepickHero_RemoveHeroAndSpawnSelector( gg_unit_H00O_0193, enum_unit, 4 )
set udg_kh_hero = null
// LOTHAR (has one unit per stance)
elseif( unit_type_id == 'H00S' or unit_type_id == 'H00T' or unit_type_id == 'H00U' ) then
call Trig_RepickHero_RemoveHeroAndSpawnSelector( gg_unit_H00S_0037, enum_unit, 5 )
set udg_al_hero = null
// TASSADAR
elseif( unit_type_id == 'H00D' ) then
call Trig_RepickHero_RemoveHeroAndSpawnSelector( gg_unit_H00D_0222, enum_unit, 6 )
set udg_ts_hero = null
// HATTORI HANZOU
elseif( unit_type_id == 'E002' ) then
call Trig_RepickHero_RemoveHeroAndSpawnSelector( gg_unit_E002_0215, enum_unit, 7 )
set udg_hh_hero = null
else
// PUT NEW HEROES HERE
endif
set enum_unit = null
endfunction
// Actions: Will remove all Heroes for the Player and create an equal amount of Wisps (Hero Selector units).
function Trig_RepickHero_Actions takes nothing returns nothing
local group unit_group = GetUnitsOfPlayerMatching(GetTriggerPlayer(), Condition(function Trig_RepickHero_HeroFilter))
set udg_player_is_ready[GetConvertedPlayerId(GetTriggerPlayer())] = false
call ForGroup( unit_group, function Trig_RepickHero_Repick )
call MultiboardSetItemIconBJ( udg_wave_multiboard, 1, ( GetConvertedPlayerId(GetTriggerPlayer()) + 3 ), "ReplaceableTextures\\WorldEditUI\\Editor-Random-Unit.blp" )
call DestroyGroup( unit_group )
set unit_group = null
endfunction
//===========================================================================
function InitTrig_RepickHero takes nothing returns nothing
set gg_trg_RepickHero = CreateTrigger( )
call TriggerRegisterPlayerChatEvent( gg_trg_RepickHero, Player(0), "-repick", true )
call TriggerRegisterPlayerChatEvent( gg_trg_RepickHero, Player(1), "-repick", true )
call TriggerRegisterPlayerChatEvent( gg_trg_RepickHero, Player(2), "-repick", true )
call TriggerRegisterPlayerChatEvent( gg_trg_RepickHero, Player(3), "-repick", true )
call TriggerAddCondition( gg_trg_RepickHero, Condition( function Trig_RepickHero_Conditions ) )
call TriggerAddAction( gg_trg_RepickHero, function Trig_RepickHero_Actions )
endfunction
//
// Returns true if one of the heroes are available.
//
function Trig_RandomHero_Hero_Available takes nothing returns boolean
local integer hero_index = 0
loop
if (not udg_is_hero_taken[hero_index]) then
return true
endif
exitwhen hero_index > 7
endloop
return false
endfunction
//
// Returns true if at least one selector unit is available.
//
function Trig_RandomHero_Conditions takes nothing returns boolean
return Trig_RandomHero_Hero_Available() and CountLivingPlayerUnitsOfTypeId('ewsp', GetTriggerPlayer()) >= 1
endfunction
//
// Main actions: will choose a random hero for the Player.
// Moves the wisp to a select region to trigger the hero select "normally".
//
function Trig_RandomHero_Actions takes nothing returns nothing
local integer hero_i
local group tempGroup = GetUnitsOfPlayerAndTypeId(GetTriggerPlayer(), 'ewsp') //Get wisp
local unit wisp = GroupPickRandomUnit(tempGroup)
//local string leaderboard_icon_name
call DestroyGroup (tempGroup)
set tempGroup = null
call DisplayTextToForce( GetPlayersAll(), "TRIGSTR_141" )
// Find a random hero (should not loop forever...)
loop
set hero_i = GetRandomInt(0, 7)
exitwhen not ( udg_is_hero_taken[hero_i] )
endloop
call SetUnitX(wisp, GetRectCenterX(udg_hero_select_regions[hero_i]))
call SetUnitY(wisp, GetRectCenterY(udg_hero_select_regions[hero_i]))
set wisp = null
endfunction
//===========================================================================
function InitTrig_RandomHero takes nothing returns nothing
set gg_trg_RandomHero = CreateTrigger( )
call TriggerRegisterPlayerChatEvent( gg_trg_RandomHero, Player(0), "-random", true )
call TriggerRegisterPlayerChatEvent( gg_trg_RandomHero, Player(1), "-random", true )
call TriggerRegisterPlayerChatEvent( gg_trg_RandomHero, Player(2), "-random", true )
call TriggerRegisterPlayerChatEvent( gg_trg_RandomHero, Player(3), "-random", true )
call TriggerAddCondition( gg_trg_RandomHero, Condition( function Trig_RandomHero_Conditions ) )
call TriggerAddAction( gg_trg_RandomHero, function Trig_RandomHero_Actions )
endfunction
globals
framehandle difficulty_mainFrame = null
framehandle difficulty_titleLabel = null
framehandle difficultyEnemyEffectsLabel = null
framehandle difficultyHeroEffectLabel = null
framehandle difficulty_veryEasyButton = null
framehandle difficulty_easyButton = null
framehandle difficulty_normalButton = null
framehandle difficulty_hardButton = null
framehandle difficulty_impossibleButton = null
framehandle difficulty_customButton = null
framehandle difficulty_veryEasyEnemyLabel = null
framehandle difficulty_easyEnemyLabel = null
framehandle difficulty_normalEnemyLabel = null
framehandle difficulty_hardEnemyLabel = null
framehandle difficulty_impossibleEnemyLabel = null
framehandle difficulty_customEnemyLabel = null
framehandle difficulty_veryEasyHeroLabel = null
framehandle difficulty_easyHeroLabel = null
framehandle difficulty_normalHeroLabel = null
framehandle difficulty_hardHeroLabel = null
framehandle difficulty_impossibleHeroLabel = null
framehandle difficulty_customHeroLabel = null
trigger Trigger_difficulty_veryEasyButton = null
trigger Trigger_difficulty_easyButton = null
trigger Trigger_difficulty_normalButton = null
trigger Trigger_difficulty_hardButton = null
trigger Trigger_difficulty_impossibleButton = null
trigger Trigger_difficulty_customButton = null
endglobals
library difficultySettingsUiRaid initializer init
function difficulty_veryEasyButtonFunc takes nothing returns nothing
call BlzFrameSetEnable(difficulty_easyButton, false)
call BlzFrameSetEnable(difficulty_easyButton, true)
set udg_difficulty_hp_factor = 0.80
set udg_difficulty_dmg_factor = 0.60
set udg_difficulty_starting_life = 2
set udg_difficulty_starting_h_pot = 3
set udg_difficulty_starting_m_pot = 3
call DisplayTextToForce(GetPlayersAll(), (GetPlayerName(GetTriggerPlayer()) + " has selected difficulty: |c00a6ffbcVery Easy|r"))
call BlzFrameSetVisible(difficulty_mainFrame, false)
endfunction
function difficulty_easyButtonFunc takes nothing returns nothing
call BlzFrameSetEnable(difficulty_easyButton, false)
call BlzFrameSetEnable(difficulty_easyButton, true)
set udg_difficulty_hp_factor = 0.90
set udg_difficulty_dmg_factor = 0.80
set udg_difficulty_starting_life = 1
set udg_difficulty_starting_h_pot = 2
set udg_difficulty_starting_m_pot = 1
call DisplayTextToForce(GetPlayersAll(), (GetPlayerName(GetTriggerPlayer()) + " has selected difficulty: |c0096f064Easy|r"))
call BlzFrameSetVisible(difficulty_mainFrame, false)
endfunction
function difficulty_normalButtonFunc takes nothing returns nothing
call BlzFrameSetEnable(difficulty_normalButton, false)
call BlzFrameSetEnable(difficulty_normalButton, true)
set udg_difficulty_hp_factor = 1.0
set udg_difficulty_dmg_factor = 1.0
set udg_difficulty_starting_life = 0
set udg_difficulty_starting_h_pot = 1
set udg_difficulty_starting_m_pot = 0
call DisplayTextToForce(GetPlayersAll(), (GetPlayerName(GetTriggerPlayer()) + " has selected difficulty: |c00AEBFF1Normal|r"))
call BlzFrameSetVisible(difficulty_mainFrame, false)
endfunction
function difficulty_hardButtonFunc takes nothing returns nothing
call BlzFrameSetEnable(difficulty_hardButton, false)
call BlzFrameSetEnable(difficulty_hardButton, true)
set udg_difficulty_hp_factor = 1.33
set udg_difficulty_dmg_factor = 1.1
set udg_difficulty_starting_life = 0
set udg_difficulty_starting_h_pot = 1
set udg_difficulty_starting_m_pot = 0
call DisplayTextToForce(GetPlayersAll(), (GetPlayerName(GetTriggerPlayer()) + " has selected difficulty: |c00FF4840Hard|r"))
call BlzFrameSetVisible(difficulty_mainFrame, false)
endfunction
function difficulty_impossibleButtonFunc takes nothing returns nothing
call BlzFrameSetEnable(difficulty_hardButton, false)
call BlzFrameSetEnable(difficulty_hardButton, true)
set udg_difficulty_starting_life = 0
set udg_difficulty_hp_factor = 1.66
set udg_difficulty_dmg_factor = 1.2
set udg_difficulty_starting_life = 0
set udg_difficulty_starting_h_pot = 1
set udg_difficulty_starting_m_pot = 0
call DisplayTextToForce(GetPlayersAll(), (GetPlayerName(GetTriggerPlayer()) + " has selected difficulty: |c00600606Impossible|r"))
call BlzFrameSetVisible(difficulty_mainFrame, false)
endfunction
function difficulty_customButtonFunc takes nothing returns nothing
call BlzFrameSetEnable(difficulty_customButton, false)
call BlzFrameSetEnable(difficulty_customButton, true)
endfunction
private function init takes nothing returns nothing
set difficulty_mainFrame = BlzCreateFrame("QuestButtonPushedBackdropTemplate", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0),0,0)
call BlzFrameSetAbsPoint(difficulty_mainFrame, FRAMEPOINT_TOPLEFT, 0.240220, 0.503170)
call BlzFrameSetAbsPoint(difficulty_mainFrame, FRAMEPOINT_BOTTOMRIGHT, 0.599930, 0.196540)
set difficulty_titleLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_titleLabel, FRAMEPOINT_TOPLEFT, 0.260110, 0.491924)
call BlzFrameSetAbsPoint(difficulty_titleLabel, FRAMEPOINT_BOTTOMRIGHT, 0.407640, 0.464300)
call BlzFrameSetText(difficulty_titleLabel, "|cffffffffDifficulty Settings|r")
call BlzFrameSetEnable(difficulty_titleLabel, false)
call BlzFrameSetScale(difficulty_titleLabel, 1.71)
call BlzFrameSetTextAlignment(difficulty_titleLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
set difficultyEnemyEffectsLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficultyEnemyEffectsLabel, FRAMEPOINT_TOPLEFT, 0.375000, 0.470722)
call BlzFrameSetAbsPoint(difficultyEnemyEffectsLabel, FRAMEPOINT_BOTTOMRIGHT, 0.477770, 0.456910)
call BlzFrameSetText(difficultyEnemyEffectsLabel, "|cffFFCC00Enemy Effects|r")
call BlzFrameSetEnable(difficultyEnemyEffectsLabel, false)
call BlzFrameSetScale(difficultyEnemyEffectsLabel, 1.00)
call BlzFrameSetTextAlignment(difficultyEnemyEffectsLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
set difficultyHeroEffectLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficultyHeroEffectLabel, FRAMEPOINT_TOPLEFT, 0.485000, 0.472095)
call BlzFrameSetAbsPoint(difficultyHeroEffectLabel, FRAMEPOINT_BOTTOMRIGHT, 0.587770, 0.457730)
call BlzFrameSetText(difficultyHeroEffectLabel, "|cffFFCC00Hero Effects|r")
call BlzFrameSetEnable(difficultyHeroEffectLabel, false)
call BlzFrameSetScale(difficultyHeroEffectLabel, 1.00)
call BlzFrameSetTextAlignment(difficultyHeroEffectLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
//Very Easy
set difficulty_veryEasyButton = BlzCreateFrame("ScriptDialogButton", difficulty_mainFrame,0,0)
call BlzFrameSetPoint(difficulty_veryEasyButton, FRAMEPOINT_TOPLEFT, difficulty_mainFrame, FRAMEPOINT_TOPLEFT, 0.019780, -0.047658)
call BlzFrameSetPoint(difficulty_veryEasyButton, FRAMEPOINT_BOTTOMRIGHT, difficulty_mainFrame, FRAMEPOINT_BOTTOMRIGHT, -0.23992, 0.22527)
call BlzFrameSetText(difficulty_veryEasyButton, "|c00a6ffbcVery Easy|r")
call BlzFrameSetScale(difficulty_veryEasyButton, 1.00)
set Trigger_difficulty_veryEasyButton = CreateTrigger()
call BlzTriggerRegisterFrameEvent(Trigger_difficulty_veryEasyButton, difficulty_veryEasyButton, FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(Trigger_difficulty_veryEasyButton, function difficulty_veryEasyButtonFunc)
set difficulty_veryEasyEnemyLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_veryEasyEnemyLabel, FRAMEPOINT_TOPLEFT, 0.374490, 0.453689)
call BlzFrameSetAbsPoint(difficulty_veryEasyEnemyLabel, FRAMEPOINT_BOTTOMRIGHT, 0.479470, 0.420540)
call BlzFrameSetText(difficulty_veryEasyEnemyLabel, "|cffFFCC00Enemy hp: 80%\nEnemy dmg: 60%|r")
call BlzFrameSetEnable(difficulty_veryEasyEnemyLabel, false)
call BlzFrameSetScale(difficulty_veryEasyEnemyLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_veryEasyEnemyLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
set difficulty_veryEasyHeroLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_veryEasyHeroLabel, FRAMEPOINT_TOPLEFT, 0.485550, 0.453689)
call BlzFrameSetAbsPoint(difficulty_veryEasyHeroLabel, FRAMEPOINT_BOTTOMRIGHT, 0.590530, 0.420540)
call BlzFrameSetText(difficulty_veryEasyHeroLabel, "|cffFFCC00Extra Life: 2\nHealing Potion: 3\nMana Potion: 3|r")
call BlzFrameSetEnable(difficulty_veryEasyHeroLabel, false)
call BlzFrameSetScale(difficulty_veryEasyHeroLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_veryEasyHeroLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
//Easy
set difficulty_easyButton = BlzCreateFrame("ScriptDialogButton", difficulty_mainFrame,0,0)
call BlzFrameSetPoint(difficulty_easyButton, FRAMEPOINT_TOPLEFT, difficulty_mainFrame, FRAMEPOINT_TOPLEFT, 0.019780, -0.089860)
call BlzFrameSetPoint(difficulty_easyButton, FRAMEPOINT_BOTTOMRIGHT, difficulty_mainFrame, FRAMEPOINT_BOTTOMRIGHT, -0.23992, 0.18307)
call BlzFrameSetText(difficulty_easyButton, "|c0096f064Easy|r")
call BlzFrameSetScale(difficulty_easyButton, 1.00)
set Trigger_difficulty_easyButton = CreateTrigger()
call BlzTriggerRegisterFrameEvent(Trigger_difficulty_easyButton, difficulty_easyButton, FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(Trigger_difficulty_easyButton, function difficulty_easyButtonFunc)
set difficulty_easyEnemyLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_easyEnemyLabel, FRAMEPOINT_TOPLEFT, 0.374490, 0.412040)
call BlzFrameSetAbsPoint(difficulty_easyEnemyLabel, FRAMEPOINT_BOTTOMRIGHT, 0.479470, 0.378891)
call BlzFrameSetText(difficulty_easyEnemyLabel, "|cffFFCC00Enemy hp: 90%\nEnemy dmg: 80%|r")
call BlzFrameSetEnable(difficulty_easyEnemyLabel, false)
call BlzFrameSetScale(difficulty_easyEnemyLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_easyEnemyLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
set difficulty_easyHeroLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_easyHeroLabel, FRAMEPOINT_TOPLEFT, 0.485550, 0.412040)
call BlzFrameSetAbsPoint(difficulty_easyHeroLabel, FRAMEPOINT_BOTTOMRIGHT, 0.590530, 0.378891)
call BlzFrameSetText(difficulty_easyHeroLabel, "|cffFFCC00Extra Life: 1\nHealing Potion: 2\nMana Potion: 1|r")
call BlzFrameSetEnable(difficulty_easyHeroLabel, false)
call BlzFrameSetScale(difficulty_easyHeroLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_easyHeroLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
//Normal
set difficulty_normalButton = BlzCreateFrame("ScriptDialogButton", difficulty_mainFrame,0,0)
call BlzFrameSetPoint(difficulty_normalButton, FRAMEPOINT_TOPLEFT, difficulty_mainFrame, FRAMEPOINT_TOPLEFT, 0.019780, -0.13206)
call BlzFrameSetPoint(difficulty_normalButton, FRAMEPOINT_BOTTOMRIGHT, difficulty_mainFrame, FRAMEPOINT_BOTTOMRIGHT, -0.23992, 0.14087)
call BlzFrameSetText(difficulty_normalButton, "|c00AEBFF1Normal|r")
call BlzFrameSetScale(difficulty_normalButton, 1.00)
set Trigger_difficulty_normalButton = CreateTrigger()
call BlzTriggerRegisterFrameEvent(Trigger_difficulty_normalButton, difficulty_normalButton, FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(Trigger_difficulty_normalButton, function difficulty_normalButtonFunc)
set difficulty_normalEnemyLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_normalEnemyLabel, FRAMEPOINT_TOPLEFT, 0.374490, 0.370391)
call BlzFrameSetAbsPoint(difficulty_normalEnemyLabel, FRAMEPOINT_BOTTOMRIGHT, 0.479470, 0.337242)
call BlzFrameSetText(difficulty_normalEnemyLabel, "|cffFFCC00Enemy hp: 100%\nEnemy dmg: 100%|r")
call BlzFrameSetEnable(difficulty_normalEnemyLabel, false)
call BlzFrameSetScale(difficulty_normalEnemyLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_normalEnemyLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
set difficulty_normalHeroLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_normalHeroLabel, FRAMEPOINT_TOPLEFT, 0.485550, 0.370391)
call BlzFrameSetAbsPoint(difficulty_normalHeroLabel, FRAMEPOINT_BOTTOMRIGHT, 0.590530, 0.337242)
call BlzFrameSetText(difficulty_normalHeroLabel, "|cffFFCC00Extra Life: 0\nHealing Potion: 1\nMana Potion: 0|r")
call BlzFrameSetEnable(difficulty_normalHeroLabel, false)
call BlzFrameSetScale(difficulty_normalHeroLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_normalHeroLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
//Hard
set difficulty_hardButton = BlzCreateFrame("ScriptDialogButton", difficulty_mainFrame,0,0)
call BlzFrameSetPoint(difficulty_hardButton, FRAMEPOINT_TOPLEFT, difficulty_mainFrame, FRAMEPOINT_TOPLEFT, 0.019780, -0.17426)
call BlzFrameSetPoint(difficulty_hardButton, FRAMEPOINT_BOTTOMRIGHT, difficulty_mainFrame, FRAMEPOINT_BOTTOMRIGHT, -0.23992, 0.098664)
call BlzFrameSetText(difficulty_hardButton, "|c00FF4840Hard|r")
call BlzFrameSetScale(difficulty_hardButton, 1.00)
set Trigger_difficulty_hardButton = CreateTrigger()
call BlzTriggerRegisterFrameEvent(Trigger_difficulty_hardButton, difficulty_hardButton, FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(Trigger_difficulty_hardButton, function difficulty_hardButtonFunc)
set difficulty_hardEnemyLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_hardEnemyLabel, FRAMEPOINT_TOPLEFT, 0.374490, 0.328742)
call BlzFrameSetAbsPoint(difficulty_hardEnemyLabel, FRAMEPOINT_BOTTOMRIGHT, 0.479470, 0.295593)
call BlzFrameSetText(difficulty_hardEnemyLabel, "|cffFFCC00Enemy hp: 133%\nEnemy dmg: 110%|r")
call BlzFrameSetEnable(difficulty_hardEnemyLabel, false)
call BlzFrameSetScale(difficulty_hardEnemyLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_hardEnemyLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
set difficulty_hardHeroLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_hardHeroLabel, FRAMEPOINT_TOPLEFT, 0.485550, 0.328742)
call BlzFrameSetAbsPoint(difficulty_hardHeroLabel, FRAMEPOINT_BOTTOMRIGHT, 0.590530, 0.295593)
call BlzFrameSetText(difficulty_hardHeroLabel, "|cffFFCC00Extra Life: 0\nHealing Potion: 1\nMana Potion: 0|r")
call BlzFrameSetEnable(difficulty_hardHeroLabel, false)
call BlzFrameSetScale(difficulty_hardHeroLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_hardHeroLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
//Impossible
set difficulty_impossibleButton = BlzCreateFrame("ScriptDialogButton", difficulty_mainFrame,0,0)
call BlzFrameSetPoint(difficulty_impossibleButton, FRAMEPOINT_TOPLEFT, difficulty_mainFrame, FRAMEPOINT_TOPLEFT, 0.019780, -0.21647)
call BlzFrameSetPoint(difficulty_impossibleButton, FRAMEPOINT_BOTTOMRIGHT, difficulty_mainFrame, FRAMEPOINT_BOTTOMRIGHT, -0.23992, 0.056462)
call BlzFrameSetText(difficulty_impossibleButton, "|c00600606Impossible|r")
call BlzFrameSetScale(difficulty_impossibleButton, 1.00)
set Trigger_difficulty_impossibleButton = CreateTrigger()
call BlzTriggerRegisterFrameEvent(Trigger_difficulty_impossibleButton, difficulty_impossibleButton, FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(Trigger_difficulty_impossibleButton, function difficulty_impossibleButtonFunc)
set difficulty_impossibleEnemyLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_impossibleEnemyLabel, FRAMEPOINT_TOPLEFT, 0.374490, 0.287093)
call BlzFrameSetAbsPoint(difficulty_impossibleEnemyLabel, FRAMEPOINT_BOTTOMRIGHT, 0.479470, 0.253944)
call BlzFrameSetText(difficulty_impossibleEnemyLabel, "|cffFFCC00Enemy hp: 166%\nEnemy dmg: 120%|r")
call BlzFrameSetEnable(difficulty_impossibleEnemyLabel, false)
call BlzFrameSetScale(difficulty_impossibleEnemyLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_impossibleEnemyLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
set difficulty_impossibleHeroLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_impossibleHeroLabel, FRAMEPOINT_TOPLEFT, 0.485550, 0.287093)
call BlzFrameSetAbsPoint(difficulty_impossibleHeroLabel, FRAMEPOINT_BOTTOMRIGHT, 0.590530, 0.253944)
call BlzFrameSetText(difficulty_impossibleHeroLabel, "|cffFFCC00Extra Life: 0\nHealing Potion: 1\nMana Potion: 0|r")
call BlzFrameSetEnable(difficulty_impossibleHeroLabel, false)
call BlzFrameSetScale(difficulty_impossibleHeroLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_impossibleHeroLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
//Custom
set difficulty_customButton = BlzCreateFrame("ScriptDialogButton", difficulty_mainFrame,0,0)
call BlzFrameSetPoint(difficulty_customButton, FRAMEPOINT_TOPLEFT, difficulty_mainFrame, FRAMEPOINT_TOPLEFT, 0.019780, -0.25867)
call BlzFrameSetPoint(difficulty_customButton, FRAMEPOINT_BOTTOMRIGHT, difficulty_mainFrame, FRAMEPOINT_BOTTOMRIGHT, -0.23992, 0.014260)
call BlzFrameSetText(difficulty_customButton, "|cffFCD20DCustom|r")
call BlzFrameSetScale(difficulty_customButton, 1.00)
set Trigger_difficulty_customButton = CreateTrigger()
call BlzTriggerRegisterFrameEvent(Trigger_difficulty_customButton, difficulty_customButton, FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(Trigger_difficulty_customButton, function difficulty_customButtonFunc)
call BlzFrameSetEnable(difficulty_customButton, false)
set difficulty_customEnemyLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_customEnemyLabel, FRAMEPOINT_TOPLEFT, 0.374490, 0.245444)
call BlzFrameSetAbsPoint(difficulty_customEnemyLabel, FRAMEPOINT_BOTTOMRIGHT, 0.479470, 0.212295)
call BlzFrameSetText(difficulty_customEnemyLabel, "|cffFFCC00Cusomizable|nComing soon!|r")
call BlzFrameSetEnable(difficulty_customEnemyLabel, false)
call BlzFrameSetScale(difficulty_customEnemyLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_customEnemyLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
set difficulty_customHeroLabel = BlzCreateFrameByType("TEXT", "name", difficulty_mainFrame, "", 0)
call BlzFrameSetAbsPoint(difficulty_customHeroLabel, FRAMEPOINT_TOPLEFT, 0.485550, 0.245444)
call BlzFrameSetAbsPoint(difficulty_customHeroLabel, FRAMEPOINT_BOTTOMRIGHT, 0.590530, 0.212295)
call BlzFrameSetText(difficulty_customHeroLabel, "|cffFFCC00Customizable|nComing soon!|r")
call BlzFrameSetEnable(difficulty_customHeroLabel, false)
call BlzFrameSetScale(difficulty_customHeroLabel, 1.00)
call BlzFrameSetTextAlignment(difficulty_customHeroLabel, TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
endfunction
endlibrary
globals
constant integer ALCHANGE_STANCE_ABILITY = 'A04I'
endglobals
//Returns the amount Lothar always blocks
function ALLeadership_DefensiveBlockAmount_Percent takes nothing returns real
return 0.1
endfunction
function ALLeadership_DefensiveBlockFromArmor_Percent takes nothing returns real
return 0.4 + 0.1 * GetUnitAbilityLevel(udg_al_hero, ALCHANGE_STANCE_ABILITY)
endfunction
function ALLeadership_DefensiveRetaliation_Percent takes nothing returns real
return 0.45 + 0.05 * GetUnitAbilityLevel(udg_al_hero, ALCHANGE_STANCE_ABILITY)
endfunction
globals
constant integer PULL_ITEMS_ABILITY = 'A02S'
constant integer PUSH_ITEMS_ABILITY = 'A02T'
constant integer SWAP_ITEMS_ABILITY = 'A02U'
endglobals
function ItemManagement_PushCast takes nothing returns nothing
local integer i = 0
local unit stone = GetTriggerUnit()
local integer playerNum = GetPlayerId(GetOwningPlayer(stone))
local unit hero = LoadUnitHandle(udg_heroes_hash, playerNum, 0)
local integer freeSlots = 6 - UnitInventoryCount(hero)
local item itemToMove
loop
set itemToMove = UnitItemInSlot(stone, i)
if itemToMove != null then
call UnitAddItem(hero, itemToMove)
endif
set i = i + 1
exitwhen i >= bj_MAX_INVENTORY or freeSlots == 0
endloop
set itemToMove = null
set stone = null
set hero = null
endfunction
function ItemManagement_PullCast takes nothing returns nothing
local integer i = 0
local unit stone = GetTriggerUnit()
local integer playerNum = GetPlayerId(GetOwningPlayer(stone))
local unit hero = LoadUnitHandle(udg_heroes_hash, playerNum, 0)
local integer freeSlots = 6 - UnitInventoryCount(stone)
local item itemToMove
loop
set itemToMove = UnitItemInSlot(hero, i)
if itemToMove != null then
call UnitAddItem(stone, itemToMove)
endif
set i = i + 1
exitwhen i >= bj_MAX_INVENTORY or freeSlots == 0
endloop
set itemToMove = null
set stone = null
set hero = null
endfunction
function ItemManagement_SwapCast takes nothing returns nothing
local integer i = 0
local unit stone = GetTriggerUnit()
local integer playerNum = GetPlayerId(GetOwningPlayer(stone))
local unit hero = LoadUnitHandle(udg_heroes_hash, playerNum, 0)
local item itemHero
local item itemStone
loop
set itemHero = UnitItemInSlot(hero, i)
set itemStone = UnitItemInSlot(stone, i)
if itemHero != null then
call UnitDropItemPoint(hero, itemHero, GetUnitX(hero), GetUnitY(hero))
call UnitAddItem(hero, itemStone)
call UnitAddItem(stone, itemHero)
call UnitDropItemSlot(hero, itemStone, i)
call UnitDropItemSlot(stone, itemHero, i)
elseif itemStone != null then
call UnitAddItem(hero, itemStone)
call UnitDropItemSlot(hero, itemStone, i)
endif
set i = i + 1
exitwhen i >= bj_MAX_INVENTORY
endloop
set itemHero = null
set itemStone = null
set stone = null
set hero = null
endfunction
function InitItemManagement takes nothing returns nothing
call RegisterSpellEffectEvent(PUSH_ITEMS_ABILITY, function ItemManagement_PushCast)
call RegisterSpellEffectEvent(PULL_ITEMS_ABILITY, function ItemManagement_PullCast)
call RegisterSpellEffectEvent(SWAP_ITEMS_ABILITY, function ItemManagement_SwapCast)
endfunction
// ================================== System ===================================
// Super simple system that creates dummy-units as needed and gets them from a "stash" typically.
// This system do NOT care about angles, because with correctly setup dummy, you don't need to.
// I prefix functions with TZ to avoid collision with other systems.
// =============================== Configuration ===============================
// You MUST set DUMMY_ID to a correctly setup dummy.
// Suggestion on how to set it up:
// 1. Copy "Undead -> Melee -> Special -> Locust"
// 2. Enter the unit id "dumy" (or whatever you like, but you need to set the `DUMMY_ID` to this value!)
// 3. Set the folloing values:
// Art - Animation - Blend Time (seconds) -> 0.0
// Art - Model File -> _.mdl
// Note: Shift + Double-click to set "_.mdl" (or just enter it in the "Custom"-field)
// Art - Shadow Image -> None
// Combat - Attacks Enabled -> None
// Movment - Speed Base -> 0
// Movment - Type -> None
// (Optional) Stats - Hit Points Maximum (base) -> 99999
// (Optional) Stats - Hit Points Regeneration Rate -> 1000
// (Optional) Text - Name -> LocustDummy
// (Optional) Abilities - Normal -> Aloc,Avul
// Note: Shift + Double-click to set "Aloc,Avul"
// Credit: Uncle from Hiveworkshop. Thanks for saying how to do this again and again!
// ================================= Functions =================================
// - TZRecycle
// takes unit - Dummy to recycle, must be initalized by this system!
// Note: Must remove any added spell manually
//
// - TZGetDummy
// takes real x, real y, player owner
// Position and who should own the dummy
//
// - TZGetDummyTimed
// real x, real y, Position
// integer spell_id, Spell the dummy should come with
// real duration, Duration dummy should be usable. Typically 0.05 is enough.
// player owner Owner of the dummy for the duration.
//
// - TZGetDummyForUnitTimed (util, same as TZGetDummyTimed but uses data from source unit)
// unit source, The "original caster"
// integer spell_id, Spell the dummy should come with
// real duration Duration dummy should be usable. Typically 0.05 is enough.
//
// - TZDummyAddSpell
// unit dummy Dummy to add spell to
// integer spell_id The 4-letter spell id for ability. For example 'A001'
// =================================== Why ? ===================================
// Comment below by: DummyRecycler v1.25 by Flux
// Why is recycling a unit important?
// Because creating a unit is is one of the slowest function in the game
// and there are reports that will always leave a permanent tiny bit of
// memory (0.04 KB).
// On average, retrieving a pending Dummy is approximately 4x faster compared
// to creating a new one and recycling a Dummy compared to removing it is
// approximately 1.3x faster.
// Furthermore, if you're using a lot of "Unit has entered map" events,
// using this system will even result to even more better performance
// because retrieving Dummy units does not cause that event to run.
library TZSimpleDummyRecycler initializer init requires TimerUtils optional WorldBounds
globals
private constant integer DUMMY_ID = 'dumy' // Unit id of dummy.
private constant player DEFAULT_OWNER = Player(PLAYER_NEUTRAL_PASSIVE) //Owner when dummy is "not in use". PLAYER_NEUTRAL_PASSIVE is defined in `common.j`
//should this system handle its own indexing? Note: dummies often have "locust" and are not indexed by default by many indexers
private constant boolean INTERNAL_INDEXER = true
//should units be stored in the corner when not in use?
private constant boolean HIDE_IN_MAP_CORNER = true
private constant boolean DEBUG_MODE = false
//how many dummies should be initalized on start-up? (Will still create more dummies as needed!)
//Not implemented Yet!!! - private constant integer INIT_DUMMIES = 10
private unit array dummy_cache
private integer array dummy_spell_id
//static if INTERNAL_INDEXER then - TODO: Do not declare these variables when not needed!
private unit array dummy_indexed
private integer index_max = 0
//endif
private integer cache_max = 0
//Where units should be stored when not in use
private real hide_x
private real hide_y
endglobals
function TZRecycleDummy takes unit u returns nothing
local integer index = GetUnitUserData(u)
if dummy_spell_id[index] != 0 then
call UnitRemoveAbility(u, dummy_spell_id[index])
endif
call SetUnitOwner(u, DEFAULT_OWNER, false)
call ShowUnit(u, false)
static if HIDE_IN_MAP_CORNER then
call SetUnitX(u, hide_x)
call SetUnitY(u, hide_y)
endif
static if DEBUG_MODE then
call BJDebugMsg("Recycling dummy. UIX=" + I2S(index) + ", CacheIndex=" + I2S(cache_max) + ". SpellId=" + I2S(dummy_spell_id[index]))
endif
set dummy_cache[cache_max] = u
set cache_max = cache_max + 1
endfunction
private function TZRecycleDummyTimed takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer index = GetTimerData(t)
static if INTERNAL_INDEXER then
local unit dummy = dummy_indexed[index]
else
//Note: This is how it should work for Bribes unit indexer. If you use someting else, you need to use that system's way of
// getting the unit from the unit user data (Note: index comes from `TZGetDummyTimed`)
local unit dummy = udg_UDexUnits[index]
static if DEBUG_MODE then
if dummy == null then
call BJDebugMsg("TZSimpleDummyRecycler: Failed to get dummy! Probably an issue with indexing system!")
endif
endif
endif
call TZRecycleDummy(dummy)
call ReleaseTimer(t)
set t = null
set dummy = null
endfunction
function TZGetDummy takes real x, real y, player owner returns unit
local integer index
if cache_max == 0 then
set bj_lastCreatedUnit = CreateUnit(owner, DUMMY_ID, x, y, 0.0)
static if INTERNAL_INDEXER then
set index_max = index_max + 1
set index = index_max
call SetUnitUserData(bj_lastCreatedUnit, index)
set dummy_indexed[index] = bj_lastCreatedUnit
endif
static if DEBUG_MODE then
call BJDebugMsg("Created New dummy. UIX=" + I2S(index) + ", UnitUserData=" + I2S(GetUnitUserData(bj_lastCreatedUnit)))
endif
else
set cache_max = cache_max - 1
set bj_lastCreatedUnit = dummy_cache[cache_max]
call ShowUnit(bj_lastCreatedUnit, true)
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
set index = GetUnitUserData(bj_lastCreatedUnit)
static if DEBUG_MODE then
call BJDebugMsg("Getting recycled dummy. UIX=" + I2S(index) + ", CacheIndex=" + I2S(cache_max))
endif
endif
set dummy_spell_id[index] = 0
call SetUnitOwner(bj_lastCreatedUnit, owner, false)
return bj_lastCreatedUnit
endfunction
function TZGetDummyTimed takes real x, real y, integer spell_id, real duration, player owner returns unit
local timer t
local integer index
call TZGetDummy(x, y, owner)
set index = GetUnitUserData(bj_lastCreatedUnit)
call UnitAddAbility(bj_lastCreatedUnit, spell_id)
set dummy_spell_id[index] = spell_id
set t = NewTimerEx(index)
call TimerStart(t, duration, false, function TZRecycleDummyTimed)
set t = null
return bj_lastCreatedUnit
endfunction
function TZGetDummyForUnitTimed takes unit source, integer spell_id, real duration returns unit
return TZGetDummyTimed(GetUnitX(source), GetUnitY(source), spell_id, duration, GetOwningPlayer(source))
endfunction
function TZDummyAddSpell takes unit dummy, integer spell_id returns nothing
local integer index = GetUnitUserData(dummy)
call UnitAddAbility(dummy, spell_id)
set dummy_spell_id[index] = spell_id
endfunction
private function init takes nothing returns nothing
static if HIDE_IN_MAP_CORNER then
if bj_mapInitialPlayableArea != null then
set hide_x = GetRectMaxX(bj_mapInitialPlayableArea)
set hide_y = GetRectMaxY(bj_mapInitialPlayableArea)
static if DEBUG_MODE then
call BJDebugMsg("TZSimpleDummyRecycler.onInit: bj_mapInitialPlayableArea was initalized! hide_x=" + R2S(hide_x) + ", hide_y=" + R2S(hide_y))
endif
else
call BJDebugMsg("TZSimpleDummyRecycler.init: bj_mapInitialPlayableArea not initalized!? (Defined in Blizzard.j)")
endif
endif
endfunction
endlibrary
library TZSimpleDummyRecyclerUtils requires TZSimpleDummyRecycler
function GetDummy takes unit source, integer spell_id returns unit
call TZGetDummy(GetUnitX(source), GetUnitY(source), GetOwningPlayer(source))
call TZDummyAddSpell(bj_lastCreatedUnit, spell_id)
return bj_lastCreatedUnit
endfunction
function DummyCastTarget takes unit source, unit target, integer spell_id, string spell_order returns nothing
call GetDummy(source, spell_id)
call IssueTargetOrder(bj_lastCreatedUnit, spell_order, target)
call TZRecycleDummy(bj_lastCreatedUnit)
endfunction
endlibrary
library DTCStringUtils
function R2I2S takes real r returns string
return I2S(R2I(r + 0.0001))
endfunction
function R2I2Sx100 takes real r returns string
return I2S(R2I(r*100.0 + 0.0001))
endfunction
function R2Sx100 takes real r returns string
local real scaled = r * 100.0 //I.E. r is 0.0123 -> 1.23%
local real diff = scaled - R2I(scaled + 0.000001) //diff -> (1.23 - 1.0) = 0.23
if (diff == 0.) then //0.23 > 0.06, so else
return I2S(R2I(scaled + 0.000001))
else
set diff = scaled * 10. - R2I(scaled * 10.) //12.3 - 12.0 = 0.3
if diff > 0.99 then
return R2SW(scaled + 0.000001, 1, 2)
else
return R2SW(scaled + 0.000001, 1, 1)
endif
endif
endfunction
endlibrary
globals
constant integer BONUS_DAMAGE_ABILITY = 'A049'
constant integer BONUS_ATTACK_SPEED_ABILITY = 'A07T'
constant integer STUN_ORDER_ID = 851973
constant integer STORM_BOLT_STUN_DUMMY = 'A01U'
constant integer CRIPPLE_DUMMY = 'S004'
constant integer BASHER_STUNNED_BUFF = 'B000'
constant integer STUNNED_BUFF = 'BPSE'
constant integer UNHOLY_AURA_BUFF = 'BUau'
constant integer BRILLIANCE_AURA_BUFF = 'BHab'
constant integer UNHOLY_AURA_INCONVENIENTLY_HEAVY_SHIELD = 'A06H'
constant integer UNHOLY_AURA_RING_OF_POWER = 'A02P'
constant integer BRILLIANCE_AURA_MANAFLOWIFICATOR = 'A009'
constant integer BRILLIANCE_AURA_RING_OF_POWER = 'A02Q'
constant integer BONUS_BONUS_DAMAGE = 101
constant integer BONUS_BONUS_AGILITY = 103
constant integer BONUS_BONUS_STRENGTH = 104
constant integer BONUS_BONUS_INTELLIGENCE = 105
constant real CRIT_CHANCE_PER_AGILITY = 0.00125
constant real CRIT_DAMAGE_PER_AGILITY = 0.0025
constant real SPELL_POWER_PER_INTELLIGENCE = 1.0
constant integer LAST_HUMAN_PLAYER_INDEX = 5
constant integer HERO_TABLE_SHIELD_WAVE_START = 100
constant player PASSIVE_PLAYER = Player(PLAYER_NEUTRAL_PASSIVE)
Table timerTable
Table enemyTable
unit t_unit1
location zChecker = Location(0,0)
endglobals
//call BJDebugMsg("")
function interface CoordinateAction takes real x, real y, real angle returns nothing
function interface UnitAction takes unit target, unit source returns nothing
function interface EffectAction takes effect vfx returns nothing
//==================================================================================================================
// Trigonomitry
//==================================================================================================================
function distanceBetweenXYXY takes real x1, real y1, real x2, real y2 returns real
local real dx = x1 - x2
local real dy = y1 - y2
return SquareRoot(dx * dx + dy * dy)
endfunction
function distanceBetweenUnits takes unit u1, unit u2 returns real
local real dx = GetUnitX(u1) - GetUnitX(u2)
local real dy = GetUnitY(u1) - GetUnitY(u2)
return SquareRoot(dx * dx + dy * dy)
endfunction
function distanceBetweenXYZXYZ takes real x1, real y1, real z1, real x2, real y2, real z2 returns real
local real dx = x1 - x2
local real dy = y1 - y2
local real dz = z1 - z2
return SquareRoot(dx * dx + dy * dy + dz * dz)
endfunction
function GetZHeight takes real x, real y returns real
call MoveLocation(zChecker, x, y)
return GetLocationZ(zChecker)
endfunction
//==================================================================================================================
// Math
//==================================================================================================================
//Simple Modulo function that assumes numbers equal to or greater than 0
function Mod takes integer dividend, integer divisor returns integer
return dividend - (dividend / divisor) * divisor
endfunction
function Dot takes real x1, real y1, real x2, real y2 returns real
return x1 * x2 + y1 * y2
endfunction
//Normal x,y defines the plane, together with plane_distance (how much offset it is)
//ux, uy is the coord currently being analysed. Margin is if the coord should be treated as infront with this extra margin (E.G. CollisionSize)
function IsInFrontOfPlane takes real normx, real normy, real px, real py, real ux, real uy, real margin returns boolean
local real newx = ux - px
local real newy = uy - py
local real result = (Dot(newx, newy, normx, normy) + margin)
//call BJDebugMsg("Norm: " + R2S(normx) + ", " + R2S(normy) + " - pos: " + R2S(ux) + ", " + R2S(uy) + ". result=" + R2S(result))
return result > 0
endfunction
struct EffectDelayed
effect vfx
EffectAction vfxSetup
private static method Show takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call BlzSetSpecialEffectTimeScale(vfx, 1.0)
if vfxSetup != null then
call vfxSetup.execute(vfx)
endif
call ReleaseTimer(t)
set t = null
set vfx = null
call .deallocate()
endmethod
static method PlayLater takes effect vfx, EffectAction vfxSetup, real delay, real destroyAfter returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
call BlzSetSpecialEffectTimeScale(vfx, 0.0)
set .vfx = vfx
set .vfxSetup = vfxSetup
call TimerStart(t, delay, false, function thistype.Show)
call DestroyEffectLater(vfx, destroyAfter)
set t = null
endmethod
endstruct
struct TimedCoordinateAction
real x
real y
real angle
CoordinateAction f
method destroy takes nothing returns nothing
call .deallocate()
endmethod
static method runAction takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call ReleaseTimer(t)
set t = null
call f.execute(x, y, angle)
call .destroy()
endmethod
static method setup takes real x, real y, real angle, CoordinateAction f, real delay returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
set .x = x
set .y = y
set .angle = angle
set .f = f
call TimerStart(t, delay, false, function thistype.runAction)
set t = null
endmethod
endstruct
struct OverTimeUnitAction
unit target
unit source
UnitAction f
timer t
timer tEnd
method destroy takes nothing returns nothing
call ReleaseTimer(t)
call ReleaseTimer(tEnd)
set t = null
set tEnd = null
set target = null
set source = null
call .deallocate()
endmethod
static method end takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call .destroy()
endmethod
static method runAction takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call f.execute(target, source)
call .destroy()
endmethod
static method setup takes unit target, unit source, UnitAction f, real tickRate, real duration returns nothing
local thistype this = thistype.allocate()
set .t = NewTimerEx(this)
set .tEnd = NewTimerEx(this)
set .target = target
set .source = source
set .f = f
call TimerStart(t, tickRate, false, function thistype.runAction)
call TimerStart(t, duration, false, function thistype.end)
endmethod
endstruct
struct DelayedUnitAction
unit target
unit source
UnitAction f
static method runAction takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call f.execute(target, source)
call ReleaseTimer(t)
set t = null
set target = null
set source = null
call .deallocate()
endmethod
static method setup takes unit target, unit source, UnitAction f, real delay returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
set .target = target
set .source = source
set .f = f
call TimerStart(t, delay, false, function thistype.runAction)
set t = null
endmethod
endstruct
struct TrackingStruct
real x
real y
real speed
unit target
CoordinateAction f
timer tLoop
timer tEnd
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real ux = GetUnitX(target)
local real uy = GetUnitY(target)
local real angleThisToTarget = Atan2(uy - y, ux - x)
local real adjustedDistance = speed
local real dx = x - ux
local real dy = y - uy
local real distanceToTarget = SquareRoot(dx * dx + dy * dy)
if distanceToTarget < speed then
set adjustedDistance = distanceToTarget
endif
set x = x + adjustedDistance * Cos(angleThisToTarget)
set y = y + adjustedDistance * Sin(angleThisToTarget)
call f.execute(x, y, angleThisToTarget)
endmethod
method destroy takes nothing returns nothing
call ReleaseTimer(tLoop)
call ReleaseTimer(tEnd)
set tLoop = null
set tEnd = null
set target = null
call .deallocate()
endmethod
static method end takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call .destroy()
endmethod
static method setup takes real sx, real sy, unit target, CoordinateAction f, real tickRate, real speedPerTick, real duration returns nothing
local thistype this = thistype.allocate()
set .tLoop = NewTimerEx(this)
set .tEnd = NewTimerEx(this)
set .x = sx
set .y = sy
set .speed = speedPerTick
set .target = target
set .f = f
call TimerStart(tLoop, tickRate, true, function thistype.onLoop)
call TimerStart(tEnd, duration, false, function thistype.end)
endmethod
endstruct
struct DirectionalStruct
real x
real y
real speed
real angle
CoordinateAction f
timer tLoop
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set x = x + speed * Cos(angle)
set y = y + speed * Sin(angle)
call f.execute(x, y, angle)
endmethod
static method end takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call ReleaseTimer(tLoop)
call ReleaseTimer(t)
set tLoop = null
set t = null
call .deallocate()
endmethod
static method setup takes real sx, real sy, real angle, CoordinateAction f, real tickRate, real speedPerTick, real duration returns nothing
local thistype this = thistype.allocate()
local timer tEnd = NewTimerEx(this)
set .tLoop = NewTimerEx(this)
set .x = sx
set .y = sy
set .speed = speedPerTick
set .angle = angle
set .f = f
call TimerStart(tLoop, tickRate, true, function thistype.onLoop)
call TimerStart(tEnd, duration, false, function thistype.end)
set tEnd = null
endmethod
endstruct
struct DelayedTrigger
trigger trig
static method execute takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call TriggerExecute(trig)
call ReleaseTimer(t)
set t = null
set trig = null
call .deallocate()
endmethod
static method setup takes trigger trig, real delay returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
set .trig = trig
call TimerStart(t, delay, false, function thistype.execute)
set t = null
endmethod
endstruct
//Useful to give timed temporary buffs from aura-based abilities. Note: Do not track "overlapping" timers!
//use `call TemporaryAura.setup(udg_tempUnit, 'A000', 'B000', 2.25)`
struct TemporaryAura
integer buffid
integer abilityid
unit u
static method cleanup takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call ReleaseTimer(t)
set t = null
call UnitRemoveAbility(u, abilityid)
call UnitRemoveAbility(u, buffid)
set u = null
call .deallocate()
endmethod
static method setup takes unit u, integer abilityid, integer buffid, real duration returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
set .abilityid = abilityid
set .buffid = buffid
set .u = u
call UnitAddAbility(u, abilityid)
call TimerStart(t, duration, false, function thistype.cleanup)
set t = null
endmethod
endstruct
struct CosmeticMissile extends Missiles
private trigger onHitTrig
method onFinish takes nothing returns boolean
set udg_x = .x
set udg_y = .y
call TriggerExecute(onHitTrig)
set onHitTrig = null
return true
endmethod
public static method setup takes unit source, unit target, trigger onHitTrigger, string model, real speed returns CosmeticMissile
local real ux = GetUnitX(source)
local real uy = GetUnitY(source)
local real uz = GetZHeight(ux, uy) + 70.0
local thistype missile = thistype.create(ux, uy, uz, 0.0, 0.0, 0.0)
set missile.onHitTrig = onHitTrigger
set missile.source = source
set missile.target = target
set missile.model = model
set missile.speed = speed
//set missile.collision = 32.0
//set missile.damage = .damage
set missile.owner = GetOwningPlayer(source)
call missile.launch()
return missile
endmethod
endstruct
//source, x, y, damage, radius, damage-type
function damageGroup takes unit source, group groupToDamage, real damage, damagetype dmgType returns nothing
local group g = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
call BlzGroupAddGroupFast(groupToDamage, g)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call Damage.applySpell(source, u, damage, dmgType)
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup (g)
set owner = null
set g = null
endfunction
//source, x, y, damage, radius, damage-type
function damageAoENoFF takes unit source, real x, real y, real damage, real radius, damagetype dmgType returns nothing
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius + 64, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and not IsUnitAlly(u, owner) and IsUnitInRangeXY(u, x, y, radius) then
//call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, dmgType, null)
call Damage.applySpell(source, u, damage, dmgType)
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endfunction
//source, x, y, damage, radius, damage-type
function damageAoENoFFIgnoreUnit takes unit source, real x, real y, real damage, real radius, damagetype dmgType, unit unitToIgnore returns nothing
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius + 64, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and not IsUnitAlly(u, owner) and IsUnitInRangeXY(u, x, y, radius) and u != unitToIgnore then
//call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, dmgType, null)
call Damage.applySpell(source, u, damage, dmgType)
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endfunction
//source, x, y, damage, radius, damage-type
function damageAoENoFFNoBuildingDamage takes unit source, real x, real y, real damage, real radius, damagetype dmgType returns nothing
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius + 64, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and not IsUnitAlly(u, owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitInRangeXY(u, x, y, radius) then
//call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, dmgType, null)
call Damage.applySpell(source, u, damage, dmgType)
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endfunction
//source, x, y, damage, radius, damage-type
function damageAoENoFriendlyBuildingDamage takes unit source, real x, real y, real damage, real radius, damagetype dmgType returns nothing
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius + 64, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and not (IsUnitAlly(u, owner) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) and IsUnitInRangeXY(u, x, y, radius) then
//call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, dmgType, null)
call Damage.applySpell(source, u, damage, dmgType)
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endfunction
function refreshAbility takes unit u, integer abilityId returns nothing
call IncUnitAbilityLevel(u, abilityId)
call DecUnitAbilityLevel(u, abilityId)
endfunction
function AddAbilityIfMissingAsPermanent takes unit u, integer abilityId returns nothing
if ( BlzGetUnitAbility(u, abilityId) == null ) then
call UnitAddAbility( u, abilityId )
call UnitMakeAbilityPermanent(u, true, abilityId)
endif
endfunction
function CountItemsOfType takes unit u, integer itemId returns integer
local integer itemCount = 0
local integer i = 0
loop
if GetItemTypeId(UnitItemInSlot(u, i)) == itemId then
set itemCount = itemCount + 1
endif
set i = i + 1
exitwhen i > bj_MAX_INVENTORY
endloop
return itemCount
endfunction
function DummyCastTargeted takes unit target, integer abilityId, string orderId returns nothing
local unit dummy = TZGetDummyTimed(GetUnitX(target), GetUnitY(target), abilityId, 0.01, GetOwningPlayer(target))
call IssueTargetOrder(dummy, orderId, target )
set dummy = null
endfunction
function DummyCastNoTarget takes real x, real y, unit owningUnit, integer abilityId, string orderId returns nothing
local unit dummy = TZGetDummyTimed(x, y, abilityId, 0.01, GetOwningPlayer(owningUnit))
call IssueImmediateOrder(dummy, orderId)
set dummy = null
endfunction
function StunTarget takes unit source, unit target, real durationUnits, real durationHeroes returns nothing
local unit dummy = TZGetDummyTimed(GetUnitX(target), GetUnitY(target), STORM_BOLT_STUN_DUMMY, 0.00, PASSIVE_PLAYER)
call BlzSetAbilityRealLevelField( BlzGetUnitAbility(dummy, STORM_BOLT_STUN_DUMMY), ABILITY_RLF_DURATION_NORMAL, 0, durationUnits )
call BlzSetAbilityRealLevelField( BlzGetUnitAbility(dummy, STORM_BOLT_STUN_DUMMY), ABILITY_RLF_DURATION_HERO, 0, durationHeroes )
call IssueTargetOrder(dummy, "thunderbolt", target )
set dummy = null
endfunction
function SetupSlowDummy takes unit source, integer abilityId, real moveSlowPercent, real attackSlowPercent, real dmgReductionPercent, real durationUnits, real durationHeroes returns nothing
local real x = GetUnitX(source)
local real y = GetUnitY(source)
set t_unit1 = TZGetDummyTimed(x, y, abilityId, 0.00, GetOwningPlayer(source))
call BlzSetAbilityRealLevelField( BlzGetUnitAbility(t_unit1, abilityId), ABILITY_RLF_DURATION_NORMAL, 0, durationUnits )
call BlzSetAbilityRealLevelField( BlzGetUnitAbility(t_unit1, abilityId), ABILITY_RLF_DURATION_HERO, 0, durationHeroes )
call BlzSetAbilityRealLevelField( BlzGetUnitAbility(t_unit1, abilityId), ABILITY_RLF_MOVEMENT_SPEED_REDUCTION_PERCENT_CRI1, 0, moveSlowPercent )
call BlzSetAbilityRealLevelField( BlzGetUnitAbility(t_unit1, abilityId), ABILITY_RLF_ATTACK_SPEED_REDUCTION_PERCENT_CRI2, 0, attackSlowPercent )
call BlzSetAbilityRealLevelField( BlzGetUnitAbility(t_unit1, abilityId), ABILITY_RLF_DAMAGE_REDUCTION_CRI3, 0, dmgReductionPercent )
endfunction
function SlowTarget takes unit source, unit target, real moveSlowPercent, real attackSlowPercent, real dmgReductionPercent, real durationUnits, real durationHeroes returns nothing
call SetupSlowDummy(source, CRIPPLE_DUMMY, moveSlowPercent, attackSlowPercent, dmgReductionPercent, durationUnits, durationHeroes)
call IssueTargetOrder(t_unit1, "cripple", target )
endfunction
function IsUnitStunned takes unit u returns boolean
return GetUnitAbilityLevel(u, STUNNED_BUFF) > 0 or GetUnitAbilityLevel(u, BASHER_STUNNED_BUFF) > 0
endfunction
//------------ Note that you must clear flag for it not to "leak" for that unit!
function IsFlagSet takes unit target, string flag returns boolean
return HaveSavedBoolean(udg_item_hash, GetHandleId(target), StringHash(flag))
endfunction
function ClearFlag takes unit target, string flag returns nothing
call RemoveSavedBoolean(udg_item_hash, GetHandleId(target), StringHash(flag))
endfunction
function SetFlag takes unit target, string flag returns nothing
call SaveBoolean(udg_item_hash, GetHandleId(target), StringHash(flag), true)
endfunction
//------------
library DTCUtils initializer init requires ExtendableBonusSystem, Table
globals
Table heroTable
endglobals
private function init takes nothing returns nothing
set heroTable = Table.create()
endfunction
function GiveGoldToPlayers takes integer amount returns nothing
local player p
local integer i = 0
set udg_total_gold_earned = udg_total_gold_earned + amount
loop
set p = Player(i)
if IsPlayerInForce(p, udg_current_human_players) then
call SetPlayerState(p, PLAYER_STATE_GOLD_GATHERED, GetPlayerState(p, PLAYER_STATE_GOLD_GATHERED) + amount)
call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + amount)
endif
set i = i + 1
exitwhen i > LAST_HUMAN_PLAYER_INDEX //LAST_HUMAN_PLAYER_INDEX is inclusive
endloop
set p = null
endfunction
function bitFromBitIndex takes integer bitIndex returns integer
local integer i = 0
local integer bit = 1
loop
if i == bitIndex then
return bit
endif
set i = i + 1
set bit = bit * 2
exitwhen i > 32
endloop
return -1 //This should never happen!
endfunction
function MissingHp takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_LIFE) - GetUnitState(u, UNIT_STATE_LIFE)
endfunction
function MissingMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_MANA) - GetUnitState(u, UNIT_STATE_MANA)
endfunction
function MaxHp takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
function MaxMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_MANA)
endfunction
function RestoreManaWithText takes unit u, real amount, real sizeAdjustment returns nothing
local real current = GetUnitState(u, UNIT_STATE_MANA)
local real max = GetUnitState(u, UNIT_STATE_MAX_MANA)
local real angle = GetRandomReal(0., 2. * bj_PI)
local real adjustmentFactor = amount / (amount + 150.)
local real textSize = 6.0 + 6.0 * adjustmentFactor * sizeAdjustment
local real textTime = (1.0 + 0.5 * adjustmentFactor) * sizeAdjustment
local real x = GetUnitX(u) + 40. * Cos(angle)
local real y = GetUnitY(u) + 40. * Sin(angle)
local texttag text = CreateTextTag()
if current + amount > max then
set amount = max - current
endif
call SetUnitState(u, UNIT_STATE_MANA, RMaxBJ(0,current + amount))
if amount > 0.2 then
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, textTime)
call SetTextTagFadepoint(text, textTime * 0.8)
call SetTextTagVelocityBJ(text, GetRandomReal(128., 160.), GetRandomReal(60., 120.))
call SetTextTagColor(text, 128, 100, 236, R2I(150. + adjustmentFactor * 80.))
if amount < 100 then
call SetTextTagText(text, R2SW(amount, 0, 1), TextTagSize2Height(textSize))
else
call SetTextTagText(text, R2SW(amount, 0, 0), TextTagSize2Height(textSize))
endif
call SetTextTagPos(text, x, y, GetRandomReal(96., 128.))
else
call DestroyTextTag(text)
endif
set text = null
endfunction
function RestoreHpWithText takes unit u, real amount, real sizeAdjustment returns nothing
local real current = GetUnitState(u, UNIT_STATE_LIFE)
local real max = GetUnitState(u, UNIT_STATE_MAX_LIFE)
local real angle = GetRandomReal(0., 2. * bj_PI)
local real adjustmentFactor = amount / (amount + 150.)
local real textSize = 6.0 + 6.0 * adjustmentFactor * sizeAdjustment
local real textTime = (1.0 + 0.5 * adjustmentFactor) * sizeAdjustment
local real x = GetUnitX(u) + 40. * Cos(angle)
local real y = GetUnitY(u) + 40. * Sin(angle)
local texttag text = CreateTextTag()
if current + amount > max then
set amount = max - current
endif
call SetUnitState(u, UNIT_STATE_LIFE, RMaxBJ(0., current + amount))
if amount > 0.2 then
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, textTime)
call SetTextTagFadepoint(text, textTime * 0.8)
call SetTextTagVelocityBJ(text, GetRandomReal(128., 160.), GetRandomReal(60., 120.))
call SetTextTagColor(text, 150, 240, 100, R2I(150. + adjustmentFactor * 80.))
if amount < 100. then
call SetTextTagText(text, R2SW(amount, 0, 1), TextTagSize2Height(textSize))
else
call SetTextTagText(text, R2SW(amount, 0, 0), TextTagSize2Height(textSize))
endif
call SetTextTagPos(text, x, y, GetRandomReal(96., 128.))
else
call DestroyTextTag(text)
endif
set text = null
endfunction
function ShowCritText takes unit u, real amount, real bangFactor returns nothing
local real angle = GetRandomReal(0., 2. * bj_PI)
local real x = GetUnitX(u) + 30. * Cos(angle)
local real y = GetUnitY(u) + 30. * Sin(angle)
local real textSize = 7.0 + 4.0 * bangFactor
local real textTime = (0.6 + 0.4 * bangFactor)
local texttag text = CreateTextTag()
if amount > 0.2 then
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, textTime)
call SetTextTagFadepoint(text, textTime * 0.8)
call SetTextTagVelocityBJ(text, 1.0 / bangFactor * GetRandomReal(128., 160.), 1.0 / bangFactor * GetRandomReal(60., 120.))
call SetTextTagColor(text, 255, 20, 20, 180)
call SetTextTagText(text, I2S(R2I(amount+.01)), TextTagSize2Height(textSize))
call SetTextTagPos(text, x, y, GetRandomReal(96., 128.))
else
call DestroyTextTag(text)
endif
set text = null
endfunction
function GetHeroBonusInt takes unit u returns integer
return GetHeroInt(u, true) - GetHeroInt(u, false)
endfunction
function GetHeroBonusStr takes unit u returns integer
return GetHeroStr(u, true) - GetHeroStr(u, false)
endfunction
function GetHeroBonusAgi takes unit u returns integer
return GetHeroAgi(u, true) - GetHeroAgi(u, false)
endfunction
function GetSpellPowerBaseAmount takes unit u returns integer
return udg_spell_power[GetUnitUserData(u)]
endfunction
function SetSpellPowerBaseAmount takes unit u, integer amount returns nothing
set udg_spell_power[GetUnitUserData(u)] = amount
endfunction
function GetSpellPowerMultiplier takes unit u returns real
return udg_spell_power_multiplier[GetUnitUserData(u)]
endfunction
function SetSpellPowerMultiplier takes unit u, real amount returns nothing
set udg_spell_power_multiplier[GetUnitUserData(u)] = amount
endfunction
function GetSpellPower takes unit u returns integer
return R2I((GetSpellPowerBaseAmount(u) + GetHeroInt(u, true) * SPELL_POWER_PER_INTELLIGENCE) * GetSpellPowerMultiplier(u))
endfunction
function GetCriticalHitChance takes unit u returns real
return udg_crit_chance[GetUnitUserData(u)]
endfunction
function GetCriticalHitChanceInclAgi takes unit u returns real
return udg_crit_chance[GetUnitUserData(u)] + CRIT_CHANCE_PER_AGILITY * GetHeroAgi(u, true)
endfunction
function SetCriticalHitChance takes unit u, real amount returns nothing
set udg_crit_chance[GetUnitUserData(u)] = amount
endfunction
function GetCriticalHitDamage takes unit u returns real
return udg_crit_damage[GetUnitUserData(u)]
endfunction
function GetCriticalHitDamageInclAgi takes unit u returns real
return udg_crit_damage[GetUnitUserData(u)] + CRIT_DAMAGE_PER_AGILITY * GetHeroAgi(u, true)
endfunction
function SetCriticalHitDamage takes unit u, real amount returns nothing
set udg_crit_damage[GetUnitUserData(u)] = amount
endfunction
function GetTotalDamage takes unit u returns real
return BlzGetUnitBaseDamage(u, 0) + BlzGetUnitDiceNumber(u, 0) + GetUnitBonus(u, BONUS_DAMAGE)
endfunction
function GetHealthRegeneration takes unit u returns real
local real auraRegen = 0.
if GetUnitAbilityLevel(u, UNHOLY_AURA_BUFF) > 0 then
set auraRegen = 1.0
endif
return GetHeroStr(u, true) * 0.03 + GetUnitBonus(u, BONUS_HEALTH_REGEN) + BlzGetUnitRealField(u, UNIT_RF_HIT_POINTS_REGENERATION_RATE) + auraRegen
endfunction
function GetManaRegeneration takes unit u returns real
local real auraRegen = 0.
if GetUnitAbilityLevel(u, BRILLIANCE_AURA_BUFF) > 0 then
set auraRegen = 1.0
endif
return GetHeroInt(u, true) * 0.03 + GetUnitBonus(u, BONUS_MANA_REGEN) + BlzGetUnitRealField(u, UNIT_RF_MANA_REGENERATION) + auraRegen
endfunction
function GetBonusPrimaryAttribute takes unit u returns integer
local integer stat = BlzGetUnitIntegerField(u, UNIT_IF_PRIMARY_ATTRIBUTE)
if stat == 1 then //Str
return GetHeroBonusStr(u)
elseif stat == 2 then //Int
return GetHeroBonusInt(u)
else //Agi
return GetHeroBonusAgi(u)
endif
endfunction
function GetBonusDamage takes unit u returns real
if IsUnitType(u, UNIT_TYPE_HERO) then
return GetUnitBonus(u, BONUS_DAMAGE) + 1.5 * GetBonusPrimaryAttribute(u)
else
return GetUnitBonus(u, BONUS_DAMAGE)
endif
endfunction
struct ArmorPenetrationFlat
static method Get takes unit u returns real
return udg_armor_penetration_flat[GetUnitUserData(u)]
endmethod
static method Set takes unit u, real value returns real
set udg_armor_penetration_flat[GetUnitUserData(u)] = value
return value
endmethod
endstruct
struct ArmorPenetrationPercent
static method Get takes unit u returns real
return udg_armor_penetration_percent[GetUnitUserData(u)]
endmethod
static method Set takes unit u, real value returns real
set udg_armor_penetration_percent[GetUnitUserData(u)] = value
return value
endmethod
endstruct
function GetTotalAttackSpeed takes unit u returns real
if IsUnitType(u, UNIT_TYPE_HERO) then
return 0.01 * GetHeroAgi(u, true) + GetUnitBonus(u, BONUS_ATTACK_SPEED)
else
return GetUnitBonus(u, BONUS_ATTACK_SPEED)
endif
endfunction
function GetAttackCooldown takes unit u returns real
return BlzGetUnitAttackCooldown(u, 0) / (1.0 + GetTotalAttackSpeed(u))
endfunction
function GetAttacksPerSecond takes unit u returns real
return 1.0 / GetAttackCooldown(u)
endfunction
function GetBonusAttackSpeed takes unit u returns real
if IsUnitType(u, UNIT_TYPE_HERO) then
return 0.01 * GetHeroBonusAgi(u) + GetUnitBonus(u, BONUS_ATTACK_SPEED)
else
return GetUnitBonus(u, BONUS_ATTACK_SPEED)
endif
endfunction
function GetSpellHaste takes unit u returns real
return GetUnitBonus(u, BONUS_SPELL_HASTE)
endfunction
function TauntEnemies takes unit source, real radius returns nothing
local group targetsWithinRadius = CreateGroup()
local real x = GetUnitX(source)
local real y = GetUnitY(source)
local player owner = GetOwningPlayer(source)
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius + 64, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and not IsUnitAlly(u, owner) and not IsUnitType(u, UNIT_TYPE_HERO) and not IsUnitType(u, UNIT_TYPE_RESISTANT) and IsUnitInRangeXY(u, x, y, radius) then
//call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, dmgType, null)
//call Damage.applySpell(source, u, damage, dmgType)
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endfunction
endlibrary
/*
constant integer BONUS_ARMOR_PEN_FLAT = 73
constant integer BONUS_ARMOR_PEN_PERCENT = 74
*/
library DTCNewBonusExtraUtils requires ExtendableBonusesBasicBonuses, ExtendableBonusesDtc, DTCStringUtils
globals
constant integer MISSING_HP_SCALING = 10000 //Not a bonus, just something that abilities scales with sometimes, just a large value
endglobals
function Bonus2Color takes integer bonus returns string
if bonus == BONUS_DAMAGE or bonus == BONUS_BONUS_DAMAGE then
return "|c00FF7800"
elseif bonus == BONUS_ARMOR then
return "|c008AAAC0"
elseif bonus == BONUS_AGILITY or bonus == BONUS_BONUS_AGILITY then
return "|c00EEE8AA"
elseif bonus == BONUS_STRENGTH or bonus == BONUS_BONUS_STRENGTH then
return "|c00EE8072"
elseif bonus == BONUS_INTELLIGENCE or bonus == BONUS_BONUS_INTELLIGENCE then
return "|c0072AAEE"
elseif bonus == BONUS_HEALTH then
return "|c0096f064"
elseif bonus == BONUS_MANA then
return "|c008064EC"
elseif bonus == BONUS_MOVEMENT_SPEED then
return ""
elseif bonus == BONUS_SIGHT_RANGE then
return ""
elseif bonus == BONUS_SPELL_POWER then
return "|c00DF00FF"
elseif bonus == BONUS_HEALTH_REGEN then
return "|c0096f064"
elseif bonus == BONUS_MANA_REGEN then
return "|c008064EC"
elseif bonus == BONUS_ATTACK_SPEED then
return "|c00FFC899"
elseif bonus == BONUS_CRITICAL_CHANCE then
return "|c00FF4840"
elseif bonus == BONUS_CRITICAL_DAMAGE then
return "|c00FF4840"
elseif bonus == BONUS_SPELL_POWER_MULTIPLIER then
return "|c00DF00FF"
elseif bonus == BONUS_ARMOR_PEN_FLAT or bonus == BONUS_ARMOR_PEN_PERCENT then
return "|c00DF00FF"
elseif bonus == DTCShielding.typeid then
return "|c00D4D4FF"
elseif bonus == MISSING_HP_SCALING then
return "|c0096f064"
else
call BJDebugMsg("BonusToColor: Invalid bonus. Got: " + I2S(bonus))
endif
return ""
endfunction
function Bonus2String takes integer bonus returns string
if bonus == BONUS_DAMAGE then
return "damage"
elseif bonus == BONUS_ARMOR then
return "armor"
elseif bonus == BONUS_AGILITY then
return "agility"
elseif bonus == BONUS_STRENGTH then
return "strength"
elseif bonus == BONUS_INTELLIGENCE then
return "intelligence"
elseif bonus == BONUS_HEALTH then
return "hp"
elseif bonus == BONUS_MANA then
return "mana"
elseif bonus == BONUS_MOVEMENT_SPEED then
return "movement speed"
elseif bonus == BONUS_SIGHT_RANGE then
return "sight range"
elseif bonus == BONUS_SPELL_POWER then
return "spell power"
elseif bonus == BONUS_HEALTH_REGEN then
return "hp/s"
elseif bonus == BONUS_MANA_REGEN then
return "mana/s"
elseif bonus == BONUS_ATTACK_SPEED then
return "attack speed"
elseif bonus == BONUS_CRITICAL_CHANCE then
return "critical hit chance"
elseif bonus == BONUS_CRITICAL_DAMAGE then
return "critical hit damage"
elseif bonus == BONUS_BONUS_AGILITY then
return "bonus agility"
elseif bonus == BONUS_BONUS_STRENGTH then
return "bonus strength"
elseif bonus == BONUS_BONUS_INTELLIGENCE then
return "bonus intelligence"
elseif bonus == BONUS_BONUS_DAMAGE then
return "bonus damage"
elseif bonus == BONUS_SPELL_POWER_MULTIPLIER then
return "spell power multiplier"
elseif bonus == BONUS_ARMOR_PEN_FLAT then
return "flat armor penetration"
elseif bonus == BONUS_ARMOR_PEN_PERCENT then
return "percent armor penetration"
elseif bonus == DTCShielding.typeid then
return "shielding"
elseif bonus == MISSING_HP_SCALING then
return "missing hp"
else
call BJDebugMsg("BonusToColor: Invalid bonus. Got: " + I2S(bonus))
endif
return ""
endfunction
//Returns BONUS_STRENGTH, BONUS_INTELLIGENCE or BONUS_AGILITY
function PrimaryAttributeToBonus takes unit u returns integer
local integer stat = BlzGetUnitIntegerField(u, UNIT_IF_PRIMARY_ATTRIBUTE)
if stat == 1 then //str
return BONUS_STRENGTH
elseif stat == 2 then //int
return BONUS_INTELLIGENCE
else //agi
return BONUS_AGILITY
endif
endfunction
function ScalingText takes real value, real percent, integer bonusType returns string
return Bonus2Color(bonusType) + R2I2S(value * percent) + " (" + R2Sx100(percent) + "%% of " + Bonus2String(bonusType) + ")|r"
endfunction
function ScalingTextMissing takes real value, real percent, integer bonusType returns string
return Bonus2Color(bonusType) + "0-" + R2I2S(value * percent) + " (" + R2Sx100(percent) + "%% of " + Bonus2String(bonusType) + ")|r"
endfunction
function ScalingTextValueless takes real percent, integer bonusType returns string
return Bonus2Color(bonusType) + R2Sx100(percent) + "%% of " + Bonus2String(bonusType) + "|r"
endfunction
function ScalingTextPercentPerX takes real value, real perX, integer bonusType, string whatPerX returns string
return Bonus2Color(bonusType) + R2I2S(value * 1.0 / perX) + " (" + whatPerX + " per " + R2I2S(perX) + " " + Bonus2String(bonusType) + ")|r"
endfunction
function ScalingTextPercentPerXValueless takes real perX, integer bonusType, string whatPerX returns string
return Bonus2Color(bonusType) + whatPerX + " per " + R2I2S(perX) + " " + Bonus2String(bonusType) + "|r"
endfunction
endlibrary
function GetTotalPrimaryAttribute takes unit u returns integer
local integer stat = BlzGetUnitIntegerField(u, UNIT_IF_PRIMARY_ATTRIBUTE)
if stat == 1 then //Str
return GetHeroStr(u, true)
elseif stat == 2 then //Int
return GetHeroInt(u, true)
else //Agi
return GetHeroAgi(u, true)
endif
endfunction
function GetAbilityDurationNormal takes ability a, integer level returns real
return BlzGetAbilityRealLevelField(a, ABILITY_RLF_DURATION_NORMAL, level)
endfunction
function GetDurationNormal takes unit u, integer abilityId returns real
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), ABILITY_RLF_DURATION_NORMAL, GetUnitAbilityLevel(u, abilityId))
endfunction
function GetDurationHero takes unit u, integer abilityId returns real
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), ABILITY_RLF_DURATION_HERO, GetUnitAbilityLevel(u, abilityId))
endfunction
function SetDurationPercent takes unit u, integer abilityId, real percent returns nothing
local integer level = GetUnitAbilityLevel(u, abilityId)
local ability abi = BlzGetUnitAbility(u, abilityId)
call BlzSetAbilityRealLevelField(abi, ABILITY_RLF_DURATION_NORMAL, level, GetDurationNormal(u, abilityId) * percent)
call BlzSetAbilityRealLevelField(abi, ABILITY_RLF_DURATION_HERO, level, GetDurationHero(u, abilityId) * percent)
set abi = null
endfunction
function NPCGetTargetInRange takes unit caster, real range returns nothing
local group g = CreateGroup()
local boolean hasFoundHero = false
local unit selectedUnit = null
local player owner = GetOwningPlayer(caster)
local unit u
call GroupEnumUnitsInRange(g, GetUnitX(caster), GetUnitY(caster), range, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if IsPlayerEnemy(owner, GetOwningPlayer(u)) and UnitAlive(u) then
if not hasFoundHero and IsUnitType(u, UNIT_TYPE_HERO) then
set selectedUnit = u
set hasFoundHero = true
elseif not hasFoundHero then
set selectedUnit = u
endif
endif
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
set g = null
set owner = null
set udg_temp_unit = selectedUnit
set selectedUnit = null
endfunction
function NPCGetHeroFurthestAway takes unit caster returns nothing
local group g = CreateGroup()
local unit selectedUnit = null
local unit u = null
local boolean hasFoundHero = false
local real distance = 0.0
local real d = 0.0
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
call BlzGroupAddGroupFast(udg_heroes_unit_group, g)
loop
set u = FirstOfGroup(g)
exitwhen u == null
set d = distanceBetweenXYXY(GetUnitX(u), GetUnitY(u), casterX, casterY)
if UnitAlive(u) and d > distance then
set distance = d
set selectedUnit = u
endif
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
set udg_temp_unit = selectedUnit
set selectedUnit = null
set g = null
endfunction
function GetVisibleEnemiesClosestToCoords takes unit source, real tx, real ty, real maxRange, real heroPreferenceRange returns unit
local group g = CreateGroup()
local unit closestUnit = null
local unit closestHero = null
local unit u = null
local real unitDistance = 99999.9
local real heroDistance = 99999.9
local real d = 0.0
call GroupEnumUnitsInRange(g, tx, ty, maxRange, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if UnitAlive(u) and IsPlayerEnemy(GetOwningPlayer(source), GetOwningPlayer(u)) and IsUnitVisible(u, GetOwningPlayer(source)) then
set d = distanceBetweenXYXY(GetUnitX(u), GetUnitY(u), tx, ty)
if IsUnitType(u, UNIT_TYPE_HERO) then
if d < heroDistance then
set heroDistance = d
set closestHero = u
endif
else
if d < unitDistance then
set unitDistance = d
set closestUnit = u
endif
endif
endif
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
if unitDistance + heroPreferenceRange < heroDistance then
set udg_temp_unit = closestUnit //If there is a unit 150 units closer to "targetRadius" than any hero, use unit
set udg_temp_real6 = unitDistance
else
set udg_temp_unit = closestHero //Otherwise use hero
set udg_temp_real6 = heroDistance
endif
set closestUnit = null
set closestHero = null
set g = null
return udg_temp_unit
endfunction
//Gets Unit closest to "targetRadius".
//Sets the distance in udg_temp_real6, and unit in udg_temp_unit
//If 2 units are close, one unit and one hero, the heroPreferenceRange determins how much closer
//the "unit" can be than the hero and the hero is still chosen
function GetTargetClosestToDistancePerferHeroes takes unit caster, real searchRadius, real targetRadius, real heroPreferenceRange returns nothing
local group g = CreateGroup()
local unit closestUnit = null
local unit closestHero = null
local unit u = null
local real unitDistance = 99999.9
local real heroDistance = 99999.9
local real d = 0.0
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
call GroupEnumUnitsInRange(g, GetUnitX(caster), GetUnitY(caster), searchRadius, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if UnitAlive(u) and IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(u)) then
set d = RAbsBJ(distanceBetweenXYXY(GetUnitX(u), GetUnitY(u), casterX, casterY) - targetRadius)
if IsUnitType(u, UNIT_TYPE_HERO) then
if d < heroDistance then
set heroDistance = d
set closestHero = u
endif
else
if d < unitDistance then
set unitDistance = d
set closestUnit = u
endif
endif
endif
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
if unitDistance + heroPreferenceRange < heroDistance then
set udg_temp_unit = closestUnit //If there is a unit "heroPreferenceRange" range-units closer to "targetRadius" than any hero, use unit
set udg_temp_real6 = unitDistance
else
set udg_temp_unit = closestHero //Otherwise use hero
set udg_temp_real6 = heroDistance
endif
set closestUnit = null
set closestHero = null
set g = null
endfunction
function NPCAttackMoveToCenterLater takes unit u, unit unused returns nothing
call IssuePointOrder( u, "attack", 0.0, 0.0 )
endfunction
function GetUnitsInCircleSegment2 takes real x, real y, real centerRadians, real radius, real segRadians, real requiredCollisionPercent returns group
local group g = CreateGroup()
local group result = CreateGroup()
local unit u
local real tx
local real ty
// Mid
local real diffX = -radius * Cos(centerRadians)
local real diffY = -radius * Sin(centerRadians)
local real diffLength = SquareRoot(diffX * diffX + diffY * diffY)
local real nCenterX = -diffY / diffLength // Swap of X, Y is intentional!
local real nCenterY = diffX / diffLength // Swap of X, Y is intentional!
local real nLeftX
local real nLeftY
local real nRightX
local real nRightY
local real unitCollisionRadius
local real radiusToRemove
local real margin
// Left
set diffX = -radius * Cos(centerRadians + segRadians)
set diffY = -radius * Sin(centerRadians + segRadians)
set diffLength = SquareRoot(diffX * diffX + diffY * diffY)
set nLeftX = -diffY / diffLength // Swap of X, Y is intentional!
set nLeftY = diffX / diffLength // Swap of X, Y is intentional!
// Right
set diffX = radius * Cos(centerRadians - segRadians)
set diffY = radius * Sin(centerRadians - segRadians)
set diffLength = SquareRoot(diffX * diffX + diffY * diffY)
set nRightX = -diffY / diffLength // Swap of X, Y is intentional!
set nRightY = diffX / diffLength // Swap of X, Y is intentional!
call GroupEnumUnitsInRange(g, x, y, radius + 100.0, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set unitCollisionRadius = BlzGetUnitCollisionSize(u)
set radiusToRemove = unitCollisionRadius * (1.0 - requiredCollisionPercent)
set margin = unitCollisionRadius - radiusToRemove
if UnitAlive(u) and IsUnitInRangeXY(u, x, y, radius - unitCollisionRadius * requiredCollisionPercent) then
if IsInFrontOfPlane(nCenterX, nCenterY, x, y, tx, ty, 0.0) then
if IsInFrontOfPlane(nRightX, nRightY, x, y, tx, ty, margin) then
call GroupAddUnit(result, u)
endif
elseif IsInFrontOfPlane(nLeftX, nLeftY, x, y, tx, ty, margin) then
//IsInFrontOfPlane(nLeftX, nLeftY, x, y, tx, ty, margin)
call GroupAddUnit(result, u)
endif
endif
call GroupRemoveUnit(g, u)
endloop
call DestroyGroup(g)
set g = null
return result
endfunction
function GetUnitsInCircleSegment takes real x, real y, real centerDirection, real radius, real segmentRadians, real collisionRadiusPercent returns group
local group g = CreateGroup()
local group result = CreateGroup()
local unit u
local real tx = x + radius * Cos(centerDirection + segmentRadians)
local real ty = y + radius * Sin(centerDirection + segmentRadians)
local real diffX = x - tx
local real diffY = y - ty
local real diffLength = SquareRoot(diffX * diffX + diffY * diffY)
local real normLeftX = -diffY / diffLength //Swap of X, Y is intentional!
local real normLeftY = diffX / diffLength //Swap of X, Y is intentional!
local real normRightX
local real normRightY
local real unitCollitionRadius
local real radiusToRemove
set tx = x + radius * Cos(centerDirection - segmentRadians)
set ty = y + radius * Sin(centerDirection - segmentRadians)
set diffX = tx - x
set diffY = ty - y
set diffLength = SquareRoot(diffX * diffX + diffY * diffY)
set normRightX = -diffY / diffLength //Swap of X, Y is intentional!
set normRightY = diffX / diffLength //Swap of X, Y is intentional!
//set bx1 = (x + tx) / 2.0 + normRightX * 40
//set by1 = (y + ty) / 2.0 + normRightY * 40
call GroupEnumUnitsInRange(g, x, y, radius + 100.0, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set unitCollitionRadius = BlzGetUnitCollisionSize(u)
set radiusToRemove = unitCollitionRadius * (1.0 - collisionRadiusPercent)
//set angleFromStartToUnit = RAbsBJ(Atan2(GetUnitY(u) - y, GetUnitX(u) - x))
//call SetUnitVertexColor(u, 255, 255, 255, 255)
//set r = 255
//set gr = 255
//set b = 255
if UnitAlive(u) and IsUnitInRangeXY(u, x, y, radius - unitCollitionRadius * collisionRadiusPercent) then
//set r = 64
//set gr = 64
//set b = 64
//call BJDebugMsg("Is in range: " + GetUnitName(u))
//if IsInFrontOfPlane(normLeftX, normLeftY, x, y, tx, ty, BlzGetUnitCollisionSize(u)) then
// call BJDebugMsg("In front of left: " + GetUnitName(u))
// set b = 255
//endif
//if IsInFrontOfPlane(normRightX, normRightY, x, y, tx, ty, BlzGetUnitCollisionSize(u)) then
// call BJDebugMsg("In front of Right: " + GetUnitName(u))
// set r = 255
//endif
if IsInFrontOfPlane(normLeftX, normLeftY, x, y, tx, ty, unitCollitionRadius - radiusToRemove) and IsInFrontOfPlane(normRightX, normRightY, x, y, tx, ty, unitCollitionRadius - radiusToRemove) then
//call BJDebugMsg("HIT: " + GetUnitName(u))
call GroupAddUnit(result, u)
endif
//call BJDebugMsg(" ")
endif
//call SetUnitVertexColor(u, r, gr, b, 255)
call GroupRemoveUnit(g, u)
endloop
call DestroyGroup(g)
set g = null
return result
endfunction
function GetUnitsInCircleSegmentInFrontOfUnit takes unit caster, real radius, real segmentRadians, real collisionRadiusPercent returns group
return GetUnitsInCircleSegment2(GetUnitX(caster), GetUnitY(caster), bj_DEGTORAD * GetUnitFacing(caster), radius, segmentRadians, collisionRadiusPercent)
endfunction
library DTCHeroAbilityEvents initializer dtcHeroAbilityEventsInit requires Table, DTCUtils, ShildingSystem
globals
constant integer AE_BONUS_COUNT_OFFSET = 1000
//Reals
constant integer AE_AMOUNT_BASE = 1
constant integer AE_AMOUNT_DAMAGE = 2
constant integer AE_AMOUNT_SPELL_POWER = 3
constant integer AE_AMOUNT_MAX_HP = 4
constant integer AE_AMOUNT_MAX_MANA = 5
constant integer BONUS_TIME = 10
//integers
private constant integer BONUS_ID = 1
//Darth Vader
constant integer AE_DV_FORCE_PUSH_INDEX = 1
constant integer AE_DV_FORCE_LIGHTNING_INDEX = 2
constant integer AE_DV_FORCE_SLOW_TOGGLE_INDEX = 3
constant integer AE_DV_FORCE_CHOKE_STARTED_INDEX = 4
//Samus
constant integer AE_SA_CHARGE_BEAM_HIT = 1
constant integer AE_SA_MORPH = 2
constant integer AE_SA_MISSILE = 3
constant integer AE_SA_OVERCHARGE = 4
constant integer AE_SA_BOMB = 5
constant integer AE_SA_BOOST = 6
constant integer AE_SA_CHARGE_BEAM_CHARGE_START = 7
constant integer AE_SA_CHARGE_BEAM_FULL_CHARGE = 8
constant integer AE_SA_MISSILE_EMPOWER = 9
//Lothar
constant integer AE_AL_SLASH_HIT = 1
constant integer AE_AL_CHARGE_START = 2
constant integer AE_AL_STANCE_CHANGED = 3
constant integer AE_AL_CAVALRY_SUPPORT_CAST = 4
/*
hero
ability
bonusInstance
real[0]: baseBonusAmount
*/
Table heroAbility
private unit heroCaster
private integer heroIndex
private integer abilityIndex
endglobals
function InitHero takes unit u returns nothing
local integer i = GetUnitUserData(u)
local unit showcaseUnit = udg_hero_select_units[udg_hero_select_index]
local integer playerNum = GetPlayerId(GetOwningPlayer(u))
call SetUnitCooldownReductionFlat(u, 0.0)
call heroAbility.link(i)
call SaveUnitHandle(udg_heroes_hash, playerNum, 0, u)
call GroupAddUnit(udg_heroes_unit_group, u)
set udg_is_hero_taken[udg_hero_select_index] = true
set udg_player_is_ready[playerNum + 1] = true
call ShowUnit(showcaseUnit, false)
set showcaseUnit = null
endfunction
function DestroyHero takes unit u returns nothing
local integer i = GetUnitUserData(u)
call heroAbility.remove(i)
endfunction
private function EvaluateSingleTimedBonus takes Table subTable returns nothing
local real amtDmg = subTable.real[AE_AMOUNT_DAMAGE] * GetTotalDamage(heroCaster)
local real amtSp = subTable.real[AE_AMOUNT_SPELL_POWER] * GetSpellPower(heroCaster)
local real amtHp = subTable.real[AE_AMOUNT_MAX_HP] * MaxHp(heroCaster)
local real amtMana = subTable.real[AE_AMOUNT_MAX_MANA] * MaxMana(heroCaster)
local real amount = subTable.real[AE_AMOUNT_BASE] + amtDmg + amtSp + amtHp + amtMana
local integer bonusId = subTable.integer[BONUS_ID]
//call BJDebugMsg("bonusId=" + I2S(bonusId) + ", totalAmount=" + R2S(amount) + ", SUBTABLE=" + I2S(subTable))
if bonusId == DTCShielding.typeid then
call AddTimedShield(heroCaster, amount, subTable.real[BONUS_TIME])
else
call AddUnitBonusTimed(heroCaster, bonusId, amount, subTable.real[BONUS_TIME])
endif
endfunction
private function EvaluateTimedBonuses takes nothing returns nothing
local integer i = 1
local integer bonusesRegisteredOnAbility = heroAbility[heroIndex].integer[abilityIndex + AE_BONUS_COUNT_OFFSET]
local Table abilityTable
if bonusesRegisteredOnAbility != 0 then
set abilityTable = heroAbility[heroIndex][abilityIndex]
loop
exitwhen i > bonusesRegisteredOnAbility
//call BJDebugMsg("AbilityEvent "+ I2S(abilityIndex) +" evalIndex=" + I2S(i) + " out of " + I2S(bonusesRegisteredOnAbility))
call EvaluateSingleTimedBonus(abilityTable[i])
set i = i + 1
endloop
endif
endfunction
//abilityIndex must be non-0!!
function RegisterTimedBonus takes unit u, integer abilityIndex, integer bonusId, real time returns Table
local integer uix = GetUnitUserData(u)
local integer bonuses = heroAbility[uix].integer[abilityIndex + AE_BONUS_COUNT_OFFSET]
local Table subtable = heroAbility.link(uix).link(abilityIndex).link(bonuses + 1)
//call BJDebugMsg("Registering AbilityEvent, bonuses=" + I2S(bonuses))
if bonuses == 0 then
//call BJDebugMsg("AbilityEvent Created trigger for " + I2S(abilityIndex))
//set heroAbility[uix].trigger[abilityIndex] = CreateTrigger()
//set heroAbility[uix][abilityIndex] = Table.create()
//call TriggerAddCondition(heroAbility[uix].trigger[abilityIndex], Filter(function EvaluateTimedBonuses))
call heroAbility.link(uix).trigger.save(abilityIndex, CreateTrigger())
//call heroAbility.link(uix).link(abilityIndex)
call TriggerAddCondition(heroAbility[uix].trigger[abilityIndex], Filter(function EvaluateTimedBonuses))
//set subtable = heroAbility.link(uix).link(abilityIndex).link(bonuses + 1) //next index for this ability
endif
//set subtable = Table(heroAbility[uix][abilityIndex])[bonuses + 1] //next index for this ability
//set subtable = heroAbility.link(uix).link(abilityIndex).link(bonuses + 1) //next index for this ability
call heroAbility[uix].integer.save(abilityIndex + AE_BONUS_COUNT_OFFSET, bonuses + 1)
set subtable.integer[BONUS_ID] = bonusId
set subtable.real[BONUS_TIME] = time
//call BJDebugMsg("AbilityEvent Registered ai: " + I2S(abilityIndex) + ", bonusIndex: " + I2S(bonusId) + ", SUBTABLE=" + I2S(subtable) + ", bonues=" + I2S(bonuses + 1))
return subtable
endfunction
function FireHeroAbilityEvents takes unit u, integer paramAbilityIndex returns nothing
set heroCaster = u
set heroIndex = GetUnitUserData(u)
set abilityIndex = paramAbilityIndex
//call BJDebugMsg("Fire Event pre-check " + I2S(abilityIndex))
if heroAbility[heroIndex].trigger[abilityIndex] != null then
//call BJDebugMsg("Fire Event hasTrigger: " + I2S(abilityIndex))
call TriggerEvaluate(heroAbility[heroIndex].trigger[abilityIndex])
endif
endfunction
private function dtcHeroAbilityEventsInit takes nothing returns nothing
set heroAbility = Table.create()
endfunction
endlibrary
function HealthStoneValue takes unit u returns real
return 40. + 0.12 * MissingHp(u)
endfunction
function ManaStoneValue takes unit u returns real
return 40. + 0.12 * MissingMana(u)
endfunction
library SimpleDelayedDestroyer requires TimerUtils
//--------------- Effect ------------
struct EffectDestroyer
effect effect
private method destroy takes nothing returns nothing
call DestroyEffect(this.effect)
set this.effect = null
call this.deallocate()
endmethod
private static method destroyFromTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call this.destroy()
call ReleaseTimer(t)
set t = null
endmethod
static method destroyLater takes effect e, real delay returns nothing
local thistype this = allocate()
local timer t = NewTimerEx(this)
call TimerStart(t, delay, false, function thistype.destroyFromTimer)
set this.effect = e
set t = null
endmethod
endstruct
function DestroyEffectLater takes effect e, real delay returns nothing
call EffectDestroyer.destroyLater(e, delay)
endfunction
function DestroyLastCreatedEffectLater takes real delay returns nothing
call EffectDestroyer.destroyLater(bj_lastCreatedEffect, delay)
endfunction
//--------------- Lightning ------------
struct LightningDestroyer
lightning effect
private method destroy takes nothing returns nothing
call DestroyLightning(this.effect)
set this.effect = null
call this.deallocate()
endmethod
private static method destroyFromTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call this.destroy()
call ReleaseTimer(t)
set t = null
endmethod
static method destroyLater takes lightning e, real delay returns nothing
local thistype this = allocate()
local timer t = NewTimerEx(this)
call TimerStart(t, delay, false, function thistype.destroyFromTimer)
set this.effect = e
set t = null
endmethod
endstruct
function DestroyLightningLater takes lightning e, real delay returns nothing
call LightningDestroyer.destroyLater(e, delay)
endfunction
//--------------- Item ------------
struct ItemDestroyer
item itemToDestroy
private method destroy takes nothing returns nothing
call RemoveItem(this.itemToDestroy)
set this.itemToDestroy = null
call this.deallocate()
endmethod
private static method destroyFromTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call this.destroy()
call ReleaseTimer(t)
set t = null
endmethod
static method destroyLater takes item i, real delay returns nothing
local thistype this = allocate()
local timer t = NewTimerEx(this)
call TimerStart(t, delay, false, function thistype.destroyFromTimer)
set this.itemToDestroy = i
set t = null
endmethod
endstruct
function DestroyItemLater takes item i, real delay returns nothing
call ItemDestroyer.destroyLater(i, delay)
endfunction
function DestroyLastCreatedItemLater takes real delay returns nothing
call ItemDestroyer.destroyLater(bj_lastCreatedItem, delay)
endfunction
endlibrary
library ShildingSystem requires TimerUtils /* Also requires some unit-indexer that sets UnitUserData to a valid unique index */
globals
private constant string DEFAULT_VFX = "vfx\\shield\\Ubershield White.mdx"
//private constant string DEFAULT_VFX = "vfx\\Buff_Shield_TC.mdx"
private constant string DEFAULT_VFX_ATTACHMENT_POINT = "chest"
private constant real XOFFSET = -70.
private constant real YOFFSET = 62.5
//private constant real TICK_RATE = 0.03125000
private constant real TICK_RATE = 0.02
endglobals
struct DTCShieldInstance
real currShield
real maxShield
integer ticks
boolean persistant //If true, instance will persist even when 0 currShield or timed out, but "RemoveShieldInstance" will still destroy this
DTCShieldInstance next
public method destroy takes nothing returns nothing
set .currShield = 0
set .maxShield = 0
set .ticks = 0
set .next = 0
set .persistant = false
call deallocate()
endmethod
public method absorbDamage takes real dmgAmount returns real
set .currShield = .currShield - dmgAmount
return .currShield
endmethod
public method isTimed takes nothing returns boolean
return ticks != -1
endmethod
public method setPersistant takes boolean newValue returns DTCShieldInstance
set .persistant = newValue
return this
endmethod
static method initShield takes real amount returns DTCShieldInstance
local thistype this = thistype.allocate()
set .currShield = amount
set .maxShield = amount
set .ticks = -1
set .next = 0
set .persistant = false
return this
endmethod
static method initTimed takes real amount, real duration returns DTCShieldInstance
local thistype this = thistype.allocate()
set .currShield = amount
set .maxShield = amount
set .ticks = R2I(duration / TICK_RATE)
set .next = 0
set .persistant = false
return this
endmethod
endstruct
struct DTCShielding
private static DTCShielding array shieldingIndex
private static group shieldedUnits
private static timer textUpdateTimer
private unit u
private effect vfx
private DTCShieldInstance first
private real currentAmount
private real totalAmount
private texttag floatingText
public method RefreshAmounts takes nothing returns nothing
local DTCShieldInstance curr = .first
set .currentAmount = 0.0
set .totalAmount = 0.0
loop
set .currentAmount = .currentAmount + curr.currShield
set .totalAmount = .totalAmount + curr.maxShield
set curr = curr.next
exitwhen curr == 0
endloop
endmethod
public method IsPersistant takes nothing returns boolean
local DTCShieldInstance curr = .first
loop
if curr.persistant then
return true
endif
set curr = curr.next
exitwhen curr == 0
endloop
return false
endmethod
method RegisterShieldInstance takes DTCShieldInstance instance returns nothing
local DTCShieldInstance curr
local DTCShieldInstance prev
if .first == 0 then
set .first = instance
elseif instance.isTimed() then
set curr = .first
set prev = 0
loop
exitwhen curr.next == 0 or curr.ticks < instance.ticks
set prev = curr
set curr = curr.next
endloop
if prev == 0 then
set .first = instance
else
set prev.next = instance
endif
set instance.next = curr
else
set curr = .first
loop
exitwhen curr.next == 0
set curr = curr.next
endloop
set curr.next = instance
endif
set .currentAmount = .currentAmount + instance.currShield
set .totalAmount = .totalAmount + instance.maxShield
endmethod
public method AdjustShieldInstanceAmount takes DTCShieldInstance instance, real amount returns nothing
if instance.currShield + amount > instance.maxShield then
set amount = instance.maxShield - instance.currShield
endif
if vfx == null then
set .vfx = AddSpecialEffectTarget(DEFAULT_VFX, u, DEFAULT_VFX_ATTACHMENT_POINT)
endif
set instance.currShield = instance.currShield + amount
set currentAmount = currentAmount + amount
endmethod
public method destroy takes nothing returns nothing
local DTCShieldInstance prev = 0
local DTCShieldInstance curr = .first
call DestroyEffect(.vfx)
set .vfx = null
if IsPersistant() then
loop
if curr.persistant then
set curr.currShield = 0.
set prev = curr
set curr = curr.next
else
if prev == 0 then
set .first = curr.next
call curr.destroy()
set curr = .first
else
set prev.next = curr.next
call curr.destroy()
set curr = prev.next
endif
endif
exitwhen curr == 0
endloop
call RefreshAmounts()
else
call GroupRemoveUnit(shieldedUnits, u)
set shieldingIndex[GetUnitUserData(u)] = 0
set u = null
call DestroyEffect(.vfx)
set .vfx = null
call DestroyTextTag(.floatingText)
set floatingText = null
set currentAmount = 0.
set totalAmount = 0.
loop
exitwhen .first == 0
set curr = .first
set .first = .first.next
call curr.destroy()
endloop
if BlzGroupGetSize(shieldedUnits) == 0 then
call ReleaseTimer(textUpdateTimer)
set textUpdateTimer = null
endif
call .deallocate()
endif
endmethod
public method RemoveShieldInstance takes DTCShieldInstance instance returns nothing
local DTCShieldInstance prev = 0
local DTCShieldInstance curr = .first
if this == 0 then
return
endif
loop
if curr == instance then
if prev == 0 then //First instance
if instance.next == 0 then
//the only instance
call destroy()
else
//Current instance is first, but not the only
set .first = instance.next
call instance.destroy()
call RefreshAmounts()
endif
else
set prev.next = instance.next
call instance.destroy()
call RefreshAmounts()
endif
exitwhen true //Found and handled removal of the instance
endif
set prev = curr
set curr = curr.next
if curr == 0 then
call BJDebugMsg("Tried to remove shield isntance, but unit didn't have that instance")
endif
exitwhen curr == 0 //This shouldn't happen
endloop
//call instance.destroy()
endmethod
private method TickTimedShields takes nothing returns boolean //True means all ok, continue update
local DTCShieldInstance prev = 0
local DTCShieldInstance curr = .first
loop
if curr.isTimed() then
set curr.ticks = curr.ticks - 1
if curr.ticks == 0 then
if curr.next == 0 and prev == 0 then
call destroy()
return false
elseif prev == 0 then
set .first = curr.next
set currentAmount = currentAmount - curr.currShield
set totalAmount = totalAmount - curr.maxShield
call curr.destroy()
set curr = .first
else
set prev.next = curr.next
set currentAmount = currentAmount - curr.currShield
set totalAmount = totalAmount - curr.maxShield
call curr.destroy()
set curr = prev.next
endif
else
set prev = curr
set curr = curr.next
endif
else
set prev = curr
set curr = curr.next
endif
exitwhen curr == 0
endloop
return .first != 0
endmethod
private method UpdateShield takes nothing returns nothing
if TickTimedShields() then
call SetTextTagText(.floatingText, I2S(R2I(currentAmount)) + "/" + I2S(R2I(totalAmount)), TextTagSize2Height(8.0))
call SetTextTagPos(.floatingText, GetUnitX(.u) + XOFFSET, GetUnitY(.u) + YOFFSET, 100.)
endif
endmethod
private static method UpdateAllShieldText takes nothing returns nothing
local unit u
local group g = CreateGroup()
call BlzGroupAddGroupFast(shieldedUnits, g)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call shieldingIndex[GetUnitUserData(u)].UpdateShield()
call GroupRemoveUnit(g, u)
endloop
call DestroyGroup(g)
set g = null
endmethod
public static method ProcessDamage takes unit damageTarget, real damageAmount returns real
local thistype this = shieldingIndex[GetUnitUserData(damageTarget)]
local DTCShieldInstance prev = 0
local DTCShieldInstance curr = .first
local real shieldResult
if .u == damageTarget then
loop
set .currentAmount = .currentAmount - RMinBJ(curr.currShield, damageAmount)
set shieldResult = curr.absorbDamage(damageAmount)
set prev = curr
set curr = curr.next
if shieldResult < 0. then //Destroyed a shield instance
if curr == 0 then //No next shield
call destroy()
return -shieldResult
endif
set .totalAmount = .totalAmount - curr.maxShield
if prev.persistant then
set prev.currShield = 0.
else
call prev.destroy() //Remove shield instance
endif
set damageAmount = -shieldResult //flip remaining damage as positive and continue to next intsance
else //Shield survived the damage, 0 remaining damage
set damageAmount = 0.
exitwhen true
endif
endloop
endif
return damageAmount
endmethod
// private method DoFloatingText takes real shieldedAmount returns nothing
// if floatingText == null then
// set floatingText = CreateTextTag()
// call SetTextTagLifespan(floatingText, 1.75)
// call SetTextTagFadepoint(floatingText, 1.35)
// call SetTextTagVelocityBJ(floatingText, GetRandomReal(64, 96), GetRandomReal(60, 120))
// call SetTextTagColor(floatingText, 212, 212, 255, 200)
// endif
// call SetTextTagAge(floatingText, 0.)
// call SetTextTagText(floatingText, I2S(R2I(shieldedAmount)), TextTagSize2Height(9.))
// call SetTextTagPos(floatingText, GetUnitX(.u), GetUnitY(.u), GetRandomReal(96, 128))
// endmethod
//public static method ProcessDamage takes unit damageTarget, real damageAmount returns real
// local thistype this = shieldingIndex[GetUnitUserData(damageTarget)]
// local real shieldedAmount
// if .u != null then
// set shieldedAmount = RMinBJ(.currentAmount, damageAmount)
// set .currentAmount = .currentAmount - damageAmount
// if .currentAmount < 0.1 then
// call destroy()
// endif
// set damageAmount = damageAmount - shieldedAmount
// endif
// return damageAmount
//endmethod
public static method create takes unit u returns DTCShielding
local thistype this = thistype.allocate()
set .u = u
set .currentAmount = 0.
set .totalAmount = 0.
set .floatingText = null
set .vfx = AddSpecialEffectTarget(DEFAULT_VFX, u, DEFAULT_VFX_ATTACHMENT_POINT)
set shieldingIndex[GetUnitUserData(u)] = this
call GroupAddUnit(shieldedUnits, u)
set floatingText = CreateTextTag()
call SetTextTagColor(floatingText, 212, 212, 255, 200)
if textUpdateTimer == null then
set textUpdateTimer = NewTimer()
call TimerStart(textUpdateTimer, TICK_RATE, true, function thistype.UpdateAllShieldText)
endif
return this
endmethod
public method HasShildingInstance takes DTCShieldInstance instance returns boolean
local DTCShieldInstance curr = .first
loop
if curr == instance then
return true
endif
set curr = curr.next
exitwhen curr == 0
endloop
return false
endmethod
public static method Get takes unit u returns DTCShielding
return shieldingIndex[GetUnitUserData(u)]
endmethod
public static method HasInstance takes unit u returns boolean
return shieldingIndex[GetUnitUserData(u)].u != null
endmethod
public static method GetOrCreate takes unit u returns DTCShielding
if not HasInstance(u) then
return create(u)
endif
return Get(u)
endmethod
private static method onInit takes nothing returns nothing
set shieldedUnits = CreateGroup()
set textUpdateTimer = null
endmethod
endstruct
function AddTimedShield takes unit u, real amount, real time returns DTCShieldInstance
local DTCShieldInstance instance
if not BlzIsUnitInvulnerable(u) then
set instance = DTCShieldInstance.initTimed(amount, time)
call DTCShielding.GetOrCreate(u).RegisterShieldInstance(instance)
return instance
endif
return 0
endfunction
function AddShieldInstance takes unit u, real amount returns DTCShieldInstance
local DTCShieldInstance instance
if not BlzIsUnitInvulnerable(u) then
set instance = DTCShieldInstance.initShield(amount)
call DTCShielding.GetOrCreate(u).RegisterShieldInstance(instance)
return instance
endif
return 0
endfunction
function RemoveShieldInstance takes unit u, DTCShieldInstance instance returns nothing
call DTCShielding.Get(u).RemoveShieldInstance(instance)
endfunction
function HasShieldInstance takes unit u, DTCShieldInstance instance returns boolean
if DTCShielding.HasInstance(u) then
return DTCShielding.Get(u).HasShildingInstance(instance)
else
return false
endif
endfunction
endlibrary
library ShieldUtils initializer Meep requires ShildingSystem
globals
private constant real TICK_RATE = 0.03125000
endglobals
struct ShieldRegeneration extends array
private static integer next = 1
private integer ticks
private real percentPerTick
private unit u
private static method Tick takes nothing returns nothing
local integer i = 1
endmethod
public static method Create takes unit u, real duraction, real percentPerSecond returns nothing
local thistype this = thistype[next]
//set next = next + 1
//set .u = u
//set .percentPerTick = percentPerSecond / TICK_RATE
//set .ticks = R2I(duration / TICK_RATE)
//call BJDebugMsg("index=" + I2S(this))
endmethod
endstruct
private function Meep takes nothing returns nothing
//call ShieldRegeneration.Create(null, 1.0, 1.0)
endfunction
endlibrary
library DTCCustomStatFrames initializer InitHeroTalentButton requires DTCUtils, DTCStringUtils, Table
globals
private constant real STAT_TEXT_INSET = 0.022
private framehandle tooltipFrame = null //async variable!!
private framehandle statbackground = null //async variable!!
private framehandle statText = null //async variable!!
private framehandle statButton = null //async variable!!
private boolean wantStatsOpened = false //async variable!! - Have toggled the button on/off - Must be true to show!
private boolean showStatsIfOpened = true //async variable!! - An override to temporary hide stats-panel - Must be true to show!
private Table playerToSelection
unit customStatUnit = null //async variable!!
endglobals
private function getCDR takes unit u returns string
local real haste = GetSpellHaste(u)
return R2Sx100(haste / (haste + 100.))
endfunction
function isTooltipVisibleForLocalPlayer takes nothing returns boolean
return BlzFrameIsVisible(tooltipFrame)
endfunction
function setShowStatIfOpenedForLocalPlayer takes boolean newShowStats returns nothing
set showStatsIfOpened = newShowStats
endfunction
function updateSelectedUnit takes nothing returns nothing
local string statStr1
local string statStr2
local string statStr3
local string statStr4
local string statStr5
local string statStr6
local integer playerId
if wantStatsOpened and showStatsIfOpened and customStatUnit != null then
set playerId = GetConvertedPlayerId(GetOwningPlayer(customStatUnit))
set statStr1 = "|c00FF7800Damage: " + R2I2S(GetTotalDamage(customStatUnit)) + "|r|nMovement Speed: " + R2I2S(GetUnitMoveSpeed(customStatUnit))
set statStr2 = "|n|c00FFC899Attack Speed: " + R2Sx100(GetTotalAttackSpeed(customStatUnit)) + "%%|nAttack Cooldown: " + R2SW(GetAttackCooldown(customStatUnit), 0, 2)
set statStr3 = "s|n|c00DF00FFSpell Power: " + I2S(GetSpellPower(customStatUnit)) + "|n|c00D2B2FFSpell Haste: " + R2I2S(GetSpellHaste(customStatUnit)) + "|nCooldown Reduction: " + getCDR(customStatUnit)
set statStr4 = "%%|n|c00FF4840Critical Hit Chance: " + R2Sx100(GetCriticalHitChanceInclAgi(customStatUnit)) + "%%|nCritical Hit Damage: +" + R2Sx100(GetCriticalHitDamageInclAgi(customStatUnit)) + "%%|n|c0096f064Hp/s: "
set statStr5 = R2SW(GetHealthRegeneration(customStatUnit),1,1) + "|n|c008064ECMana/s: " + R2SW(GetManaRegeneration(customStatUnit),1,1)
set statStr6 = "|n|c0096f064Health Stone on-kill: " + R2Sx100( udg_on_kill_health_stone[playerId]) + "%%|n|c008064ECMana Stone on-kill: " + R2Sx100( udg_on_kill_mana_stone[playerId]) + "%%"
call BlzFrameSetText(statText, statStr1 + statStr2 + statStr3 + statStr4 + statStr5 + statStr6)
call BlzFrameSetVisible(statbackground, not BlzFrameIsVisible(tooltipFrame))
else
call BlzFrameSetVisible(statbackground, false)
endif
endfunction
function SetStatDisplayHero takes unit u returns nothing
set customStatUnit = u
endfunction
private function toggleStatsFrame takes nothing returns nothing
if GetTriggerPlayer() == GetLocalPlayer() then
call BlzFrameSetEnable(statButton, false)
call BlzFrameSetEnable(statButton, true)
set wantStatsOpened = not wantStatsOpened
if not wantStatsOpened then
call BlzFrameSetVisible(statbackground, false)
endif
endif
endfunction
function DeselectAction takes nothing returns nothing
//call BJDebugMsg("Deselect")
set playerToSelection.unit[GetPlayerId(GetTriggerPlayer())] = null
if GetTriggerPlayer() == GetLocalPlayer() then
call SetStatDisplayHero(null)
endif
endfunction
function SelectAction takes nothing returns nothing
local unit u = GetTriggerUnit()
local player p = GetTriggerPlayer()
//call BJDebugMsg("Select: " + GetUnitName(u))
if IsUnitType(u, UNIT_TYPE_HERO) then
set playerToSelection.unit[GetPlayerId(p)] = u
if p == GetLocalPlayer() then
call SetStatDisplayHero(u)
endif
endif
set u = null
set p = null
endfunction
function CustomFrameSelectionTriggerSetup takes nothing returns nothing
local trigger selectionTrigger = CreateTrigger()
local trigger deselectTrigger = CreateTrigger()
local integer i = 0
loop
exitwhen i > LAST_HUMAN_PLAYER_INDEX
call TriggerRegisterPlayerUnitEvent(selectionTrigger, Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
call TriggerRegisterPlayerUnitEvent(deselectTrigger, Player(i), EVENT_PLAYER_UNIT_DESELECTED, null)
set i = i + 1
endloop
call TriggerAddAction(selectionTrigger, function SelectAction)
call TriggerAddAction(deselectTrigger, function DeselectAction)
set selectionTrigger = null
set deselectTrigger = null
endfunction
function OpenTalents takes nothing returns nothing
local player p = GetTriggerPlayer()
local unit selectedUnit = playerToSelection.unit[GetPlayerId(p)]
//call BJDebugMsg("Open Talents")
set selectedUnit = null
set p = null
endfunction
function DelayedInitHeroTalentButton takes nothing returns nothing
local trigger toggleStats = CreateTrigger()
local trigger openTalents = CreateTrigger()
//Move Hero-primary-stat-icon
//local framehandle heroIcon = BlzGetFrameByName("InfoPanelIconHeroIcon", 6)
//local framehandle levelBar = BlzGetFrameByName("SimpleHeroLevelBar", 0)
//local framehandle unitPanel = BlzGetFrameByName("SimpleUnitStatsPanel", 0)
local framehandle closeStats
local framehandle talentsButton
//This is called from a timer, might as well clean it up
call DestroyTimer(GetExpiredTimer())
call CustomFrameSelectionTriggerSetup()
//Dummy leaderboard to get a parent.
//local framehandle leaderboardFrame
//call CreateLeaderboardBJ(bj_FORCE_ALL_PLAYERS, "title")
//set leaderboardFrame = BlzGetFrameByName("Leaderboard", 0)
//call BlzFrameSetSize(leaderboardFrame, 0, 0)
//call BlzFrameSetVisible(BlzGetFrameByName("LeaderboardBackdrop", 0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("LeaderboardTitle", 0), false)
set tooltipFrame = BlzGetOriginFrame(ORIGIN_FRAME_UBERTOOLTIP, 0)
//Move hero primary-stat icon to give space for a talent-button
//call BlzFrameClearAllPoints(heroIcon)
//call BlzFrameSetPoint(heroIcon, FRAMEPOINT_TOP, levelBar, FRAMEPOINT_BOTTOM, 0.023, -0.006)
//Talent button - REMOVED (maybe should be repourposed to show selected augments?)
// set talentsButton = BlzCreateFrameByType("GLUETEXTBUTTON", "unitTalentButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "ScriptDialogButton", 0)
// call BlzFrameSetAbsPoint(talentsButton, FRAMEPOINT_BOTTOMRIGHT, 0.42775, 0.115)
// call BlzFrameSetText(talentsButton, "|cffFCD20DTalents|r")
// call BlzFrameSetSize(talentsButton, 0.08, 0.035)
// call BlzFrameSetScale(talentsButton, 0.7)
//
// call BlzTriggerRegisterFrameEvent(openTalents, talentsButton, FRAMEEVENT_CONTROL_CLICK)
// call TriggerAddAction(openTalents, function OpenTalents)
//Stats Panel:
set statbackground = BlzCreateFrame("ListBoxWar3", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
set statText = BlzCreateFrameByType("TEXT", "statText", statbackground, "StandardInfoTextTemplate", 0)
call BlzFrameSetAbsPoint(statbackground, FRAMEPOINT_BOTTOMRIGHT, 0.5975, 0.135)
//call BlzFrameSetPoint(statbackground, FRAMEPOINT_RIGHT, leaderboardFrame, FRAMEPOINT_RIGHT, 0., 0.)
call BlzFrameSetSize(statbackground, 0.13765, 0.135) //0.0982)
call BlzFrameClearAllPoints(statText)
call BlzFrameSetText(statText, "Cooldown Reduction: XX.0%%|n2|n3|n4|n5|n6|n7|n8")
//call BlzFrameSetSize(statText, 0.14, 0.10)
//call BlzFrameSetPoint(statText, FRAMEPOINT_CENTER, statbackground, FRAMEPOINT_CENTER, 0., 0.)
call BlzFrameSetPoint(statText, FRAMEPOINT_BOTTOMRIGHT, statbackground, FRAMEPOINT_BOTTOMRIGHT, 0, 0)
call BlzFrameSetPoint(statText, FRAMEPOINT_TOPLEFT, statbackground, FRAMEPOINT_TOPLEFT, 0.012, -0.011)
call BlzFrameSetScale(statText, 0.95)
call TimerStart(CreateTimer(), 0.34, true, function updateSelectedUnit)
call BlzFrameSetVisible(statbackground, false)
set closeStats = BlzCreateFrameByType("GLUETEXTBUTTON", "statsButtonX", statbackground, "ScriptDialogButton", 0)
call BlzFrameSetPoint(closeStats, FRAMEPOINT_TOPRIGHT, statbackground, FRAMEPOINT_TOPRIGHT, 0., 0.)
call BlzFrameSetText(closeStats, "|cffFCD20DX")
call BlzFrameSetSize(closeStats, 0.035, 0.035)
call BlzFrameSetScale(closeStats, 0.65)
call BlzTriggerRegisterFrameEvent(toggleStats, closeStats, FRAMEEVENT_CONTROL_CLICK)
//Stats-panel-toggle-button
set statButton = BlzCreateFrameByType("GLUETEXTBUTTON", "statsButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "ScriptDialogButton", 0)
call BlzFrameSetAbsPoint(statButton, FRAMEPOINT_BOTTOMRIGHT, 0.48575, 0.115)
call BlzFrameSetSize(statButton, 0.06, 0.035)
call BlzFrameSetText(statButton, "|cffFCD20DStats|r")
call BlzFrameSetScale(statButton, 0.70)
call BlzTriggerRegisterFrameEvent(toggleStats, statButton, FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(toggleStats, function toggleStatsFrame)
//call BJDebugMsg("Successfully init")
//set unitPanel = null
set openTalents = null
set talentsButton = null
set toggleStats = null
set closeStats = null
//set leaderboardFrame = null
//set heroIcon = null
//set levelBar = null
endfunction
function InitHeroTalentButton takes nothing returns nothing
//call DelayedInitHeroTalentButton()
call TimerStart(CreateTimer(), 0.4, false, function DelayedInitHeroTalentButton)
set playerToSelection = Table.create()
endfunction
endlibrary
library HeroTalentsUsingCards initializer init requires TalentCardLib, DTCHeroAbilityEvents, Table, OnHitSystem
globals
constant integer DV_TALENTS = 5
constant integer SA_TALENTS = 8
constant integer KH_TALENTS = 5
constant integer MT_TALENTS = 5
constant integer MC_TALENTS = 3
constant integer HH_TALENTS = 3
constant integer TS_TALENTS = 5
constant integer AL_TALENTS = 5
constant integer GENERIC_TALENTS = 13
endglobals
// ============ Darth Vader =========
private function DVActivatePull takes nothing returns nothing
local Table bonusSubtable = RegisterTimedBonus(udg_dv_hero, AE_DV_FORCE_PUSH_INDEX, DTCShielding.typeid, 5.0)
set bonusSubtable.real[AE_AMOUNT_SPELL_POWER] = 0.35
set bonusSubtable.real[AE_AMOUNT_MAX_HP] = 0.04
set udg_dv_force_pull = true
endfunction
private function DVInitForcePull takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Force Pull"
local string text = "Changes your Force Push into Force Pull. When cast, gain |c0096f0644%% of max hp|r + |c00DF00FF35%% of Spell Power|r |c00D4D4FFShield|r for 5 seconds."
call TriggerAddCondition(activate, Filter(function DVActivatePull))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateExtendedPushRange takes nothing returns nothing
set udg_dv_force_push_min_range = udg_dv_force_push_min_range + 225.
endfunction
private function DVQExtendedPushRange takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Push it to the Limit"
local string text = "Increases the minimum push distance of Force Push from " + R2I2S(udg_dv_force_push_min_range) + " to " + R2I2S(udg_dv_force_push_min_range + 225.) + "."
call TriggerAddCondition(activate, Filter(function ActivateExtendedPushRange))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateDVRDrainForce takes nothing returns nothing
set udg_dv_choke_drain_force_talent = true
endfunction
private function DVRDrainForce takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Drain Force Energy"
local string text = "Choking also drains force energy from the target. Lower all |c0096f064healing|r of Force Choke by 33%% and convert it to restore |c008064ECmana|r. The |c0096f064health scaling|r is converted to |c008064ECmana scaling|r."
call TriggerAddCondition(activate, Filter(function ActivateDVRDrainForce))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateDVLightningShock takes nothing returns nothing
local ability forcelightning = BlzGetUnitAbility(udg_dv_hero, 'A07E')
set udg_dv_lightning_shock_talent = true
if forcelightning != null then
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_NORMAL, 0, 4.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_NORMAL, 1, 4.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_NORMAL, 2, 4.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_NORMAL, 3, 4.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_NORMAL, 4, 4.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_HERO, 0, 2.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_HERO, 1, 2.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_HERO, 2, 2.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_HERO, 3, 2.)
call BlzSetAbilityRealLevelField(forcelightning, ABILITY_RLF_DURATION_HERO, 4, 2.)
endif
set forcelightning = null
endfunction
private function DVLightningShock takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Lightning Shock"
local string text = "Force Lightning slow duration is increased by 100%%."
call TriggerAddCondition(activate, Filter(function ActivateDVLightningShock))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateDVExploitWeakness takes nothing returns nothing
set udg_dv_exploit_weakness_talent = true
endfunction
private function DVExploitWeakness takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Exploit Weakness"
local string text = "When Force Lightning hits a Force Slowed target, stun for half the slow duration and if the heal from Force Lightning wouldn't occur, heal with 50%% effectivness."
call TriggerAddCondition(activate, Filter(function ActivateDVExploitWeakness))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
// === Samus ===
private function ActivateMissileNoFriendlyFire takes nothing returns nothing
set sa_missile_friendly_fire = false
set sa_missile_explosion_radius = sa_missile_explosion_radius + 40.
endfunction
private function SAQMissileNoFriendlyFire takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Smart Missiles"
local string text = "Missiles no longer hit or damages friendly targets. Missile explosion radius is also increased by 40."
call TriggerAddCondition(activate, Filter(function ActivateMissileNoFriendlyFire))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMissileIncreaseEmpower takes nothing returns nothing
set sa_missile_empower_distance = sa_missile_empower_distance - 150.
set sa_missile_empowerd_crit_damage = sa_missile_empowerd_crit_damage + 0.2
endfunction
private function SAQMissileIncreaseEmpower takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Enhanced Empowered Missiles"
local string text = "Missiles need to travel 150 units shorter before becoming |c00CCAACCEMPOWERED|r and |c00CCAACCEMPOWERED|r damage is increased by another |c00FF4840+20%% of your Critical Strike Damage|r."
call TriggerAddCondition(activate, Filter(function ActivateMissileIncreaseEmpower))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateBombShilding takes nothing returns nothing
set udg_sa_bomb_shielding_talent = true
endfunction
private function SAQBombShilding takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Shielding Bombs"
local string text = "Allies within the blast radius of a bomb gains a |c00D4D4FFShield|r for |c00D4D4FF60%% of the bombs damage|r for 5 seconds."
call TriggerAddCondition(activate, Filter(function ActivateBombShilding))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateRemnantEnergy takes nothing returns nothing
set udg_sa_remnant_energy_active = true
call EnableTrigger( gg_trg_SAConsumeRemnantEnergy )
endfunction
private function SAQRRemnantEnergy takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Remnant Energy"
local string text = "Overcharge and Charge Beam leaves |c00CCAACCRemnant Energy|r on enemies hit. When Samus lands an attack on an enemy with |c00CCAACCRemnant Energy|r, the energy is detonated, dealing "
set text = text + "|c00FF780010%% of Damage|r + |c00DF00FF10%% of Spell Power|r damage, restoreing |c008064EC8 mana|r and gives a |c00D4D4FFShield|r of |c00FF780010%% of Damage|r + |c00DF00FF10%% of Spell Power|r for 5 seconds."
call TriggerAddCondition(activate, Filter(function ActivateRemnantEnergy))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateChargeUpBomb takes nothing returns nothing
set udg_sa_charge_up_bomb_talent = true
endfunction
private function SAQQChargeUpBomb takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Charge Up Bomb"
local string text = "When you fully charge your Charge Beam, place a Bomb at your current location."
call TriggerAddCondition(activate, Filter(function ActivateChargeUpBomb))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateAutoBomb takes nothing returns nothing
call EnableTrigger( gg_trg_SAAutoBomb )
endfunction
private function SAAutoBomb takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Auto Bomb"
local string text = "Your attacks have a 10%% chance of placeing a Bomb at your current location."
call TriggerAddCondition(activate, Filter(function ActivateAutoBomb))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateOverchargeMissiles takes nothing returns nothing
set udg_sa_overcharge_missiles_talent = true
endfunction
private function SAOverchargeMissiles takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Overcharge Missiles"
local string text = "After Overcharge has fired, fire 3 Missiles in the same direction with a small arc."
call TriggerAddCondition(activate, Filter(function ActivateOverchargeMissiles))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateChargedUpWeapon takes nothing returns nothing
set udg_sa_charged_up_weapon_talent = true
endfunction
private function SAChargedUpWeapon takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Charged up weapon"
local string text = "Each Charge Beam Charge generated gives |c00FF4840+1%% Critical Hit Chance|r and |c00FF4840+1.5%% Critical Hit Damage|r for 16 seconds."
call TriggerAddCondition(activate, Filter(function ActivateChargedUpWeapon))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
// === Khadgar ===
private function ActivateHeavierRampingBlizzard takes nothing returns nothing
set udg_kh_blizzard_base_shards_ps = udg_kh_blizzard_base_shards_ps - 3.0
set udg_kh_blizzard_base_accelerate_ps = udg_kh_blizzard_base_accelerate_ps + 1.5
set udg_kh_blizzard_manacost_inc_ps = udg_kh_blizzard_manacost_inc_ps + 1.0
endfunction
private function TalentHeavierRampingBlizzard takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Heavier Ramping Blizzard"
local string text = "Blizzard is a little slower to start, lowering initial Shards Per Second by 3, but ramps faster, increaseing added Shards Per Second by 1.5 but |c008064EC+1 mana cost|r increase per second."
call TriggerAddCondition(activate, Filter(function ActivateHeavierRampingBlizzard))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateShieldingFlameShield takes nothing returns nothing
set kh_flame_shield_shield_talent = true
endfunction
private function TalentShieldingFlameShield takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Shielding Flame Shield"
local string text = "If cast on ally, |c00D4D4FFShield|r it for 125%% of its damage per second for the next 10 seconds."
call TriggerAddCondition(activate, Filter(function ActivateShieldingFlameShield))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateSoothingFlameShield takes nothing returns nothing
set kh_flame_shield_soothing_talent = true
endfunction
private function TalentSoothingFlameShield takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Soothing Flame Shield"
local string text = "Instead of dealing |c00FF0000Friendly Fire!|r, the flames sooth allies, |c00D4D4FFShielding|r them for 14%% of the damage for 4 seconds."
call TriggerAddCondition(activate, Filter(function ActivateSoothingFlameShield))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateKHSplittingFireball takes nothing returns nothing
set kh_fireball_split_on_amp_flameshield = true
endfunction
private function KHSplttingFireball takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Flame Shield Fireball Splitter"
local string text = "When a Fireball bounce lands on a target with |c00AEBFF1Amplified Flame Shield|r, 3 new Fireballs originates from that target, going in direction of the Flame Shield fire orbs."
call TriggerAddCondition(activate, Filter(function ActivateKHSplittingFireball))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateKHExtendedFireballBounces takes nothing returns nothing
set kh_fireball_bounces = kh_fireball_bounces + 2
endfunction
private function KHExtendedFireballBounces takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Extended Fireball Bounces"
local string text = "Your Fireball tavels further by adding 2 additional bounces on the end of the Fireball."
call TriggerAddCondition(activate, Filter(function ActivateMissileNoFriendlyFire))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
// === MammothTank ===
private function ActivateMTAutoEnergizingTuskMissiles takes nothing returns nothing
call EnableTrigger( gg_trg_MTTuskMissilesAutoEnergizeTalent )
endfunction
private function MTAutoEnergizingTuskMissiles takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Auto Energizing Tusks"
local string text = "On enemy kill, lower the remaining cooldown of Tusk Missiles by 1 second."
call TriggerAddCondition(activate, Filter(function ActivateMTAutoEnergizingTuskMissiles))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMTV2TuskTargeting takes nothing returns nothing
set udg_mt_v2_tusk_targeting_talent = true
endfunction
private function MTV2TuskTargeting takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "V2 Tusk Targeting"
local string text = "If you fire V2 Rocket within Tusk Missile range, fire Tusk Missiles at center of V2 Rocket target."
call TriggerAddCondition(activate, Filter(function ActivateMTV2TuskTargeting))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMTMotherlandsGrace takes nothing returns nothing
set udg_mt_motherland_hpsmhp = udg_mt_motherland_hpsmhp + 0.004
endfunction
private function MTMotherlandsGrace takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Motherland's Grace"
local string text = "Agghhh Motherland healing is increased by |c0096f0640.4%% of missing hp|r per second."
call TriggerAddCondition(activate, Filter(function ActivateMTMotherlandsGrace))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMTHighExplosiveTuskMissiles takes nothing returns nothing
set udg_mt_tusk_range = udg_mt_tusk_range - 100.
set udg_mt_tusk_radius = udg_mt_tusk_radius + 45.
endfunction
private function MTHighExplosiveTuskMissiles takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "High Explosive Tusk Missiles"
local string text = "Increase Tusk Missiles explosion radius by 45 but lower max range by 100."
call TriggerAddCondition(activate, Filter(function ActivateMTHighExplosiveTuskMissiles))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMTV2KrakMissile takes nothing returns nothing
local ability v2ability = BlzGetUnitAbility(udg_mammoth_tank_hero, 'A028')
local real oldValue = BlzGetAbilityRealLevelField(v2ability, ABILITY_RLF_AREA_OF_EFFECT, 0)
set udg_mt_v2_krak_talent = true
if v2ability != null then
call BlzSetAbilityRealLevelField(v2ability, ABILITY_RLF_AREA_OF_EFFECT, 0, oldValue - 40.)
call BlzSetAbilityRealLevelField(v2ability, ABILITY_RLF_AREA_OF_EFFECT, 1, oldValue - 40.)
call BlzSetAbilityRealLevelField(v2ability, ABILITY_RLF_AREA_OF_EFFECT, 2, oldValue - 40.)
call BlzSetAbilityRealLevelField(v2ability, ABILITY_RLF_AREA_OF_EFFECT, 3, oldValue - 40.)
call BlzSetAbilityRealLevelField(v2ability, ABILITY_RLF_AREA_OF_EFFECT, 4, oldValue - 40.)
endif
endfunction
private function MTV2KrakMissile takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "V2 Krak Missile"
local string text = "Damage against units within 75 units of the center is increased by 40%%, not knocked away and are stunned for 1 second, but explosion radius is reduced by 40 units."
call TriggerAddCondition(activate, Filter(function ActivateMTV2KrakMissile))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
//=== TODO ===
private function ActivateMTIronCurtainTalent takes nothing returns nothing
//call EnableTrigger( gg_trg_MTTuskMissilesAutoEnergizeTalent )
endfunction
private function MTIronCurtainTalent takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Iron Curtain"
local string text = "TODO: Every 3rd cast of Agghhh Motherland Iron Curtains the Mammoth Tank, |c00D4D4FFshielding|r for |c0096f06475%% of max hp|r for 12 seconds."
call TriggerAddCondition(activate, Filter(function ActivateMTIronCurtainTalent))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
//=== TODO ===
// === Master Chief ===
private function ActivateMCFreshWeapon takes nothing returns nothing
set udg_mc_fresh_weapon_talent = 1
endfunction
private function MCFreshWeapon takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Fresh Weapon"
local string text = "When changing weapon gain |c00FF4840+14%%/+7%% Critical Hit Chance|r when Rouletting/Switching weapon for 14 seconds when keeping that weapon. 50 second cooldown when Switching weapons."
call TriggerAddCondition(activate, Filter(function ActivateMCFreshWeapon))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMCGrenadeAvoidence takes nothing returns nothing
set MCPlasmaGrenadeNoFriendlyFireTalent = true
endfunction
private function MCGrenadeAvoidence takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Grenade Avoidence"
local string text = "Your Plasma Grenade no longer deal |c00FF0000Friendly Fire!|r."
call TriggerAddCondition(activate, Filter(function ActivateMCGrenadeAvoidence))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMCActiveTargeting takes nothing returns nothing
set udg_mc_active_targeting_talent = true
call EnableTrigger( gg_trg_MCActiveTagetingMarkTimer )
call EnableTrigger( gg_trg_MCActiveTargetingConsume )
endfunction
private function MCActiveTargeting takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Active Targeting"
local string text = "The mark enemies hit by Vehicle Drop, and passivly every 7 seconds the enemy furtherest away (within 1500 units) is also marked.|nConsume the mark on attack hit, amplify the damage by 25%% and ignore 75%% of "
set text = text + "the target's armor for that attack and gain +|c00FF484010%% Critical Hit Chance|r for the next 4 seconds."
call TriggerAddCondition(activate, Filter(function ActivateMCActiveTargeting))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
// === ShadowDancer ===
private function ActivateTrippleShadow takes nothing returns nothing
set udg_hh_tripple_shadow_talent = true
endfunction
private function HHTrippleShadow takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Double Shadow Assasination"
local string text = "Double Strike Assasination also leaves a Living Shadow as the Shadow Dancer disappear, but no longer amplify the damage to the primary target."
call TriggerAddCondition(activate, Filter(function ActivateTrippleShadow))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateDoubleStrikeShuriken takes nothing returns nothing
set udg_hh_double_strike_shurik_talent = true
endfunction
private function HHDoubleStrikeShuriken takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Double Ninja Strike Shuriken Attack"
local string text = "Landing at least two Ninja Strikes on the same target at once (from a Living Shadow or the Shadow Dancer) throws a Shuriken at that target (from both Living Shadows and the Shadow Dancer)."
call TriggerAddCondition(activate, Filter(function ActivateDoubleStrikeShuriken))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateShadowFocus takes nothing returns nothing
set udg_hh_ability_damage_modifier = udg_hh_ability_damage_modifier - 0.15
set udg_hh_living_shadow_mirror_percen = udg_hh_living_shadow_mirror_percen + 0.2
set udg_hh_living_shadow_time_modifier = udg_hh_living_shadow_time_modifier + 1.25
endfunction
private function HHShadowFocus takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Shadow Focus"
local string text = "Living Shadows lasts for 1.25 more second and ability mirroring damage increased by 20%% (from 50%% to 70%%), but the damage from the Shadow Dancer own abilities is lowered by 15%% (from 100%% to 85%%)."
call TriggerAddCondition(activate, Filter(function ActivateShadowFocus))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
// === Tassadar ===
private function ActivateStormIntensity takes nothing returns nothing
set ts_ps_wave_delay = ts_ps_wave_delay * 0.66666
set ts_ps_damage_modifier = ts_ps_damage_modifier - 0.1
call ModifyCDRDefaultCooldown(udg_ts_hero, TS_PS_ABILITY_ID, -1.0)
endfunction
private function TSStormIntensity takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Storm Intensity"
local string text = "Psionic Storm deals its damage faster, reduces cooldown and total damage. Waves occur 3 times per second (up from 2 per second), base cooldown is reduced by 1 second and damage per wave is reduced by 10%%."
call TriggerAddCondition(activate, Filter(function ActivateStormIntensity))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateStormDuration takes nothing returns nothing
set ts_ps_wave_count = ts_ps_wave_count + 2
endfunction
private function TSStormDuration takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Storm Duration"
local string text = "Psionic Storm lasts for 2 more waves of damage."
call TriggerAddCondition(activate, Filter(function ActivateStormDuration))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateKhalaSurge takes nothing returns nothing
set udg_ts_psiassault_psi_surge_talent = true
endfunction
private function TSKhalaSurge takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Khala Surge"
local string text = "Number of Psionic Assault projectiles is halved (rounded up), damage is doubled and the damage is dealt in a 70 unit radius around the target.|n|c00888888Halves projectiles from the spell cast, not other effects."
call TriggerAddCondition(activate, Filter(function ActivateKhalaSurge))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateAssaultSwarm takes nothing returns nothing
set udg_ts_psiassault_mana_per_bonus = udg_ts_psiassault_mana_per_bonus - 35
set udg_ts_psiassault_projectiles_base = udg_ts_psiassault_projectiles_base + 1
endfunction
private function TSAssaultSwarm takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Psionic Assault Swarm"
local string text = "Increase Psionic Assault spell cast projectiles by 1 and lower mana requirement for bonus projectiles by 35 mana."
call TriggerAddCondition(activate, Filter(function ActivateAssaultSwarm))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateAssistanceOfAdun takes nothing returns nothing
set ts_assistanceOfAdun_talent = true
endfunction
private function TSAssistanceOfAdun takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Assistance of Adun"
local string text = "When a Psionic Assault projectile hits a full health target, fire 3 Psionic Assault projectiles towards that target."
call TriggerAddCondition(activate, Filter(function ActivateAssistanceOfAdun))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
// === Anduin Lothar ===
private function ActivateBranniganLeadership takes nothing returns nothing
set al_leadership_units_allowed = al_leadership_units_allowed + 2
set al_leadership_cooldown_base = al_leadership_cooldown_base - 6.
set al_leadership_cooldown_captain = al_leadership_cooldown_captain - 6.
set al_leadership_scaling_hp_bonus = al_leadership_scaling_hp_bonus - 0.12
//set al_leadership_flat_hp_bonus = al_leadership_flat_hp_bonus - 150.
endfunction
private function ALBranniganLeadership takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Brannigan Leadership Style"
local string text = "Quantity before quality, lower footman rally time by 6 seconds, increase footman capacity by 2 but reduce their |c0096f064hp-scaling|r by |c0096f06412%% of your hp|r|r."
set text = text + "|n|c00888888'...And like all my plans, it's so simple an idiot could have devised it!'"
call TriggerAddCondition(activate, Filter(function ActivateBranniganLeadership))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateCommander takes nothing returns nothing
set al_leadership_units_allowed = al_leadership_units_allowed + 1
set al_leadership_flat_hp_bonus = al_leadership_flat_hp_bonus + 25.
endfunction
private function ALCommander takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Commander"
local string text = "Increase footman capacity by 1 and their hp by |c0096f06425|r."
call TriggerAddCondition(activate, Filter(function ActivateCommander))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateEliteSoldiers takes nothing returns nothing
set al_leadership_units_allowed = al_leadership_units_allowed - 1
set al_leadership_flat_hp_bonus = al_leadership_flat_hp_bonus + 175.
//set al_leadership_scaling_hp_bonus = al_leadership_scaling_hp_bonus + 0.08
set al_leadership_flat_dmg_bonus = al_leadership_flat_dmg_bonus + 10.
set al_leadership_scaling_dmg_bonus = al_leadership_scaling_dmg_bonus + 0.08
endfunction
private function ALEliteSoldiers takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Elite Soldiers"
local string text = "Increase footmen |c00FF7800damage|r by |c00FF780010 + 8%% of your damage|r and |c0096f064hp|r by |c0096f064175|r but reduce Footmen capacity by 1."
call TriggerAddCondition(activate, Filter(function ActivateEliteSoldiers))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateEliteCaptain takes nothing returns nothing
set al_leadership_cooldown_captain = al_leadership_cooldown_captain - 2.
set al_leadership_captain_bonus = al_leadership_captain_bonus + 0.25
endfunction
private function ALEliteCaptain takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Elite Captain"
local string text = "Increase Footman Captain bonuses by another 25%% and their bonus to footman rally time by another 2 seconds."
call TriggerAddCondition(activate, Filter(function ActivateEliteCaptain))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateExecutingSlash takes nothing returns nothing
set udg_al_slash_execute_talent = true
endfunction
private function ALExecutingSlash takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Executing Slash"
local string text = "Slash deals |c0050b4784%% of target's missing hp|r additional damage.|n|c00888888This damage is added before Critial Hits bonus is added."
call TriggerAddCondition(activate, Filter(function ActivateExecutingSlash))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
// === Generic ===
private function ActivateDmg takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusDamage.typeid, 30.)
endfunction
private function GenericDmgCard takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Sharpness"
local string text = "Gain |c00FF7800+30 damage|r."
call TriggerAddCondition(activate, Filter(function ActivateDmg))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateSpellPower takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusSpellPower.typeid, 40.)
endfunction
private function GenericSpellPowerCard takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Arcane"
local string text = "Gain |c00DF00FF+40 Spell Power|r."
call TriggerAddCondition(activate, Filter(function ActivateSpellPower))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateArmor takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusArmor.typeid, 35.)
endfunction
private function GenericArmorCard takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Plated"
local string text = "Gain |c008AAAC0+35 armor|r."
call TriggerAddCondition(activate, Filter(function ActivateArmor))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateCrit takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusCriticalDamage.typeid, 0.10)
call AddUnitBonus(udg_g_unit1, BonusCriticalChance.typeid, 0.03)
endfunction
private function GenericCritCard takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Deadly"
local string text = "Gain |c00FF4840+10%% Critical Hit Damage|r and |c00FF4840+3%% Critical Hit Chance|r."
call TriggerAddCondition(activate, Filter(function ActivateCrit))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateAllStats takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusStrength.typeid, 8.)
call AddUnitBonus(udg_g_unit1, BonusAgility.typeid, 8.)
call AddUnitBonus(udg_g_unit1, BonusIntelligence.typeid, 8.)
endfunction
private function GenericAllStats takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Jack of all Trades"
local string text = "Gain |c0078DCC8+8 to all stats|r."
call TriggerAddCondition(activate, Filter(function ActivateAllStats))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateHaste takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusSpellHaste.typeid, 20.)
endfunction
private function GenericHaste takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Spell Slinger"
local string text = "Gain |c00D2B2FF+20 Spell Haste|r."
call TriggerAddCondition(activate, Filter(function ActivateHaste))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateStoneDropping takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusHealthStone.typeid, 0.0225)
call AddUnitBonus(udg_g_unit1, BonusManaStone.typeid, 0.0225)
endfunction
private function GenericStoneDropping takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Stone Dropper"
local string text = "Gain +2.25%% |c0096f064Health Stone|r and |c008064ECMana Stone|r drop chance."
call TriggerAddCondition(activate, Filter(function ActivateStoneDropping))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateLifeOnKill takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusLifeOnKillFlat.typeid, 1.0)
call AddUnitBonus(udg_g_unit1, BonusLifeOnKillMissing.typeid, 0.01)
endfunction
private function GenericLifeOnKill takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Life on Kill"
local string text = "When killing an enemy, restore |c0096f0641 + 1%% of missing hp|r hp."
call TriggerAddCondition(activate, Filter(function ActivateLifeOnKill))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateManaOnKill takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusManaOnKillFlat.typeid, 2.0)
call AddUnitBonus(udg_g_unit1, BonusManaOnKillMissing.typeid, 0.01)
endfunction
private function GenericManaOnKill takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Mana on Kill"
local string text = "When killing an enemy, restore |c008064EC2.0 + 1%% of missing mana|r mana."
call TriggerAddCondition(activate, Filter(function ActivateManaOnKill))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateSpellPowerPercent takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusSpellPowerMultiplier.typeid, 0.15)
endfunction
private function GenericSpellPowerPercent takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Amplified Power"
local string text = "Gain |c00DF00FF+15%% Spell Power Multiplier|r.|n|c00888888Spell Power Multipliers are additive."
call TriggerAddCondition(activate, Filter(function ActivateSpellPowerPercent))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateLifeOnHit takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusLifeOnHit.typeid, 1.50)
endfunction
private function GenericLifeOnHit takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Leech"
local string text = "Restore |c0096f0641.5 hp|r on-attack-hit.|n|c00888888Lowered to 30%% effectivness for further attacks within 0.5 seconds."
call TriggerAddCondition(activate, Filter(function ActivateLifeOnHit))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMovementSpeed takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusMovementSpeed.typeid, 35.0)
endfunction
private function GenericMovementSpeed takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Need for Speed"
local string text = "Gain +35 Movement Speed.|n|c00888888Max movement speed is 522."
call TriggerAddCondition(activate, Filter(function ActivateMovementSpeed))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateHealth takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusHealth.typeid, 375.0)
endfunction
private function GenericHealth takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Vitality"
local string text = "Gain |c0096f064+375 Health|r."
call TriggerAddCondition(activate, Filter(function ActivateHealth))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
private function ActivateMana takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusMana.typeid, 400.0)
endfunction
private function GenericMana takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Mystic"
local string text = "Gain |c008064EC+400 Mana|r."
call TriggerAddCondition(activate, Filter(function ActivateMana))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
/*
private function ActivateShieldBeforeTheStorm takes nothing returns nothing
call AddUnitBonus(udg_g_unit1, BonusShieldOnWaveStartPercentMaxHp.typeid, 0.05)
call AddUnitBonus(udg_g_unit1, BonusShieldOnWaveStartPercentDamage.typeid, 0.50)
call AddUnitBonus(udg_g_unit1, BonusShieldOnWaveStartPercentSpellPower.typeid, 0.50)
endfunction
private function GenericShieldBeforeTheStorm takes integer pid, integer tier, integer card returns nothing
local trigger activate = CreateTrigger()
local string title = "Shield before the Storm"
local string text = "Gain on-wave-start, gain a |c00D4D4FFShield|r for |c0096f0645%% of max hp|r + |c00FF780050%% of damage|r + |c00DF00FF50%% of Spell Power|r.|n|c00888888Also gained before Friendly Battle Arena"
call TriggerAddCondition(activate, Filter(function ActivateShieldBeforeTheStorm))
call setCardInfo(pid, tier, card, title, text, activate)
set activate = null
endfunction
*/
// === Setup ===
private function rollTalent takes integer maxCount, integer bitArray returns integer
local integer roll
loop
set roll = bitFromBitIndex(GetRandomInt(0, maxCount - 1))
if roll == -1 then
return -1
endif
exitwhen BlzBitAnd(roll, bitArray) == 0
endloop
return roll
endfunction
function SetupGenericTalent takes integer pid, integer tier, integer card, integer genericTalentBits returns integer
local integer roll = rollTalent(GENERIC_TALENTS, genericTalentBits)
if roll == -1 then
set genericTalentBits = 0
set roll = rollTalent(GENERIC_TALENTS, 0)
endif
set genericTalentBits = BlzBitOr(genericTalentBits, roll)
if roll == 1 then
call GenericDmgCard(pid, tier, card)
elseif roll == 2 then
call GenericSpellPowerCard(pid, tier, card)
elseif roll == 4 then
call GenericArmorCard(pid, tier, card)
elseif roll == 8 then
call GenericCritCard(pid, tier, card)
elseif roll == 16 then
call GenericAllStats(pid, tier, card)
elseif roll == 32 then
call GenericHaste(pid, tier, card)
elseif roll == 64 then
call GenericStoneDropping(pid, tier, card)
elseif roll == 128 then
call GenericLifeOnKill(pid, tier, card)
elseif roll == 256 then
call GenericManaOnKill(pid, tier, card)
elseif roll == 512 then
call GenericSpellPowerPercent(pid, tier, card)
elseif roll == 1024 then
call GenericLifeOnHit(pid, tier, card)
elseif roll == 2048 then
call GenericMovementSpeed(pid, tier, card)
elseif roll == 4096 then
call GenericHealth(pid, tier, card)
//call GenericShieldBeforeTheStorm(pid, tier, card)
elseif roll == 8192 then
call GenericMana(pid, tier, card)
elseif roll == 16384 then
//call (pid, tier, card)
elseif roll == 32768 then
//call (pid, tier, card)
elseif roll == 65536 then
//call (pid, tier, card)
elseif roll == 131072 then
//call (pid, tier, card)
elseif roll == 262144 then
//call (pid, tier, card)
elseif roll == 524288 then
//call (pid, tier, card)
elseif roll == 1048576 then
//call (pid, tier, card)
elseif roll == 2097152 then
//call (pid, tier, card)
elseif roll == 4194304 then
//call (pid, tier, card)
elseif roll == 8388608 then
//call (pid, tier, card)
elseif roll == 16777216 then
//call (pid, tier, card)
elseif roll == 33554432 then
//call (pid, tier, card)
endif
return genericTalentBits
endfunction
function SetupDVTalents takes player p returns nothing
local integer genericTalentBits = 0
local integer heroTalentBits = 0
local integer heroSpecificTalents = 0
local integer heroSpecificTalentsPerTier = 2
local integer heroSpecificTalentsThisTier
local integer roll
local integer tier = 0
local integer card
local integer pid = GetPlayerId(p)
call aug.unit.save(pid+PLAYER_HERO, udg_dv_hero)
loop
set card = 1
set heroSpecificTalentsThisTier = 0
loop
//Last card or too few remaining hero specific talents
if card == CARD_MAX or heroSpecificTalentsThisTier >= heroSpecificTalentsPerTier or heroSpecificTalents >= DV_TALENTS then
//Go for generic
set genericTalentBits = SetupGenericTalent(pid, tier, card, genericTalentBits)
else
//Go for hero-specific
set heroSpecificTalents = heroSpecificTalents + 1
set heroSpecificTalentsThisTier = heroSpecificTalentsThisTier + 1
set roll = rollTalent(DV_TALENTS, heroTalentBits)
set heroTalentBits = BlzBitOr(heroTalentBits, roll)
if roll == 1 then
call DVInitForcePull(pid, tier, card)
elseif roll == 2 then
call DVQExtendedPushRange(pid, tier, card)
elseif roll == 4 then
call DVRDrainForce(pid, tier, card)
elseif roll == 8 then
call DVLightningShock(pid, tier, card)
elseif roll == 16 then
call DVExploitWeakness(pid, tier, card)
endif
endif
//call BJDebugMsg("SetupDVAugments: tier=" + I2S(tier) + ", card=" + I2S(card) + ", gt-bits=" + I2S(genericTalentBits) +", specific" + I2S(heroTalentBits))
set card = card + 1
exitwhen card > CARD_MAX
endloop
set tier = tier + 1
exitwhen tier > TIER_MAX
endloop
endfunction
function SetupSATalents takes player p returns nothing
local integer genericTalentBits = 0
local integer heroTalentBits = 0
local integer heroSpecificTalents = 0
local integer heroSpecificTalentsPerTier = 3
local integer heroSpecificTalentsThisTier
local integer roll
local integer tier = 0
local integer card
local integer pid = GetPlayerId(p)
call aug.unit.save(pid+PLAYER_HERO, udg_samus_hero)
loop
set card = 1
set heroSpecificTalentsThisTier = 0
loop
//Last card or too few remaining hero specific talents
if card == CARD_MAX or heroSpecificTalentsThisTier >= heroSpecificTalentsPerTier or heroSpecificTalents >= SA_TALENTS then
//Go for generic
set genericTalentBits = SetupGenericTalent(pid, tier, card, genericTalentBits)
else
//Go for hero-specific
set heroSpecificTalents = heroSpecificTalents + 1
set heroSpecificTalentsThisTier = heroSpecificTalentsThisTier + 1
set roll = rollTalent(SA_TALENTS, heroTalentBits)
set heroTalentBits = BlzBitOr(heroTalentBits, roll)
if roll == 1 then
call SAQMissileNoFriendlyFire(pid, tier, card)
elseif roll == 2 then
call SAQMissileIncreaseEmpower(pid, tier, card)
elseif roll == 4 then
call SAQBombShilding(pid, tier, card)
elseif roll == 8 then
call SAQRRemnantEnergy(pid, tier, card)
elseif roll == 16 then
call SAQQChargeUpBomb(pid, tier, card)
elseif roll == 32 then
call SAAutoBomb(pid, tier, card)
elseif roll == 64 then
call SAOverchargeMissiles(pid, tier, card)
elseif roll == 128 then
call SAChargedUpWeapon(pid, tier, card)
endif
endif
//call BJDebugMsg("SetupSAAugments: tier=" + I2S(tier) + ", card=" + I2S(card) + ", gt-bits=" + I2S(genericTalentBits) +", specific" + I2S(heroTalentBits))
set card = card + 1
exitwhen card > CARD_MAX
endloop
set tier = tier + 1
exitwhen tier > TIER_MAX
endloop
endfunction
function SetupKHTalents takes player p returns nothing
local integer genericTalentBits = 0
local integer heroTalentBits = 0
local integer heroSpecificTalents = 0
local integer heroSpecificTalentsPerTier = 2
local integer heroSpecificTalentsThisTier
local integer roll
local integer tier = 0
local integer card
local integer pid = GetPlayerId(p)
call aug.unit.save(pid+PLAYER_HERO, udg_kh_hero)
loop
set card = 1
set heroSpecificTalentsThisTier = 0
loop
//Last card or too few remaining hero specific talents
if card == CARD_MAX or heroSpecificTalentsThisTier >= heroSpecificTalentsPerTier or heroSpecificTalents >= KH_TALENTS then
//Go for generic
set genericTalentBits = SetupGenericTalent(pid, tier, card, genericTalentBits)
else
//Go for hero-specific
set heroSpecificTalents = heroSpecificTalents + 1
set heroSpecificTalentsThisTier = heroSpecificTalentsThisTier + 1
set roll = rollTalent(KH_TALENTS, heroTalentBits)
set heroTalentBits = BlzBitOr(heroTalentBits, roll)
if roll == 1 then
call TalentHeavierRampingBlizzard(pid, tier, card)
elseif roll == 2 then
call TalentShieldingFlameShield(pid, tier, card)
elseif roll == 4 then
call TalentSoothingFlameShield(pid, tier, card)
elseif roll == 8 then
call KHSplttingFireball(pid, tier, card)
elseif roll == 16 then
call KHExtendedFireballBounces(pid, tier, card)
endif
endif
set card = card + 1
exitwhen card > CARD_MAX
endloop
set tier = tier + 1
exitwhen tier > TIER_MAX
endloop
endfunction
function SetupMTTalents takes player p returns nothing
local integer genericTalentBits = 0
local integer heroTalentBits = 0
local integer heroSpecificTalents = 0
local integer heroSpecificTalentsPerTier = 2
local integer heroSpecificTalentsThisTier
local integer roll
local integer tier = 0
local integer card
local integer pid = GetPlayerId(p)
call aug.unit.save(pid+PLAYER_HERO, udg_mammoth_tank_hero)
loop
set card = 1
set heroSpecificTalentsThisTier = 0
loop
//Last card or too few remaining hero specific talents
if card == CARD_MAX or heroSpecificTalentsThisTier >= heroSpecificTalentsPerTier or heroSpecificTalents >= MT_TALENTS then
//Go for generic
set genericTalentBits = SetupGenericTalent(pid, tier, card, genericTalentBits)
else
//Go for hero-specific
set heroSpecificTalents = heroSpecificTalents + 1
set heroSpecificTalentsThisTier = heroSpecificTalentsThisTier + 1
set roll = rollTalent(MT_TALENTS, heroTalentBits)
set heroTalentBits = BlzBitOr(heroTalentBits, roll)
if roll == 1 then
call MTAutoEnergizingTuskMissiles(pid, tier, card)
elseif roll == 2 then
call MTV2TuskTargeting(pid, tier, card)
elseif roll == 4 then
call MTMotherlandsGrace(pid, tier, card)
elseif roll == 8 then
call MTHighExplosiveTuskMissiles(pid, tier, card)
elseif roll == 16 then
call MTV2KrakMissile(pid, tier, card)
endif
endif
set card = card + 1
exitwhen card > CARD_MAX
endloop
set tier = tier + 1
exitwhen tier > TIER_MAX
endloop
endfunction
function SetupMCTalents takes player p returns nothing
local integer genericTalentBits = 0
local integer heroTalentBits = 0
local integer heroSpecificTalents = 0
local integer heroSpecificTalentsPerTier = 1
local integer heroSpecificTalentsThisTier
local integer roll
local integer tier = 0
local integer card
local integer pid = GetPlayerId(p)
call aug.unit.save(pid+PLAYER_HERO, udg_mc_hero)
loop
set card = 1
set heroSpecificTalentsThisTier = 0
loop
//Last card or too few remaining hero specific talents
if card == CARD_MAX or heroSpecificTalentsThisTier >= heroSpecificTalentsPerTier or heroSpecificTalents >= MC_TALENTS then
//Go for generic
set genericTalentBits = SetupGenericTalent(pid, tier, card, genericTalentBits)
else
//Go for hero-specific
set heroSpecificTalents = heroSpecificTalents + 1
set heroSpecificTalentsThisTier = heroSpecificTalentsThisTier + 1
set roll = rollTalent(MC_TALENTS, heroTalentBits)
set heroTalentBits = BlzBitOr(heroTalentBits, roll)
if roll == 1 then
call MCFreshWeapon(pid, tier, card)
elseif roll == 2 then
call MCGrenadeAvoidence(pid, tier, card)
elseif roll == 4 then
call MCActiveTargeting(pid, tier, card)
endif
endif
//call BJDebugMsg("SetupMCTalents: tier=" + I2S(tier) + ", card=" + I2S(card) + ", gt-bits=" + I2S(genericTalentBits) +", specific" + I2S(heroTalentBits))
set card = card + 1
exitwhen card > CARD_MAX
endloop
set tier = tier + 1
exitwhen tier > TIER_MAX
endloop
endfunction
function SetupHHTalents takes player p returns nothing
local integer genericTalentBits = 0
local integer heroTalentBits = 0
local integer heroSpecificTalents = 0
local integer heroSpecificTalentsPerTier = 1
local integer heroSpecificTalentsThisTier
local integer roll
local integer tier = 0
local integer card
local integer pid = GetPlayerId(p)
call aug.unit.save(pid+PLAYER_HERO, udg_hh_hero)
loop
set card = 1
set heroSpecificTalentsThisTier = 0
loop
//Last card or too few remaining hero specific talents
if card == CARD_MAX or heroSpecificTalentsThisTier >= heroSpecificTalentsPerTier or heroSpecificTalents >= HH_TALENTS then
//Go for generic
set genericTalentBits = SetupGenericTalent(pid, tier, card, genericTalentBits)
else
//Go for hero-specific
set heroSpecificTalents = heroSpecificTalents + 1
set heroSpecificTalentsThisTier = heroSpecificTalentsThisTier + 1
set roll = rollTalent(HH_TALENTS, heroTalentBits)
set heroTalentBits = BlzBitOr(heroTalentBits, roll)
if roll == 1 then
call HHTrippleShadow(pid, tier, card)
elseif roll == 2 then
call HHDoubleStrikeShuriken(pid, tier, card)
elseif roll == 4 then
call HHShadowFocus(pid, tier, card)
endif
endif
set card = card + 1
//call BJDebugMsg("SetupHHTalents: tier=" + I2S(tier) + ", card=" + I2S(card) + ", gt-bits=" + I2S(genericTalentBits) +", specific" + I2S(heroTalentBits))
exitwhen card > CARD_MAX
endloop
set tier = tier + 1
exitwhen tier > TIER_MAX
endloop
endfunction
function SetupTSTalents takes player p returns nothing
local integer genericTalentBits = 0
local integer heroTalentBits = 0
local integer heroSpecificTalents = 0
local integer heroSpecificTalentsPerTier = 1
local integer heroSpecificTalentsThisTier
local integer roll
local integer tier = 0
local integer card
local integer pid = GetPlayerId(p)
call aug.unit.save(pid+PLAYER_HERO, udg_ts_hero)
loop
set card = 1
set heroSpecificTalentsThisTier = 0
loop
//Last card or too few remaining hero specific talents
if card == CARD_MAX or heroSpecificTalentsThisTier >= heroSpecificTalentsPerTier or heroSpecificTalents >= TS_TALENTS then
//Go for generic
set genericTalentBits = SetupGenericTalent(pid, tier, card, genericTalentBits)
else
//Go for hero-specific
set heroSpecificTalents = heroSpecificTalents + 1
set heroSpecificTalentsThisTier = heroSpecificTalentsThisTier + 1
set roll = rollTalent(TS_TALENTS, heroTalentBits)
set heroTalentBits = BlzBitOr(heroTalentBits, roll)
if roll == 1 then
call TSStormIntensity(pid, tier, card)
elseif roll == 2 then
call TSStormDuration(pid, tier, card)
elseif roll == 4 then
call TSKhalaSurge(pid, tier, card)
elseif roll == 8 then
call TSAssaultSwarm(pid, tier, card)
elseif roll == 16 then
call TSAssistanceOfAdun(pid, tier, card)
endif
endif
//call BJDebugMsg("SetupTSTalents: tier=" + I2S(tier) + ", card=" + I2S(card) + ", gt-bits=" + I2S(genericTalentBits) +", specific" + I2S(heroTalentBits))
set card = card + 1
exitwhen card > CARD_MAX
endloop
set tier = tier + 1
exitwhen tier > TIER_MAX
endloop
endfunction
function SetupALTalents takes player p returns nothing
local integer genericTalentBits = 0
local integer heroTalentBits = 0
local integer heroSpecificTalents = 0
local integer heroSpecificTalentsPerTier = 1
local integer heroSpecificTalentsThisTier
local integer roll
local integer tier = 0
local integer card
local integer pid = GetPlayerId(p)
call aug.unit.save(pid+PLAYER_HERO, udg_al_hero)
loop
set card = 1
set heroSpecificTalentsThisTier = 0
loop
//Last card or too few remaining hero specific talents
if card == CARD_MAX or heroSpecificTalentsThisTier >= heroSpecificTalentsPerTier or heroSpecificTalents >= AL_TALENTS then
//Go for generic
set genericTalentBits = SetupGenericTalent(pid, tier, card, genericTalentBits)
else
//Go for hero-specific
set heroSpecificTalents = heroSpecificTalents + 1
set heroSpecificTalentsThisTier = heroSpecificTalentsThisTier + 1
set roll = rollTalent(AL_TALENTS, heroTalentBits)
set heroTalentBits = BlzBitOr(heroTalentBits, roll)
if roll == 1 then
call ALBranniganLeadership(pid, tier, card)
elseif roll == 2 then
call ALCommander(pid, tier, card)
elseif roll == 4 then
call ALEliteSoldiers(pid, tier, card)
elseif roll == 8 then
call ALEliteCaptain(pid, tier, card)
elseif roll == 16 then
call ALExecutingSlash(pid, tier, card)
endif
endif
set card = card + 1
exitwhen card > CARD_MAX
endloop
set tier = tier + 1
exitwhen tier > TIER_MAX
endloop
endfunction
private function init takes nothing returns nothing
endfunction
endlibrary
globals
Table aug
trigger card_onclick
integer currentTier
timer cards_visible_timer
boolean showCardsIfOpenedForLocalPlayer //Async!
constant integer PLAYER_MAX = 6 // 0 to 5, EXLUDE PlayerId 6!
constant integer PLAYER_TALENTS = 50
constant integer PLAYER_TIER_TO_USE = 51
constant integer PLAYER_MAX_TIER_ALLOWED = 52 //
constant integer PLAYER_SHOW_CARDS = 53
constant integer PLAYER_HERO = 75
constant integer CARD_FRAME_OFFSET = 1000
constant integer CARD_NUMBER = 100
constant integer CARD_TRIGGER = 101
constant integer CARD_TITLE_STRING = 10
constant integer CARD_TITLE_FRAMEH = CARD_TITLE_STRING + 1
constant integer CARD_TEXT_STRING = CARD_TITLE_STRING + 2
constant integer CARD_TEXT_FRAMEH = CARD_TITLE_STRING + 3
constant real CARD_WIDTH = 0.17
constant real CARD_HEIGHT = 0.25
constant real CARD_BTN_Y = 0.035
constant real CARD_TITLE_Y = 0.016
constant real CARD_INSET = 0.0195
constant real CARD_X_PADDING = 0.010
constant real CARD_FIRXT_X = 0.4 - 1.5 * CARD_WIDTH - 1. * CARD_X_PADDING
constant real CARD_CENTER_Y = 0.35
constant integer CARD_MAX = 4
constant integer TIER_MAX = 3 //1-indexed, inclusive, so [1,2,3]
endglobals
library TalentCardLib initializer init requires Table, DTCCustomStatFrames
function setCardVisibleForPlayer takes integer pid, boolean visible returns nothing
local integer card = 1
local Table playerSubtable = aug[pid+1]
loop
call BlzFrameSetVisible(playerSubtable.framehandle[CARD_FRAME_OFFSET + card], visible)
set card = card + 1
exitwhen card > CARD_MAX
endloop
endfunction
function updateOpenedCards takes nothing returns nothing
local integer playersRemaining = 0
local integer pid = GetPlayerId(GetLocalPlayer())
local boolean visible = showCardsIfOpenedForLocalPlayer and UnitAlive(aug.unit[pid+PLAYER_HERO]) and aug[pid+1].integer[PLAYER_TIER_TO_USE] <= aug[pid+1].integer[PLAYER_MAX_TIER_ALLOWED] and not isTooltipVisibleForLocalPlayer()
call setCardVisibleForPlayer(pid, visible)
set pid = 0
loop
exitwhen pid > PLAYER_MAX
if not IsPlayerInForce(Player(pid), udg_current_human_players) then
call aug[pid+1].integer.save(PLAYER_TIER_TO_USE, 9999)
elseif aug[pid+1].integer[PLAYER_TIER_TO_USE] <= aug[pid+1].integer[PLAYER_MAX_TIER_ALLOWED] then
set playersRemaining = playersRemaining + 1
endif
set pid = pid + 1
endloop
if playersRemaining == 0 then
call PauseTimer(cards_visible_timer)
endif
//call BJDebugMsg("selecting cards...")
endfunction
function setCardInfo takes integer pid, integer tier, integer card, string title, string text, trigger onClickEffect returns nothing
local Table cardSubtable = aug[pid+1].link(tier).link(card)
call cardSubtable.trigger.save(CARD_TRIGGER, onClickEffect)
call cardSubtable.string.save(CARD_TITLE_STRING, title)
call cardSubtable.string.save(CARD_TEXT_STRING, text)
endfunction
function updateCardInfo takes integer pid, integer tier returns nothing
local integer card = 1
local Table playerSubtable = aug[pid+1]
local Table cardSubtable
loop
set cardSubtable = playerSubtable[card]
call BlzFrameSetText(cardSubtable.framehandle[CARD_TITLE_FRAMEH], playerSubtable[tier][card].string[CARD_TITLE_STRING])
call BlzFrameSetText(cardSubtable.framehandle[CARD_TEXT_FRAMEH], playerSubtable[tier][card].string[CARD_TEXT_STRING])
set card = card + 1
exitwhen card > CARD_MAX
endloop
endfunction
function setTierForPlayer takes integer pid, integer tier returns nothing
call aug[pid+1].integer.save(PLAYER_TIER_TO_USE, tier)
call aug[pid+1].integer.save(PLAYER_MAX_TIER_ALLOWED, tier)
endfunction
function addPlayerCurrentTier takes integer pid returns nothing
call aug[pid+1].integer.save(PLAYER_TIER_TO_USE, aug[pid+1].integer[PLAYER_TIER_TO_USE] + 1)
endfunction
function addPlayerMaxTier takes integer pid returns nothing
call aug[pid+1].integer.save(PLAYER_MAX_TIER_ALLOWED, aug[pid+1].integer[PLAYER_MAX_TIER_ALLOWED] + 1)
endfunction
function printTalentInfo takes integer pid returns nothing
call BJDebugMsg("Card values: " + I2S(aug[pid+1].integer[PLAYER_TIER_TO_USE]) + ", max=" + I2S(aug[pid+1].integer[PLAYER_MAX_TIER_ALLOWED]))
endfunction
function startCardPicking takes nothing returns nothing
local integer pid = 0
local boolean canPick = false
set showCardsIfOpenedForLocalPlayer = true
loop
exitwhen pid > PLAYER_MAX
if IsPlayerInForce(Player(pid), udg_current_human_players) and aug[pid+1].integer[PLAYER_TIER_TO_USE] <= aug[pid+1].integer[PLAYER_MAX_TIER_ALLOWED] then
set canPick = true
call updateCardInfo(pid, aug[pid+1].integer[PLAYER_TIER_TO_USE])
endif
set pid = pid + 1
endloop
if canPick then
//call BJDebugMsg("start picking")
call StartSound(gg_snd_Hint)
call updateOpenedCards()
call TimerStart(cards_visible_timer, 0.35, true, function updateOpenedCards)
endif
endfunction
function giveAllPlayersOneAugment takes nothing returns nothing
local integer pid = 0
loop
exitwhen pid > PLAYER_MAX
call aug[pid+1].integer.save(PLAYER_MAX_TIER_ALLOWED, aug[pid+1].integer[PLAYER_MAX_TIER_ALLOWED] + 1)
set pid = pid + 1
endloop
endfunction
function giveAllPlayersAugments takes integer number returns nothing
local integer i = 0
loop
exitwhen i >= number
call giveAllPlayersOneAugment()
set i = i + 1
endloop
endfunction
function cardOnClickFunction takes nothing returns nothing
local framehandle f = BlzGetTriggerFrame()
local integer pid = GetPlayerId(GetTriggerPlayer())
local Table playerSubtable = aug[pid+1]
local integer cardNumber = playerSubtable.integer[GetHandleId(f)]
local integer pointsSpent = playerSubtable.integer[PLAYER_TIER_TO_USE]
call BlzFrameSetEnable(f, false)
call BlzFrameSetEnable(f, true)
call setCardVisibleForPlayer(pid, false)
set f = null
set udg_g_unit1 = aug.unit[pid+PLAYER_HERO]
call TriggerEvaluate(playerSubtable[pointsSpent][cardNumber].trigger[CARD_TRIGGER])
call playerSubtable.integer.save(PLAYER_TIER_TO_USE, pointsSpent + 1)
//call BJDebugMsg("Card selected for: " + I2S(pid))
if (playerSubtable.integer[PLAYER_TIER_TO_USE] <= playerSubtable.integer[PLAYER_MAX_TIER_ALLOWED]) then
call updateCardInfo(pid, playerSubtable.integer[PLAYER_TIER_TO_USE])
//call showCardsForPlayer(pid) //this is to handle "loading" using codes. Show next card until "max tier" is reached
elseif GetLocalPlayer() == Player(pid) then
set showCardsIfOpenedForLocalPlayer = false
endif
//call BJDebugMsg("Click: " + I2S(pid) + ", " + I2S(cardNumber) + ", fhid=" + I2S(GetHandleId(f)))
endfunction
function createCard takes integer cardNumber, Table playerSubtable returns framehandle
local framehandle cardBackground = BlzCreateFrame("EscMenuBackdrop", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0),0,0)
local framehandle cardButton = BlzCreateFrame("ScriptDialogButton", cardBackground,0,0)
local framehandle cardTitle = BlzCreateFrameByType("TEXT", "cardTitle", cardBackground, "", 0)
local framehandle cardText = BlzCreateFrameByType("TEXT", "cardTitle", cardBackground, "", 0)
local integer btnHandleId = GetHandleId(cardButton)
local Table cardSubtable = playerSubtable.link(cardNumber)
call playerSubtable.integer.save(btnHandleId, cardNumber)
//call cardSubtable.save(CARD_NUMBER, cardNumber)
call cardSubtable.framehandle.save(CARD_TITLE_FRAMEH, cardTitle)
call cardSubtable.framehandle.save(CARD_TEXT_FRAMEH, cardText)
//Main Card
call BlzFrameSetAbsPoint(cardBackground, FRAMEPOINT_CENTER, CARD_FIRXT_X + (cardNumber - 1.) * CARD_WIDTH + (cardNumber - 1.5) * CARD_X_PADDING, CARD_CENTER_Y)
call BlzFrameSetSize(cardBackground, CARD_WIDTH, CARD_HEIGHT)
//Button
call BlzFrameSetPoint(cardButton, FRAMEPOINT_BOTTOMLEFT, cardBackground, FRAMEPOINT_BOTTOMLEFT, CARD_INSET, CARD_INSET)
call BlzFrameSetPoint(cardButton, FRAMEPOINT_TOPRIGHT, cardBackground, FRAMEPOINT_BOTTOMRIGHT, -CARD_INSET, CARD_INSET + CARD_BTN_Y)
call BlzFrameSetText(cardButton, "|cffFCD20DSelect|r")
call BlzTriggerRegisterFrameEvent(card_onclick, cardButton, FRAMEEVENT_CONTROL_CLICK)
//Title
call BlzFrameSetPoint(cardTitle, FRAMEPOINT_BOTTOMLEFT, cardBackground, FRAMEPOINT_TOPLEFT, CARD_INSET, -CARD_INSET - CARD_TITLE_Y)
call BlzFrameSetPoint(cardTitle, FRAMEPOINT_TOPRIGHT, cardBackground, FRAMEPOINT_TOPRIGHT, -CARD_INSET, -CARD_INSET)
call BlzFrameSetTextAlignment(cardTitle, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
call BlzFrameSetText(cardTitle, "|cffFCD20DTest Title of Testing, Yes|r")
call BlzFrameSetScale(cardTitle, 1.2)
//Text
call BlzFrameSetPoint(cardText, FRAMEPOINT_BOTTOMLEFT, cardButton, FRAMEPOINT_TOPLEFT, 0., -CARD_INSET)
call BlzFrameSetPoint(cardText, FRAMEPOINT_TOPRIGHT, cardTitle, FRAMEPOINT_BOTTOMRIGHT, 0., 0.)
call BlzFrameSetTextAlignment(cardText, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
call BlzFrameSetText(cardText, "|cffFCD20DTest Text of Testing, Yes. Now with so much text that it spans multiple lines hopefully. I'd be great if it wasn't all on a single line, right? ... Right?|r")
call BlzFrameSetScale(cardText, 0.98)
set cardButton = null
set cardTitle = null
set cardText = null
return cardBackground
endfunction
private function setupPlayerTalentCards takes integer pid returns nothing
local integer card = 1
local Table playerSubtable = aug.link(pid+1)
local framehandle f = null
call playerSubtable.integer.save(PLAYER_TIER_TO_USE, 1) //First tier to use is 1
call playerSubtable.integer.save(PLAYER_MAX_TIER_ALLOWED, 0)
loop
set f = createCard(card, playerSubtable)
call BlzFrameSetVisible(f, false)
call playerSubtable.framehandle.save(CARD_FRAME_OFFSET + card, f)
set card = card + 1
exitwhen card > CARD_MAX
endloop
set f = null
endfunction
private function init takes nothing returns nothing
local real xBase
local real yBase
local integer i = 0
local integer aug_i = 0
local integer trig_i = 0
set aug = Table.create()
set card_onclick = CreateTrigger()
set cards_visible_timer = CreateTimer()
set showCardsIfOpenedForLocalPlayer = true
call TriggerAddAction(card_onclick, function cardOnClickFunction)
loop
exitwhen i > PLAYER_MAX
call setupPlayerTalentCards(i)
set i = i + 1
endloop
endfunction
endlibrary
function Trig_ChatGenerateSaveCode_Actions takes nothing returns nothing
if udg_last_market_wave > 3 then
call BJDebugMsg("-load " + intToBase64(udg_last_market_wave, 1) + intToBase64(udg_last_market_tot_gold, 4) + intToBase64(udg_last_market_exp, 3))
else
call BJDebugMsg("You must've been to the market at least once to save!")
endif
endfunction
//===========================================================================
function InitTrig_ChatGenerateSaveCode takes nothing returns nothing
set gg_trg_ChatGenerateSaveCode = CreateTrigger( )
call TriggerRegisterPlayerChatEvent( gg_trg_ChatGenerateSaveCode, Player(0), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_ChatGenerateSaveCode, Player(1), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_ChatGenerateSaveCode, Player(2), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_ChatGenerateSaveCode, Player(3), "-save", true )
call TriggerAddAction( gg_trg_ChatGenerateSaveCode, function Trig_ChatGenerateSaveCode_Actions )
endfunction
library ExtendableBonusSystem requires Table
/* ----------------------- ExtendableBonusSystem v0.1.1 by ThompZon ----------------------- */
//! novjass
Inspired by chopinski New Bonus system, I wanted a system that you can add your own bonuses
using a simple interface. Hopefully the interface will be simple so it is easy to implement
but also this means that the core can upgrade without impacting the extensions!
Inspired by: chopinski New Bonus system
https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/
Requires: Table
https://www.hiveworkshop.com/threads/lua-vjass-new-table.188084/
jassApi
//! endnovjass
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
globals
//This stores ability-backed bonuses for heroes and
Table bonusTable
endglobals
public interface ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean defaults false
method Get takes unit u returns real
method Set takes unit u, real value returns nothing
endinterface
//Simple bonuses that just want to store a value can be stored here!
struct TableBonus extends ExtendableBonusPlugin
method Set takes unit u, real value returns nothing
call bonusTable.link(GetUnitUserData(u)).real.save(getType(), value)
endmethod
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if bonusTable.has(index) then
return bonusTable[index].real[getType()]
endif
return 0.0
endmethod
endstruct
struct RealAbilityBonus extends ExtendableBonusPlugin
stub method AbilityCode takes nothing returns integer
call BJDebugMsg("RealAbilityBonus missing AbilityCode!")
return -1
endmethod
stub method Field takes nothing returns abilityreallevelfield
call BJDebugMsg("RealAbilityBonus missing Field!")
return null
endmethod
method Set takes unit u, real value returns nothing
local integer abilCode = AbilityCode()
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
if IsUnitType(u, UNIT_TYPE_HERO) then
call bonusTable.link(GetUnitUserData(u)).real.save(getType(), value)
endif
endif
if IsUnitType(u, UNIT_TYPE_HERO) then
call bonusTable[GetUnitUserData(u)].real.save(getType(), value)
call BlzSetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, value)
else
call BlzSetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, value)
endif
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
endmethod
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if IsUnitType(u, UNIT_TYPE_HERO) then
if bonusTable.has(index) then
return bonusTable[index].real[getType()]
endif
return 0.0
endif
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, AbilityCode()), Field(), 0)
endmethod
endstruct
struct IntegerAbilityBonus extends ExtendableBonusPlugin
stub method AbilityCode takes nothing returns integer
call BJDebugMsg("IntegerAbilityBonus missing AbilityCode!")
return -1
endmethod
stub method Field takes nothing returns abilityintegerlevelfield
call BJDebugMsg("IntegerAbilityBonus missing Field!")
return null
endmethod
method Set takes unit u, real value returns nothing
local integer abilCode = AbilityCode()
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
if IsUnitType(u, UNIT_TYPE_HERO) then
call bonusTable.link(GetUnitUserData(u)).integer.save(getType(), R2I(value))
endif
endif
if IsUnitType(u, UNIT_TYPE_HERO) then
call bonusTable[GetUnitUserData(u)].integer.save(getType(), R2I(value))
call BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, R2I(value))
else
call BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, R2I(value))
endif
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
endmethod
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if IsUnitType(u, UNIT_TYPE_HERO) then
if bonusTable.has(index) then
return I2R(bonusTable[index].integer[getType()])
endif
return 0.0
endif
return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, AbilityCode()), Field(), 0))
endmethod
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
endstruct
struct ExtendableBonus
private static ExtendableBonusPlugin array registeredBonuses
//private static integer maxTypeId = -1
static method Register takes ExtendableBonusPlugin bonus returns integer
set registeredBonuses[bonus.getType()] = bonus
//set maxTypeId = IMaxBJ(maxTypeId, bonus.getType())
//call BJDebugMsg("registedTypeId=" + I2S(bonus.getType()) + ", bonus=" + I2S(bonus))
return bonus.getType()
endmethod
static method GetBonus takes integer typeId returns ExtendableBonusPlugin
return registeredBonuses[typeId]
endmethod
static method Get takes unit u, integer typeId returns real
if (registeredBonuses[typeId] == 0) then
call BJDebugMsg("type is not registered! id=" + I2S(typeId))
return -1.0
else
return registeredBonuses[typeId].Get(u)
endif
endmethod
static method Set takes unit u, integer typeId, real value returns nothing
if (registeredBonuses[typeId] == 0) then
call BJDebugMsg("type is not registered! id=" + I2S(typeId))
else
//call BJDebugMsg("Setting type=" + I2S(typeId) + " to " + R2S(value))
call registeredBonuses[typeId].Set(u, value)
endif
endmethod
static method Add takes unit u, integer typeId, real value returns nothing
local ExtendableBonusPlugin currentBonus = GetBonus(typeId)
local real addedValue
if currentBonus.IsIntegerBonus() then
set addedValue = R2I(value)
else
set addedValue = value
endif
call Set(u, typeId, currentBonus.Get(u) + addedValue)
endmethod
static method onInit takes nothing returns nothing
//You are not supposed to do this, but I want to allow struct on-init to use the BonusTable to set themselves up (it's easier than having 1 "centralized")
set bonusTable = Table.create()
endmethod
endstruct
function GetUnitBonus takes unit u, integer typeId returns real
return ExtendableBonus.Get(u, typeId)
endfunction
function SetUnitBonus takes unit u, integer typeId, real value returns nothing
call ExtendableBonus.Set(u, typeId, value)
endfunction
function AddUnitBonus takes unit u, integer typeId, real value returns nothing
call ExtendableBonus.Add(u, typeId, value)
endfunction
function RemoveUnitBonus takes unit u, integer typeId returns nothing
call ExtendableBonus.Set(u, typeId, 0)
endfunction
function PurgeAllUnitBonus takes unit u returns nothing
endfunction
endlibrary
library ExtendableBonusesDtc requires ExtendableBonusSystem, CooldownReduction, Table
/* ----------------------- ExtendableBonusSystemBasics v0.1.1 by ThompZon ----------------------- */
//! novjass
Inspired by chopinski New Bonus system, I wanted a system that you can add your own bonuses
using a simple interface. Hopefully the interface will be simple so it is easy to implement
but also this means that the core can upgrade without impacting the extensions!
Inspired by: chopinski New Bonus system
https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/
jassApi
//! endnovjass
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
globals
//These are not required by the system, but they were the way to interact with NewBonus by chopinski
//One could use these or use "BonusDamage.typeid". Note that you must set these "manually" in "onInit" function
integer BONUS_SPELL_POWER
integer BONUS_SPELL_POWER_MULTIPLIER
integer BONUS_SPELL_HASTE //% more casts. 100 of this = 50% BONUS_COOLDOWN_REDUCTION_FLAT
integer BONUS_CRITICAL_CHANCE
integer BONUS_CRITICAL_DAMAGE
integer BONUS_ARMOR_PEN_FLAT
integer BONUS_ARMOR_PEN_PERCENT
//Table bonusTable = 0
endglobals
struct BonusSpellPower extends ExtendableBonusSystem_ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
method Get takes unit u returns real
return I2R(udg_spell_power[GetUnitUserData(u)])
endmethod
method Set takes unit u, real value returns nothing
set udg_spell_power[GetUnitUserData(u)] = R2I(value)
endmethod
static method onInit takes nothing returns nothing
set BONUS_SPELL_POWER = ExtendableBonus.Register(BonusSpellPower.allocate())
endmethod
endstruct
struct BonusSpellPowerMultiplier extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return udg_spell_power_multiplier[GetUnitUserData(u)]
endmethod
method Set takes unit u, real value returns nothing
set udg_spell_power_multiplier[GetUnitUserData(u)] = value
endmethod
static method onInit takes nothing returns nothing
set BONUS_SPELL_POWER_MULTIPLIER = ExtendableBonus.Register(BonusSpellPowerMultiplier.allocate())
endmethod
endstruct
struct BonusSpellHaste extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return 100. * (1. / (1. - GetUnitCooldownReductionFlat(u)) - 1.0)
endmethod
method Set takes unit u, real value returns nothing
call SetUnitCooldownReductionFlat(u, 1. - 1. / (1. + value / 100.))
endmethod
static method onInit takes nothing returns nothing
set BONUS_SPELL_HASTE = ExtendableBonus.Register(BonusSpellHaste.allocate())
endmethod
endstruct
//---- Crit ----
struct BonusCriticalChance extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return udg_crit_chance[GetUnitUserData(u)]
endmethod
method Set takes unit u, real value returns nothing
set udg_crit_chance[GetUnitUserData(u)] = value
endmethod
static method onInit takes nothing returns nothing
set BONUS_CRITICAL_CHANCE = ExtendableBonus.Register(BonusCriticalChance.allocate())
endmethod
endstruct
struct BonusCriticalDamage extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return udg_crit_damage[GetUnitUserData(u)]
endmethod
method Set takes unit u, real value returns nothing
set udg_crit_damage[GetUnitUserData(u)] = value
endmethod
static method onInit takes nothing returns nothing
set BONUS_CRITICAL_DAMAGE = ExtendableBonus.Register(BonusCriticalDamage.allocate())
endmethod
endstruct
// ---- Armor Penetration ----
struct BonusArmorPenetrationFlat extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return udg_armor_penetration_flat[GetUnitUserData(u)]
endmethod
method Set takes unit u, real value returns nothing
set udg_armor_penetration_flat[GetUnitUserData(u)] = value
endmethod
static method onInit takes nothing returns nothing
set BONUS_ARMOR_PEN_FLAT = ExtendableBonus.Register(BonusArmorPenetrationFlat.allocate())
endmethod
endstruct
struct BonusArmorPenetrationPercent extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return udg_armor_penetration_percent[GetUnitUserData(u)]
endmethod
method Set takes unit u, real value returns nothing
set udg_armor_penetration_percent[GetUnitUserData(u)] = value
endmethod
static method onInit takes nothing returns nothing
set BONUS_ARMOR_PEN_PERCENT = ExtendableBonus.Register(BonusArmorPenetrationPercent.allocate())
endmethod
endstruct
// ---- on kill stone drops
struct BonusHealthStone extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return udg_on_kill_health_stone[GetConvertedPlayerId(GetOwningPlayer(u))]
endmethod
method Set takes unit u, real value returns nothing
set udg_on_kill_health_stone[GetConvertedPlayerId(GetOwningPlayer(u))] = value
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(BonusHealthStone.allocate())
endmethod
endstruct
struct BonusManaStone extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return udg_on_kill_mana_stone[GetConvertedPlayerId(GetOwningPlayer(u))]
endmethod
method Set takes unit u, real value returns nothing
set udg_on_kill_mana_stone[GetConvertedPlayerId(GetOwningPlayer(u))] = value
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(BonusManaStone.allocate())
endmethod
endstruct
// ---- life/mana on-kill
struct BonusLifeOnKillFlat extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if bonusTable.has(index) then
return bonusTable[index].real[thistype.typeid]
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
call bonusTable.link(GetUnitUserData(u)).real.save(thistype.typeid, value)
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusLifeOnKillMissing extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if bonusTable.has(index) then
return bonusTable[index].real[thistype.typeid]
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
call bonusTable.link(GetUnitUserData(u)).real.save(thistype.typeid, value)
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusManaOnKillFlat extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if bonusTable.has(index) then
return bonusTable[index].real[thistype.typeid]
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
call bonusTable.link(GetUnitUserData(u)).real.save(thistype.typeid, value)
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusManaOnKillMissing extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if bonusTable.has(index) then
return bonusTable[index].real[thistype.typeid]
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
call bonusTable.link(GetUnitUserData(u)).real.save(thistype.typeid, value)
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusShieldOnWaveStartPercentMaxHp extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if bonusTable.has(index) then
return bonusTable[index].real[thistype.typeid]
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
call bonusTable.link(GetUnitUserData(u)).real.save(thistype.typeid, value)
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusShieldOnWaveStartPercentSpellPower extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if bonusTable.has(index) then
return bonusTable[index].real[thistype.typeid]
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
call bonusTable.link(GetUnitUserData(u)).real.save(thistype.typeid, value)
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusShieldOnWaveStartPercentDamage extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
local integer index = GetUnitUserData(u)
if bonusTable.has(index) then
return bonusTable[index].real[thistype.typeid]
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
call bonusTable.link(GetUnitUserData(u)).real.save(thistype.typeid, value)
endmethod
private static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
endlibrary
library ExtendableBonusesBasicBonuses requires ExtendableBonusSystem
/* ----------------------- ExtendableBonusSystemBasics v0.1.1 by ThompZon ----------------------- */
//! novjass
Inspired by chopinski New Bonus system, I wanted a system that you can add your own bonuses
using a simple interface. Hopefully the interface will be simple so it is easy to implement
but also this means that the core can upgrade without impacting the extensions!
Inspired by: chopinski New Bonus system
https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/
jassApi
//! endnovjass
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
globals
//The abilities codes for each bonus
//When pasting the abilities over to your map
//their raw code should match the bonus here
private constant integer DAMAGE_ABILITY = 'Z001'
private constant integer ARMOR_ABILITY = 'Z002'
private constant integer STATS_ABILITY = 'Z003'
private constant integer HEALTHREGEN_ABILITY = 'Z006'
private constant integer MANAREGEN_ABILITY = 'Z007'
private constant integer ATTACKSPEED_ABILITY = 'Z008'
private constant integer MOVEMENTSPEED_ABILITY = 'Z009'
private constant integer SIGHT_RANGE_ABILITY = 'Z00A'
//The abilities fields that are modified. For the sake of readability
private constant abilityintegerlevelfield DAMAGE_FIELD = ABILITY_ILF_ATTACK_BONUS
private constant abilityintegerlevelfield ARMOR_FIELD = ABILITY_ILF_DEFENSE_BONUS_IDEF
private constant abilityintegerlevelfield AGILITY_FIELD = ABILITY_ILF_AGILITY_BONUS
private constant abilityintegerlevelfield STRENGTH_FIELD = ABILITY_ILF_STRENGTH_BONUS_ISTR
private constant abilityintegerlevelfield INTELLIGENCE_FIELD = ABILITY_ILF_INTELLIGENCE_BONUS
private constant abilityintegerlevelfield MOVEMENTSPEED_FIELD = ABILITY_ILF_MOVEMENT_SPEED_BONUS
private constant abilityintegerlevelfield SIGHT_RANGE_FIELD = ABILITY_ILF_SIGHT_RANGE_BONUS
private constant abilityreallevelfield HEALTHREGEN_FIELD = ABILITY_RLF_AMOUNT_OF_HIT_POINTS_REGENERATED
private constant abilityreallevelfield MANAREGEN_FIELD = ABILITY_RLF_AMOUNT_REGENERATED
private constant abilityreallevelfield ATTACKSPEED_FIELD = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1
//These are not required by the system, but they were the way to interact with NewBonus by chopinski
//One could use these or use "BonusDamage.typeid"
integer BONUS_DAMAGE
integer BONUS_ARMOR
integer BONUS_AGILITY
integer BONUS_STRENGTH
integer BONUS_INTELLIGENCE
integer BONUS_HEALTH
integer BONUS_MANA
integer BONUS_MOVEMENT_SPEED
integer BONUS_SIGHT_RANGE
integer BONUS_HEALTH_REGEN
integer BONUS_MANA_REGEN
integer BONUS_ATTACK_SPEED
endglobals
//===== Integer Ability Based =====
struct BonusDamage extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return DAMAGE_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return DAMAGE_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_DAMAGE = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusArmor extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return ARMOR_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return ARMOR_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_ARMOR = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
/*
* Note: One can not use this ability to give negative movement speed bonus! It results in no change of movement speed unless there are positive movement speed already!
*/
struct BonusMovementSpeed extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return MOVEMENTSPEED_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return MOVEMENTSPEED_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_MOVEMENT_SPEED = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusSightRange extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return SIGHT_RANGE_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return SIGHT_RANGE_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_SIGHT_RANGE = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
//Hero stats
struct BonusAgility extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return STATS_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return AGILITY_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_AGILITY = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusStrength extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return STATS_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return STRENGTH_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_STRENGTH = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusIntelligence extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return STATS_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return INTELLIGENCE_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_INTELLIGENCE = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
//===== Real Ability Based =====
struct BonusAttackSpeed extends RealAbilityBonus
method AbilityCode takes nothing returns integer
return ATTACKSPEED_ABILITY
endmethod
method Field takes nothing returns abilityreallevelfield
return ATTACKSPEED_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_ATTACK_SPEED = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusHealthRegen extends RealAbilityBonus
method AbilityCode takes nothing returns integer
return HEALTHREGEN_ABILITY
endmethod
method Field takes nothing returns abilityreallevelfield
return HEALTHREGEN_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_HEALTH_REGEN = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusManaRegen extends RealAbilityBonus
method AbilityCode takes nothing returns integer
return MANAREGEN_ABILITY
endmethod
method Field takes nothing returns abilityreallevelfield
return MANAREGEN_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_MANA_REGEN = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
//===== Custom handled =====
struct BonusHealthPercent extends ExtendableBonusSystem_ExtendableBonusPlugin
// Not fully implemented!!
method Get takes unit u returns real
call BJDebugMsg("NOT FULLY IMPLEMENTED!")
if bonusTable.has(GetUnitUserData(u)) then
return bonusTable[GetUnitUserData(u)].real[thistype.typeid]
endif
return 0.0
endmethod
method GetBonusHealth takes unit u returns real
return 0.0
endmethod
method Set takes unit u, real value returns nothing
local real p = GetUnitLifePercent(u)
local integer amount = R2I(value)
call BJDebugMsg("NOT FULLY IMPLEMENTED!")
call BlzSetUnitMaxHP(u, BlzGetUnitMaxHP(u) + amount - bonusTable[GetUnitUserData(u)].integer[thistype.typeid])
call SetUnitLifePercentBJ(u, p)
endmethod
static method onInit takes nothing returns nothing
call ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusHealth extends ExtendableBonusSystem_ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
method Get takes unit u returns real
if bonusTable.has(GetUnitUserData(u)) then
return I2R(bonusTable[GetUnitUserData(u)].integer[thistype.typeid])
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
local real p = GetUnitLifePercent(u)
local integer amount = R2I(value)
call BlzSetUnitMaxHP(u, BlzGetUnitMaxHP(u) + amount - bonusTable[GetUnitUserData(u)].integer[thistype.typeid])
call SetUnitLifePercentBJ(u, p)
endmethod
static method onInit takes nothing returns nothing
set BONUS_HEALTH = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusMana extends ExtendableBonusSystem_ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
method Get takes unit u returns real
if bonusTable.has(GetUnitUserData(u)) then
return I2R(bonusTable[GetUnitUserData(u)].integer[thistype.typeid])
endif
return 0.0
endmethod
method Set takes unit u, real value returns nothing
local real p = GetUnitManaPercent(u)
local integer amount = R2I(value)
call BlzSetUnitMaxMana(u, BlzGetUnitMaxMana(u) + amount - bonusTable[GetUnitUserData(u)].integer[thistype.typeid])
call SetUnitManaPercentBJ(u, p)
endmethod
static method onInit takes nothing returns nothing
set BONUS_MANA = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
endlibrary
library NewBonusUtils requires ExtendableBonusSystem, RegisterPlayerUnitEvent
/* ------------------- ExtendableBonusUtils v0.1.1 by ThompZon ------------------- */
/* ----------------------- NewBonusUtils v2.2 by Chopinski ----------------------- */
//! novjass
Required Library: RegisterPlayerUnitEvent -> www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/
API:
function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
-> Add the specified amount for the specified bonus type for unit for a duration
-> Example: call AddUnitBonusTimed(GetTriggerUnit(), BONUS_ARMOR, 13, 10.5)
function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
-> Links the bonus amount specified to a buff or ability. As long as the unit has the buff or
-> the ability represented by the parameter buffId the bonus is not removed.
-> Example: call LinkBonusToBuff(GetTriggerUnit(), BONUS_ARMOR, 10, 'B000')
function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
-> Links the bonus amount specified to an item. As long as the unit has that item the bonus is not removed.
-> Note that it will work for items with the same id, because it takes as parameter the item object.
-> Example: call LinkBonusToItem(GetManipulatingUnit(), BONUS_ARMOR, 10, GetManipulatedItem())
function UnitCopyBonuses takes unit source, unit target returns nothing
-> Copy the source unit bonuses using the Add functionality to the target unit
-> Example: call UnitCopyBonuses(GetTriggerUnit(), GetSummonedUnit())
function UnitMirrorBonuses takes unit source, unit target returns nothing
-> Copy the source unit bonuses using the Set functionality to the target unit
-> Example: call UnitMirrorBonuses(GetTriggerUnit(), GetSummonedUnit())
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
private struct NewBonusUtils
static timer timer = CreateTimer()
static integer key = -1
static thistype array array
static integer k = -1
static thistype array items
unit unit
item item
real ticks
integer type
integer buff
real amount
boolean link
method remove takes integer i, boolean isItem returns integer
call AddUnitBonus(unit, type, -amount)
if isItem then
set items[i] = items[k]
set k = k - 1
else
set array[i] = array[key]
set key = key - 1
if key == -1 then
call PauseTimer(timer)
endif
endif
set unit = null
set item = null
call deallocate()
return i - 1
endmethod
static method removeBonusTypeForItem takes item itm, integer bonusToRemove returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > k
set this = items[i]
if item == itm and type == bonusToRemove then
set i = remove(i, true)
endif
set i = i + 1
endloop
endmethod
static method onDrop takes nothing returns nothing
local item itm = GetManipulatedItem()
local integer i = 0
local thistype this
loop
exitwhen i > k
set this = items[i]
if item == itm then
set i = remove(i, true)
endif
set i = i + 1
endloop
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > key
set this = array[i]
if link then
set ticks = ticks - 1
if ticks <= 0 then
set i = remove(i, false)
endif
else
if GetUnitAbilityLevel(unit, buff) == 0 then
set i = remove(i, false)
endif
endif
set i = i + 1
endloop
endmethod
static method linkTimed takes unit u, integer bonus_type, real amount, real duration, boolean link returns nothing
local thistype this = thistype.allocate()
set this.unit = u
set this.type = bonus_type
set this.ticks = duration/0.03125000
set this.link = link
set this.amount = amount
call AddUnitBonus(u, bonus_type, this.amount)
set key = key + 1
set array[key] = this
if key == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
static method linkBuff takes unit u, integer bonus_type, real amount, integer buffId, boolean link returns nothing
local thistype this = thistype.allocate()
set this.unit = u
set this.type = bonus_type
set this.buff = buffId
set this.link = link
set this.amount = amount
call AddUnitBonus(u, bonus_type, this.amount)
set key = key + 1
set array[key] = this
if key == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
static method linkItem takes unit u, integer bonus_type, real amount, item i returns nothing
local thistype this = thistype.allocate()
set this.unit = u
set this.item = i
set this.type = bonus_type
set this.amount = amount
call AddUnitBonus(u, bonus_type, this.amount)
set k = k + 1
set items[k] = this
endmethod
static method copy takes unit source, unit target returns nothing
local integer i = 1
loop
exitwhen i > 17
if GetUnitBonus(source, i) != 0 then
call AddUnitBonus(target, i, GetUnitBonus(source, i))
endif
set i = i + 1
endloop
endmethod
static method mirror takes unit source, unit target returns nothing
local integer i = 1
loop
exitwhen i > 17
call SetUnitBonus(target, i, GetUnitBonus(source, i))
set i = i + 1
endloop
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* --------------------------------------------------------------------------
*/
function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
call NewBonusUtils.linkTimed(u, bonus_type, amount, duration, true)
endfunction
function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
call NewBonusUtils.linkBuff(u, bonus_type, amount, buffId, false)
endfunction
function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
call NewBonusUtils.linkItem(u, bonus_type, amount, i)
endfunction
function RemoveSpecificBonusFromItem takes item i, integer bonus_type returns nothing
call NewBonusUtils.removeBonusTypeForItem(i, bonus_type)
endfunction
function UnitCopyBonuses takes unit source, unit target returns nothing
call NewBonusUtils.copy(source, target)
endfunction
function UnitMirrorBonuses takes unit source, unit target returns nothing
call NewBonusUtils.mirror(source, target)
endfunction
endlibrary
library OnHitSystem requires ExtendableBonusSystem, DamageEngine, DTCUtils
globals
constant real ON_HIT_HAS_HIT_MODIFIER = 0.3
constant integer ON_HIT_LOOP_AT = 60 //3*4*5 - This means that every-X-hit effects with these as components works flawlessly. Add componenets as needed.
endglobals
private interface OnHitEvents
method onHit takes unit source, integer hitCounter returns real defaults 0.0
endinterface
struct OnHit extends OnHitEvents
public OnHit next = 0
private boolean hasFiered = false
private boolean removeAfterProcessing = false
public method ShouldRemove takes nothing returns boolean
return removeAfterProcessing
endmethod
public method FlagForRemoval takes nothing returns nothing
set removeAfterProcessing = true
endmethod
public method HasFiered takes nothing returns boolean
return hasFiered
endmethod
public method ResetHasFiered takes nothing returns nothing
set hasFiered = false
endmethod
public method SetHasFiered takes nothing returns nothing
set hasFiered = true
endmethod
endstruct
struct UnitOnHit
private static thistype array units
unit u
real amount
integer hitCounter
OnHit firstCustom
private real recentHealTotal
private boolean hasAppliedRecently
private texttag text
private method startText takes nothing returns nothing
local real angle = GetRandomReal(0., 2. * bj_PI)
local real x = GetUnitX(u) + 40. * Cos(angle)
local real y = GetUnitY(u) + 40. * Sin(angle)
set text = CreateTextTag()
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, 0.99)
call SetTextTagFadepoint(text, 0.85)
call SetTextTagVelocityBJ(text, GetRandomReal(96, 128), GetRandomReal(64, 96))
call SetTextTagColor(text, 150, 240, 100, 180)
call SetTextTagPos(text, x, y, GetRandomReal(96, 128))
endmethod
private method doHeal takes real totalAmount returns nothing
local real current = GetUnitState(u, UNIT_STATE_LIFE)
local real max = GetUnitState(u, UNIT_STATE_MAX_LIFE)
if totalAmount > 0.5 then
if text == null then
call startText()
endif
if current + totalAmount > max then
set totalAmount = max - current
endif
call SetUnitState(u, UNIT_STATE_LIFE, RMaxBJ(0, current + totalAmount))
set recentHealTotal = recentHealTotal + totalAmount
if recentHealTotal > 1.0 then
if recentHealTotal < 10 then
call SetTextTagText(text, R2SW(recentHealTotal, 0, 1), TextTagSize2Height(6.5))
else
call SetTextTagText(text, R2SW(recentHealTotal, 0, 0), TextTagSize2Height(7.5))
endif
endif
endif
endmethod
private method resetHasFiered takes nothing returns nothing
local OnHit current = .firstCustom
loop
exitwhen current == 0
call current.ResetHasFiered()
set current = current.next
endloop
endmethod
method removeCustom takes integer typeId returns nothing
local OnHit current = .firstCustom
local OnHit prev = .firstCustom
loop
exitwhen current == 0
if current.getType() == typeId then
if current == .firstCustom then
set .firstCustom = current.next
else
set prev.next = current.next
endif
call current.destroy()
set current = 0
else
set prev = current
set current = current.next
endif
endloop
endmethod
method removeFlaggedForRemovalCustom takes nothing returns nothing
local OnHit current = .firstCustom
local OnHit prev = .firstCustom
loop
exitwhen current == 0
if current.ShouldRemove() then
if current == .firstCustom then
set .firstCustom = current.next
else
set prev.next = current.next
endif
call current.destroy()
set current = 0
else
set prev = current
set current = current.next
endif
endloop
endmethod
method hasCustom takes integer typeId returns boolean
local OnHit current = .firstCustom
loop
exitwhen current == 0
if current.getType() == typeId then
return true
endif
set current = current.next
endloop
return false
endmethod
public static method GetInstance takes unit u returns UnitOnHit
return units[GetUnitUserData(u)]
endmethod
private static method resetRecently takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set hasAppliedRecently = false
call resetHasFiered()
set text = null
set recentHealTotal = 0.0
call ReleaseTimer(t)
set t = null
endmethod
private method customOnHitEffects takes nothing returns real
local real sumAmount = 0
local OnHit current = .firstCustom
local boolean shouldRemoveEffect = false
//call BJDebugMsg("sum LoH")
loop
exitwhen current == 0
if current.HasFiered() then
set sumAmount = ON_HIT_HAS_HIT_MODIFIER * current.onHit(u, hitCounter)
else
//call BJDebugMsg("try to fire LoH")
set sumAmount = current.onHit(u, hitCounter)
//call BJDebugMsg("fired LoH")
endif
if current.ShouldRemove() then
set shouldRemoveEffect = true
endif
set current = current.next
endloop
if shouldRemoveEffect then
call removeFlaggedForRemovalCustom()
endif
return sumAmount
endmethod
private static method onHitEffect takes nothing returns nothing
local thistype this = GetInstance(Damage.source)
local real healModifier
if this != 0 then
//call BJDebugMsg("OnHitEarly")
if hasAppliedRecently then
set healModifier = ON_HIT_HAS_HIT_MODIFIER
else
set healModifier = 1
call TimerStart(NewTimerEx(this), 0.5, false, function thistype.resetRecently)
set hasAppliedRecently = true
endif
call doHeal(healModifier * amount + customOnHitEffects())
//call BJDebugMsg("OnHitLate")
set hitCounter = hitCounter + 1
endif
endmethod
public method AddOnHit takes OnHit additional returns nothing
local OnHit current
//call BJDebugMsg("Add LoH")
if .firstCustom == 0 then
set .firstCustom = additional
//call BJDebugMsg("First didn't exist, this is set to first")
else
set current = .firstCustom
loop
exitwhen current.next == 0
set current = current.next
endloop
set current.next = additional
//call BJDebugMsg("First existed, this is set to last")
endif
endmethod
public method AddAmount takes real additional returns nothing
set amount = amount + additional
endmethod
public method SetAmount takes real newTotal returns nothing
set amount = newTotal
endmethod
public method GetAmount takes nothing returns real
return amount
endmethod
public static method Unregister takes unit u returns nothing
local thistype instance = GetInstance(u)
local integer index = GetUnitUserData(u)
local OnHit current = instance.firstCustom
local OnHit next
loop
exitwhen current == 0
set next = current.next
call current.destroy()
set current = next
endloop
set instance.u = null
set units[index] = 0
call instance.deallocate()
endmethod
private static method unregisterOnDeath takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if (not UnitAlive(u)) then
call Unregister(u)
call ReleaseTimer(t)
endif
set t = null
endmethod
public static method Register takes unit u returns UnitOnHit
local thistype this = thistype.allocate()
set .u = u
set .amount = 0
set .hitCounter = 0
set .recentHealTotal = 0
set .hasAppliedRecently = false
set .text = null
set .firstCustom = 0
set units[GetUnitUserData(u)] = this
if not IsUnitType(u, UNIT_TYPE_HERO) then
call TimerStart(NewTimerEx(this), 10.0, true, function thistype.unregisterOnDeath)
endif
return this
endmethod
static method onInit takes nothing returns nothing
//register to damage engine
local DamageTrigger dmgTrigger = RegisterDamageEngineEx(function UnitOnHit.onHitEffect, "Mod", 3.00, DamageEngine_FILTER_ATTACK)
set dmgTrigger.filter = DamageEngine_FILTER_CODE
set dmgTrigger.damageType = GetHandleId(DAMAGE_TYPE_NORMAL)
set dmgTrigger.configured = true
endmethod
static method Get takes unit u returns UnitOnHit
local UnitOnHit instance = UnitOnHit.GetInstance(u)
if instance == 0 then
set instance = UnitOnHit.Register(u)
endif
return instance
endmethod
endstruct
function AddCustomOnHit takes unit u, OnHit loh returns nothing
call UnitOnHit.Get(u).AddOnHit(loh)
endfunction
function AddUniqueOnHitOrDestroy takes unit u, OnHit loh returns nothing
local UnitOnHit instance = UnitOnHit.Get(u)
if instance.hasCustom(loh.getType()) then
call loh.destroy()
else
call instance.AddOnHit(loh)
endif
endfunction
function RemoveCustomOnHit takes unit u, integer lohTypeId returns nothing
call UnitOnHit.GetInstance(u).removeCustom(lohTypeId)
endfunction
//Register to bonus system
struct BonusLifeOnHit extends ExtendableBonusSystem_ExtendableBonusPlugin
method Get takes unit u returns real
return UnitOnHit.GetInstance(u).GetAmount()
endmethod
method Set takes unit u, real value returns nothing
call UnitOnHit.Get(u).SetAmount(value)
endmethod
static method onInit takes nothing returns nothing
local integer a = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
endlibrary
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 6
One map, one hashtable, one Table struct.
Version 6 combines multi-dimensional Tables, TableArray, HandleTable and StringTable into...
...Table.
What's changed? What does the API look like now? Read on to find out:
Universal Table operations:
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| flush and destroy the Table.
|
| NOTE: this method incorporates the former TableArray.flush() method.
|
| method flush takes nothing returns nothing
| Erase all saved values inside of the Table
|
| NOTE: This does not flush all parallel instances in a forked Table.
| It only flushes the current Table instance.
|
| static method createFork takes integer width returns Table
| creates a Table that is a negative integer rather than positive one.
|
| NOTE: formerly, this was `static method operator []` on TableArray.
|
| method switch takes integer offset returns Table
| switches from the current Table to any offset between 0 and 'width - 1'
|
| NOTE: formerly, this was `method operator []` on TableArray.
|
| NOTE: Only works with Tables created as forks.
|
| method measureFork takes nothing returns integer
| returns the number passed to `createFork`.
| formerly, this was `method size` on TableArray.
|
| method getKeys takes nothing returns Table
| returns a readonly Table containing:
| [0] - the number of tracked keys in the Table
| [1->number of keys] - contiguous list of keys found inside of the Table.
| Effectively combines Object.keys(obj) from JavaScript with 1-based arrays of Lua.
|
| -> How to iterate to access values:
|
| local Table keys = myTable.getKeys()
| local integer i = keys[0]
| loop
| exitwhen i == 0
| call BJDebugMsg("My value is: " + I2S(myTable[keys[i]]))
| set i = i - 1
| endloop
|
Standard Table operations (store Tables or integers against integer keys):
| method operator [] takes integer key returns Table
| load the value at index `key`
|
| method operator []= takes integer key, Table value returns nothing
| assign "value" to index `key` without tracking it. Like `rawset` in Lua.
|
| method remove takes integer key returns nothing
| remove the value at index `key`
|
| method has takes integer key returns boolean
| whether or not `key` has been assigned
|
| method save takes integer key, Table value returns Table
| a new method to not just add the value to the Table, but to also track it.
| Returns `this` Table, so `save` calls can be daisy-chained.
|
Multi-Dimensional Table operations (store nested Tables against integer keys):
| method link takes integer key returns Table
| Checks if a Table already exists at the key, and creates one if not.
|
| Note: Formerly, you needed to create a Table2/3/4/5D/T to access this method.
| Note: Can be substituted for `[]` if you are certain that the key is set.
|
| Note: Links are all tracked by default. To avoid the extra tracking behavior,
| you can either use a static Table (`struct.typeid` or `key`) or you can
| use Table.createFork(1) instead of Table.create() or
| myTable.forkLink(1, key) instead of myTable.link(key)
|
| method forkLink takes integer width, integer key returns Table
| Checks if a Fork already exists at the key, and creates one with the chosen size if not.
HandleTable operations (store Tables against handle keys):
| method operator get takes handle key returns Tables
| Alias for `[]`
|
| method operator store takes handle key, Table value returns Table
| Alias for `save`
|
| method forget takes handle key returns nothing
| Alias for `remove`
|
| method stores takes handle key returns boolean
| Alias for `has`
|
| method bind takes handle key returns Table
| Alias for `link`
|
| method forkBind takes handle key returns Table
| Alias for `forkLink`
StringTable operations (store Tables against string keys):
| method operator read takes string key returns Table
| Alias for `[]`
|
| method operator write takes string key, Table value returns Table
| Alias for `save`
|
| method delete takes string key returns nothing
| Alias for `remove`
|
| method written takes string key returns boolean
| Alias for `has`
|
| method join takes string key returns Table
| Alias for `link`
|
| method forkJoin takes string key returns Table
| Alias for `forkLink`
Tables that store non-integer/Table values can access the `standard` Table API just by using .type syntax:
| myTable.unit[5] = GetTriggerUnit()
| myTable.unit.save(10, GetTriggerUnit())
| myTable.unit.store(GetAttacker(), GetTriggerUnit())
| myTable.unit.write("whatever you want", GetTriggerUnit())
|
| myTable.handle.remove(10)
| myTable.handle.forget(GetAttacker())
| myTable.handle.delete("whatever you want")
|
| local string s = myTable.string[15]
| local string t = myTable.string.get(GetSpellTargetUnit())
| local string u = myTable.string.read("something you want")
|
| local boolean b = myTable.string.has(15)
| local boolean c = myTable.string.stores(GetSpellTargetUnit())
| local boolean d = myTable.string.written("something you want")
*/ requires optional Table5BC, optional TableVBC
globals
private integer tableKeyGen = 8190 //Index generation for Tables starts from here. Configure it if your map contains more than this many structs or 'key' objects.
private hashtable hashTable = InitHashtable() // The last hashtable.
private constant boolean TEST = false // set to `true` to enable error messages and `print`/`toString` API.
private constant boolean DEEP_TEST = false // set to `true` to enable informational messages.
private keyword addKey
private keyword addTypedKey
private keyword IntegerModule
private keyword RealModule
private keyword BooleanModule
private keyword StringModule
private keyword PlayerModule
private keyword WidgetModule
private keyword DestructableModule
private keyword ItemModule
private keyword UnitModule
private keyword AbilityModule
private keyword TimerModule
private keyword TriggerModule
private keyword TriggerConditionModule
private keyword TriggerActionModule
private keyword TriggerEventModule
private keyword ForceModule
private keyword GroupModule
private keyword LocationModule
private keyword RectModule
private keyword BooleanExprModule
private keyword SoundModule
private keyword EffectModule
private keyword UnitPoolModule
private keyword ItemPoolModule
private keyword QuestModule
private keyword QuestItemModule
private keyword DefeatConditionModule
private keyword TimerDialogModule
private keyword LeaderboardModule
private keyword MultiboardModule
private keyword MultiboardItemModule
private keyword TrackableModule
private keyword DialogModule
private keyword ButtonModule
private keyword TextTagModule
private keyword LightningModule
private keyword ImageModule
private keyword UbersplatModule
private keyword RegionModule
private keyword FogStateModule
private keyword FogModifierModule
private keyword HashtableModule
private keyword FrameModule
private keyword AgentStruct
private keyword HandleStruct
private keyword IntegerStruct
private keyword RealStruct
private keyword BooleanStruct
private keyword StringStruct
private keyword PlayerStruct
private keyword WidgetStruct
private keyword DestructableStruct
private keyword ItemStruct
private keyword UnitStruct
private keyword AbilityStruct
private keyword TimerStruct
private keyword TriggerStruct
private keyword TriggerConditionStruct
private keyword TriggerActionStruct
private keyword TriggerEventStruct
private keyword ForceStruct
private keyword GroupStruct
private keyword LocationStruct
private keyword RectStruct
private keyword BooleanExprStruct
private keyword SoundStruct
private keyword EffectStruct
private keyword UnitPoolStruct
private keyword ItemPoolStruct
private keyword QuestStruct
private keyword QuestItemStruct
private keyword DefeatConditionStruct
private keyword TimerDialogStruct
private keyword LeaderboardStruct
private keyword MultiboardStruct
private keyword MultiboardItemStruct
private keyword TrackableStruct
private keyword DialogStruct
private keyword ButtonStruct
private keyword TextTagStruct
private keyword LightningStruct
private keyword ImageStruct
private keyword UbersplatStruct
private keyword RegionStruct
private keyword FogStateStruct
private keyword FogModifierStruct
private keyword HashtableStruct
private keyword FrameStruct
endglobals
struct Table extends array
private static method operator shadowTable takes nothing returns Table
// Table:Table:integer|real|string
return HashtableStruct.typeid
endmethod
private static method operator parentTable takes nothing returns Table
// Table:Table
return RealStruct.typeid
endmethod
private static method operator hasChildTables takes nothing returns Table
// Table:boolean
return BooleanStruct.typeid
endmethod
private static method operator seenTables takes nothing returns Table
// Table:boolean
return StringStruct.typeid
endmethod
private static method operator instanceData takes nothing returns Table
// Table:integer
return Table.typeid
endmethod
private static method operator widths takes nothing returns Table
// Table:integer
return HandleStruct.typeid
endmethod
private static method operator recycledArrays takes nothing returns Table
// The same table, but with a better name for its purpose.
return HandleStruct.typeid
endmethod
private static integer forkKeyGen = 0
private static boolean isShadow = false
private static integer cleanUntil
private static Table tableToClean
method operator [] takes integer key returns Table
return LoadInteger(hashTable, this, key)
endmethod
method read takes string key returns Table
return this[StringHash(key)]
endmethod
method get takes handle key returns Table
return this[GetHandleId(key)]
endmethod
method operator []= takes integer key, Table value returns nothing
call SaveInteger(hashTable, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedInteger(hashTable, this, key)
endmethod
method written takes string key returns boolean
return this.has(StringHash(key))
endmethod
method stores takes handle key returns boolean
return this.has(GetHandleId(key))
endmethod
// Remove all keys and values from a Table instance
method flush takes nothing returns nothing
local Table shadow = shadowTable[this]
call FlushChildHashtable(hashTable, this)
if this > 0 and shadow > 0 then
call FlushChildHashtable(hashTable, shadow)
endif
call RemoveSavedBoolean(hashTable, hasChildTables, this)
endmethod
// This method enables quick table[parentIndex][childIndex].
//
// local Table table = Table.createFork(3)
// set table[15] = 40 // index 0 remains on the same table, so there is no need to switch to one of the parallel tables.
// set table.switch(1).unit[5] = GetTriggerUnit()
// set table.switch(2)[10] = 20
//
// Inline-friendly when not running in `TEST` mode
//
method switch takes integer key returns Table
static if TEST then
local integer i = widths[this]
if i == 0 then
call BJDebugMsg("Table.switch Error: Tried to invoke 'switch' method on invalid Table: " + I2S(this))
elseif key < 0 or key >= i then
call BJDebugMsg("Table.switch Error: Tried to get key [" + I2S(key) + "] from outside bounds: " + I2S(i))
endif
endif
return this + key
endmethod
// Returns a new Table instance that can save/load any hashtable-compatible data type.
static method create takes nothing returns Table
local Table table = instanceData[0]
if table == 0 then
set table = tableKeyGen + 1
set tableKeyGen = table
static if DEEP_TEST then
call BJDebugMsg("Table.create getting new index: " + I2S(table))
endif
else
set instanceData[0] = instanceData[table]
static if DEEP_TEST then
call BJDebugMsg("Table.create recycling index: " + I2S(table))
endif
endif
set instanceData[table] = -1
if isShadow then
set isShadow = false
else
set isShadow = true
set shadowTable[table] = Table.create()
endif
return table
endmethod
private method recycle takes nothing returns nothing
call this.flush()
if instanceData[this] != -1 then
static if TEST then
call BJDebugMsg("Table.recycle Error: " + I2S(this) + " is already inactive!")
endif
return
endif
set instanceData[this] = instanceData[0]
set instanceData[0] = this
endmethod
private method setInactive takes nothing returns nothing
local Table shadow = shadowTable[this]
call this.recycle()
if shadow > 0 then
static if DEEP_TEST then
call BJDebugMsg("Setting " + I2S(this) + " and its shadow " + I2S(shadow) + " to inactive state.")
endif
call shadow.recycle()
call RemoveSavedInteger(hashTable, shadowTable, this)
call RemoveSavedInteger(hashTable, parentTable, this)
else
static if DEEP_TEST then
call BJDebugMsg("Setting Table: " + I2S(this) + " to inactive state.")
endif
endif
endmethod
// Returns:
// -1: invalid Table for this operation (key/typeid/fork Table, or simply a destroyed/incorrect reference to a Table).
// 0: key does not exist yet in the Table
// >0: key exists in the Table.
method getKeyIndex takes integer key returns integer
local Table shadow = shadowTable[this]
if this <= 0 or shadow == 0 then
return -1
endif
return R2I(LoadReal(hashTable, shadow, key))
endmethod
method addKey takes integer key returns nothing
local Table shadow
local integer i = this.getKeyIndex(key)
if i == 0 then
set shadow = shadowTable[this]
set i = shadow[0] + 1
set shadow[0] = i
set shadow[i] = key
call SaveReal(hashTable, shadow, key, i)
static if DEEP_TEST then
call BJDebugMsg("Increasing table " + I2S(this) + "'s' key size to " + I2S(i))
endif
endif
endmethod
static if TEST then
method addTypedKey takes integer key, string whichType returns nothing
local Table shadow = shadowTable[this]
local string oldType = LoadStr(hashTable, shadow, key)
if oldType != null and oldType != whichType then
call BJDebugMsg("Table.addKey Error: Type " + whichType + " and " + oldType + " saved at key: " + I2S(key))
endif
call SaveStr(hashTable, shadow, key, whichType)
call this.addKey(key)
endmethod
endif
private method nestTable takes integer key, Table table returns nothing
set this[key] = table
static if TEST then
call this.addTypedKey(key, "Table")
else
call this.addKey(key)
endif
set parentTable[table] = this
call SaveBoolean(hashTable, hasChildTables, this, true)
endmethod
method link takes integer key returns Table
local Table table = this[key]
if table == 0 then
set table = Table.create()
static if DEEP_TEST then
call BJDebugMsg("Table(" + I2S(this) + ")[" + I2S(key) + "] => Table(" + I2S(table) + ")")
endif
elseif instanceData[table] != -1 then
static if TEST then
call BJDebugMsg("Table.link Error: Invalid Table " + I2S(table) + " found at key " + I2S(key))
endif
return 0
endif
call this.nestTable(key, table)
return table
endmethod
method save takes integer key, Table value returns Table
static if TEST then
call Table(this).addTypedKey(key, "Table")
else
call Table(this).addKey(key)
endif
set this[key] = value
return this
endmethod
method write takes string key, Table value returns Table
return this.save(StringHash(key), value)
endmethod
method store takes handle key, Table value returns Table
return this.save(GetHandleId(key), value)
endmethod
method join takes string key returns Table
return this.link(StringHash(key))
endmethod
method bind takes handle key returns Table
return this.link(GetHandleId(key))
endmethod
private static method cleanFork takes nothing returns nothing
local Table table = tableToClean
local integer exitWhen = table + 0x1000
if exitWhen < cleanUntil then
set tableToClean = exitWhen
//Avoids hitting the op limit
call ForForce(bj_FORCE_PLAYER[0], function Table.cleanFork)
else
set exitWhen = cleanUntil
endif
loop
exitwhen table == exitWhen
call table.flush()
set table = table + 1
endloop
endmethod
private method destroyFork takes nothing returns boolean
local integer width = widths[this]
local Table recycled
local integer i = width
if this >= 0 or width == 0 then
return false
endif
set tableToClean = this
set cleanUntil = this + widths[this]
call Table.cleanFork()
call RemoveSavedInteger(hashTable, widths, this) //Clear the array size from hash memory
set recycled = recycledArrays.link(width)
set recycled[this] = recycled[0]
set recycled[0] = this
return true
endmethod
// Returns a special type of Table with `width` parallel indices.
//
// local Table fork = Table.fork(width)
//
static method createFork takes integer width returns Table
local Table recycled = recycledArrays.link(width) //Get the unique recycle list for this array size
local Table fork = recycled[0] //The last-destroyed fork that had this array size
if width <= 0 then
static if TEST then
call BJDebugMsg("Table.createFork Error: Invalid specified width: " + I2S(width))
endif
return 0
endif
if fork == 0 then
set fork = forkKeyGen - width // If we start with 8190, the first fork index will be -8190
set forkKeyGen = fork
else
set recycled[0] = recycled[fork] //Set the last destroyed to the last-last destroyed
call RemoveSavedInteger(hashTable, recycled, fork)
endif
set widths[fork] = width
return fork
endmethod
method forkLink takes integer width, integer key returns Table
local Table table = this[key]
if table == 0 then
set table = Table.createFork(width)
elseif widths[this] == 0 then
static if TEST then
call BJDebugMsg("Table.forkLink Error: Invalid Table " + I2S(table) + " found at key " + I2S(key))
endif
return 0
endif
call this.nestTable(key, table)
return table
endmethod
method forkJoin takes integer width, string key returns Table
return this.forkLink(width, StringHash(key))
endmethod
method forkBind takes integer width, handle key returns Table
return this.forkLink(width, GetHandleId(key))
endmethod
method getKeys takes nothing returns integer
local Table shadow = shadowTable[this]
if this <= 0 or shadow == 0 then
static if TEST then
call BJDebugMsg("Table.getKeys Error: Called on invalid Table " + I2S(this))
endif
return 0
endif
return shadow
endmethod
method measureFork takes nothing returns integer
return widths[this]
endmethod
private method destroyDeep takes nothing returns nothing
local Table shadow = shadowTable[this]
local integer i = shadow[0] //get the number of tracked indices
local Table table
static if DEEP_TEST then
call BJDebugMsg("Destroying Table: " + I2S(this) + " and all of its child tables.")
endif
// Mark this table as seen to avoid potentially-infinite recursion
call SaveBoolean(hashTable, seenTables, this, true)
loop
exitwhen i == 0
// Get the actual table using the index from shadow
set table = this[shadow[i]]
if table > 0 then
if instanceData[table] == -1 and /*
*/ parentTable[table] == this and /*
*/ not LoadBoolean(hashTable, seenTables, table) /*
*/ then
if LoadBoolean(hashTable, hasChildTables, table) then
call table.destroyDeep()
else
call table.setInactive()
endif
endif
elseif table < 0 then
call this.destroyFork()
endif
set i = i - 1
endloop
call this.setInactive()
endmethod
// Removes all data from a Table instance and recycles its index.
method destroy takes nothing returns nothing
if instanceData[this] != -1 then
if not this.destroyFork() then
static if TEST then
call BJDebugMsg("Table.destroy Error: Inactive Table: " + I2S(this))
endif
endif
else
if LoadBoolean(hashTable, hasChildTables, this) then
call this.destroyDeep()
call FlushChildHashtable(hashTable, seenTables)
else
call this.setInactive()
endif
endif
endmethod
method removeKey takes integer key returns nothing
local Table shadow
local Table child = this[key]
local integer i = this.getKeyIndex(key)
local integer top
if i > 0 then
set shadow = shadowTable[this]
static if TEST then
call RemoveSavedString(hashTable, shadow, key)
endif
set top = shadow[0]
if top == 1 then
call FlushChildHashtable(hashTable, shadow)
else
call RemoveSavedReal(hashTable, shadow, key)
set shadow[0] = top - 1
if top != i then
set key = shadow[top]
set shadow[i] = key
call SaveReal(hashTable, shadow, key, i)
endif
call RemoveSavedInteger(hashTable, shadow, top)
endif
endif
if child > 0 and /*
*/ instanceData[child] == -1 and /*
*/ parentTable[child] == this /*
*/ then
call child.destroy()
endif
endmethod
method remove takes integer key returns nothing
call this.removeKey(key)
call RemoveSavedInteger(hashTable, this, key)
endmethod
method delete takes string key returns nothing
call this.remove(StringHash(key))
endmethod
method forget takes handle key returns nothing
call this.remove(GetHandleId(key))
endmethod
method operator handle takes nothing returns HandleStruct
return this
endmethod
method operator agent takes nothing returns AgentStruct
return this
endmethod
// Implement modules for handle/agent/integer/real/boolean/string/etc syntax.
implement IntegerModule
implement RealModule
implement BooleanModule
implement StringModule
implement PlayerModule
implement WidgetModule
implement DestructableModule
implement ItemModule
implement UnitModule
implement AbilityModule
implement TimerModule
implement TriggerModule
implement TriggerConditionModule
implement TriggerActionModule
implement TriggerEventModule
implement ForceModule
implement GroupModule
implement LocationModule
implement RectModule
implement BooleanExprModule
implement SoundModule
implement EffectModule
implement UnitPoolModule
implement ItemPoolModule
implement QuestModule
implement QuestItemModule
implement DefeatConditionModule
implement TimerDialogModule
implement LeaderboardModule
implement MultiboardModule
implement MultiboardItemModule
implement TrackableModule
implement DialogModule
implement ButtonModule
implement TextTagModule
implement LightningModule
implement ImageModule
implement UbersplatModule
implement RegionModule
implement FogStateModule
implement FogModifierModule
implement HashtableModule
implement FrameModule
static if TEST then
private method toStringFn takes integer depth returns string
local Table shadow = shadowTable[this]
local integer i = shadow[0]
local string indent = ""
local integer k = 0
local string output
local Table table
local string typeOf
local string value
local integer keyOf
local string parsedKey
// Determine if this is a tracked table and if it's already been seen
if this > 0 and shadow > 0 then
if HaveSavedBoolean(hashTable, seenTables, this) then
// Show already-referenced Table:
return "Seen Table(" + I2S(this) + ")"
endif
call SaveBoolean(hashTable, seenTables, this, true)
if i == 0 then
// Show empty Table:
return "Table(" + I2S(this) + ")[]"
endif
set output = "Table(" + I2S(this) + ")["
elseif instanceData[this] > 0 then
return "Destroyed Table(" + I2S(this) + ")"
elseif widths[this] > 0 then
return "Tables " + I2S(this) + " through " + I2S(this + widths[this] - 1)
elseif instanceData[this] == 0 then
return "Invalid Table(" + I2S(this) + ")"
endif
loop
exitwhen k == depth
set indent = indent + " "
set k = k + 1
endloop
loop
exitwhen i == 0
set keyOf = shadow[i]
set typeOf = LoadStr(hashTable, shadow, keyOf)
set parsedKey = I2S(keyOf)
if typeOf == "Table" then
set table = this[keyOf]
set typeOf = ""
if instanceData[keyOf] == -1 or widths[keyOf] > 0 then
set parsedKey = Table(keyOf).toStringFn(depth)
endif
if instanceData[table] == -1 or widths[table] > 0 then
set value = table.toStringFn(depth + 1)
else
set value = I2S(table) // simple integer
endif
elseif typeOf == "integer" then
set typeOf = ""
set value = I2S(this[keyOf])
elseif typeOf == "string" then
set typeOf = ""
set value = "\"" + LoadStr(hashTable, this, keyOf) + "\""
elseif typeOf == "real" then
set typeOf = ""
set value = R2S(LoadReal(hashTable, this, keyOf))
elseif typeOf == "boolean" then
set typeOf = ""
if LoadBoolean(hashTable, this, keyOf) then
set value = "true"
else
set value = "false"
endif
elseif typeOf == null then
set typeOf = ""
set value = "untracked value"
else
set value = ""
endif
set output = output + "\n" + indent + " [" + parsedKey + "] = " + typeOf + value
set i = i - 1
endloop
return output + "\n" + indent + "]"
endmethod
method toString takes nothing returns string
local string result = this.toStringFn(0)
call seenTables.flush()
return result
endmethod
method print takes nothing returns nothing
call BJDebugMsg(toString())
endmethod
endif
//! runtextmacro optional TABLE_VBC_METHODS()
endstruct
/*
Create API for stuff like:
set table.unit[key] = GetTriggerUnit()
local boolean b = table.handle.has(key)
local unit u = table.unit[key]
set table.handle.remove(key)
These structs include the entire hashtable API as wrappers.
Feel free to remove any types that you don't use.
*/
struct HandleStruct extends array
method remove takes integer key returns nothing
call Table(this).removeKey(key)
call RemoveSavedHandle(hashTable, this, key)
endmethod
method delete takes string key returns nothing
call this.remove(StringHash(key))
endmethod
method forget takes handle key returns nothing
call this.remove(GetHandleId(key))
endmethod
method operator []= takes integer key, handle h returns nothing
if h != null then
// "But I need hashtables to typecast generic handles into ..." - say no more. I got u fam.
call SaveFogStateHandle(hashTable, this, key, ConvertFogState(GetHandleId(h)))
else
call this.remove(key)
endif
endmethod
method save takes integer key, agent value returns Table
static if TEST then
call Table(this).addTypedKey(key, "handle")
else
call Table(this).addKey(key)
endif
set this[key] = value
return this
endmethod
method write takes string key, agent value returns Table
return this.save(StringHash(key), value)
endmethod
method store takes handle key, agent value returns Table
return this.save(GetHandleId(key), value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(hashTable, this, key)
endmethod
method written takes string key returns boolean
return this.has(StringHash(key))
endmethod
method stores takes handle key returns boolean
return this.has(GetHandleId(key))
endmethod
endstruct
struct AgentStruct extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(hashTable, this, key, value)
endmethod
method save takes integer key, agent value returns Table
static if TEST then
call Table(this).addTypedKey(key, "agent")
else
call Table(this).addKey(key)
endif
set this[key] = value
return this
endmethod
method write takes string key, agent value returns Table
return this.save(StringHash(key), value)
endmethod
method store takes handle key, agent value returns Table
return this.save(GetHandleId(key), value)
endmethod
endstruct
//! textmacro BASIC_VALUE_TABLE takes SUPER, FUNC, TYPE
struct $SUPER$Struct extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(hashTable, this, key)
endmethod
method get takes handle key returns $TYPE$
return this[GetHandleId(key)]
endmethod
method read takes string key returns $TYPE$
return this[StringHash(key)]
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(hashTable, this, key, value)
endmethod
method save takes integer key, $TYPE$ value returns Table
static if TEST then
call Table(this).addTypedKey(key, "$TYPE$")
else
call Table(this).addKey(key)
endif
set this[key] = value
return this
endmethod
method store takes handle key, $TYPE$ value returns Table
return this.save(GetHandleId(key), value)
endmethod
method write takes string key, $TYPE$ value returns Table
return this.save(StringHash(key), value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(hashTable, this, key)
endmethod
method written takes string key returns boolean
return this.has(StringHash(key))
endmethod
method stores takes handle key returns boolean
return this.has(GetHandleId(key))
endmethod
method remove takes integer key returns nothing
call Table(this).removeKey(key)
call RemoveSaved$SUPER$(hashTable, this, key)
endmethod
method delete takes string key returns nothing
call this.remove(StringHash(key))
endmethod
method forget takes handle key returns nothing
call this.remove(GetHandleId(key))
endmethod
endstruct
module $SUPER$Module
method operator $TYPE$ takes nothing returns $SUPER$Struct
return this
endmethod
endmodule
//! endtextmacro
//! runtextmacro BASIC_VALUE_TABLE("Real", "Real", "real")
//! runtextmacro BASIC_VALUE_TABLE("Boolean", "Boolean", "boolean")
//! runtextmacro BASIC_VALUE_TABLE("String", "Str", "string")
//! runtextmacro BASIC_VALUE_TABLE("Integer", "Integer", "integer")
//! textmacro HANDLE_VALUE_TABLE takes FUNC, TYPE
struct $FUNC$Struct extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(hashTable, this, key)
endmethod
method get takes handle key returns $TYPE$
return this[GetHandleId(key)]
endmethod
method read takes string key returns $TYPE$
return this[StringHash(key)]
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(hashTable, this, key, value)
endmethod
method save takes integer key, $TYPE$ value returns Table
static if TEST then
call Table(this).addTypedKey(key, "$TYPE$")
else
call Table(this).addKey(key)
endif
set this[key] = value
return this
endmethod
method store takes handle key, $TYPE$ value returns Table
return this.save(GetHandleId(key), value)
endmethod
method write takes string key, $TYPE$ value returns Table
return this.save(StringHash(key), value)
endmethod
// deprecated; use handle.has/stores/written
method has takes integer key returns boolean
return HaveSavedHandle(hashTable, this, key)
endmethod
// deprecated; use handle.remove/forget/delete
method remove takes integer key returns nothing
call HandleStruct(this).remove(key)
endmethod
endstruct
module $FUNC$Module
method operator $TYPE$ takes nothing returns $FUNC$Struct
return this
endmethod
endmodule
//! endtextmacro
//! runtextmacro HANDLE_VALUE_TABLE("Player", "player")
//! runtextmacro HANDLE_VALUE_TABLE("Widget", "widget")
//! runtextmacro HANDLE_VALUE_TABLE("Destructable", "destructable")
//! runtextmacro HANDLE_VALUE_TABLE("Item", "item")
//! runtextmacro HANDLE_VALUE_TABLE("Unit", "unit")
//! runtextmacro HANDLE_VALUE_TABLE("Ability", "ability")
//! runtextmacro HANDLE_VALUE_TABLE("Timer", "timer")
//! runtextmacro HANDLE_VALUE_TABLE("Trigger", "trigger")
//! runtextmacro HANDLE_VALUE_TABLE("TriggerCondition", "triggercondition")
//! runtextmacro HANDLE_VALUE_TABLE("TriggerAction", "triggeraction")
//! runtextmacro HANDLE_VALUE_TABLE("TriggerEvent", "event")
//! runtextmacro HANDLE_VALUE_TABLE("Force", "force")
//! runtextmacro HANDLE_VALUE_TABLE("Group", "group")
//! runtextmacro HANDLE_VALUE_TABLE("Location", "location")
//! runtextmacro HANDLE_VALUE_TABLE("Rect", "rect")
//! runtextmacro HANDLE_VALUE_TABLE("BooleanExpr", "boolexpr")
//! runtextmacro HANDLE_VALUE_TABLE("Sound", "sound")
//! runtextmacro HANDLE_VALUE_TABLE("Effect", "effect")
//! runtextmacro HANDLE_VALUE_TABLE("UnitPool", "unitpool")
//! runtextmacro HANDLE_VALUE_TABLE("ItemPool", "itempool")
//! runtextmacro HANDLE_VALUE_TABLE("Quest", "quest")
//! runtextmacro HANDLE_VALUE_TABLE("QuestItem", "questitem")
//! runtextmacro HANDLE_VALUE_TABLE("DefeatCondition", "defeatcondition")
//! runtextmacro HANDLE_VALUE_TABLE("TimerDialog", "timerdialog")
//! runtextmacro HANDLE_VALUE_TABLE("Leaderboard", "leaderboard")
//! runtextmacro HANDLE_VALUE_TABLE("Multiboard", "multiboard")
//! runtextmacro HANDLE_VALUE_TABLE("MultiboardItem", "multiboarditem")
//! runtextmacro HANDLE_VALUE_TABLE("Trackable", "trackable")
//! runtextmacro HANDLE_VALUE_TABLE("Dialog", "dialog")
//! runtextmacro HANDLE_VALUE_TABLE("Button", "button")
//! runtextmacro HANDLE_VALUE_TABLE("TextTag", "texttag")
//! runtextmacro HANDLE_VALUE_TABLE("Lightning", "lightning")
//! runtextmacro HANDLE_VALUE_TABLE("Image", "image")
//! runtextmacro HANDLE_VALUE_TABLE("Ubersplat", "ubersplat")
//! runtextmacro HANDLE_VALUE_TABLE("Region", "region")
//! runtextmacro HANDLE_VALUE_TABLE("FogState", "fogstate")
//! runtextmacro HANDLE_VALUE_TABLE("FogModifier", "fogmodifier")
//! runtextmacro HANDLE_VALUE_TABLE("Hashtable", "hashtable")
//! runtextmacro HANDLE_VALUE_TABLE("Frame", "framehandle")
//! runtextmacro optional TABLE_VBC_STRUCTS()
// Run these only to support backwards-compatibility.
// If you want to use them, include the Table5BC library in your script.
//! runtextmacro optional TABLE_ARRAY_BC()
//! runtextmacro optional TableXD("Table2D", "Table", "createFork(1)")
//! runtextmacro optional TableXD("Table3D", "Table2D", "createFork(1)")
//! runtextmacro optional TableXD("Table4D", "Table3D", "createFork(1)")
//! runtextmacro optional TableXD("Table5D", "Table4D", "createFork(1)")
//! runtextmacro optional TableXD("Table2DT", "Table", "create()")
//! runtextmacro optional TableXD("Table3DT", "Table2DT", "create()")
//! runtextmacro optional TableXD("Table4DT", "Table3DT", "create()")
//! runtextmacro optional TableXD("Table5DT", "Table4DT", "create()")
//! runtextmacro optional TableXD("HashTable", "Table", "createFork(1)")
//! runtextmacro optional TableXD("HashTableEx", "Table", "create()")
endlibrary
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=28
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=21
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
library DummyRecycler /*
// DummyRecycler v1.25
// by Flux
//
// A system that recycles dummy units while considering their facing angle.
// It can be used as attachment dummies for visual effects or as dummy caster.
//
// Why is recycling a unit important?
// Because creating a unit is is one of the slowest function in the game
// and there are reports that will always leave a permanent tiny bit of
// memory (0.04 KB).
// On average, retrieving a pending Dummy is approximately 4x faster compared
// to creating a new one and recycling a Dummy compared to removing it is
// approximately 1.3x faster.
// Furthermore, if you're using a lot of "Unit has entered map" events,
// using this system will even result to even more better performance
// because retrieving Dummy units does not cause that event to run.
*/ requires /*
nothing
*/ optional Table/*
if not found, this system will use a hashtable. Hashtables are limited to
255 per map.
*/ optional WorldBounds /*
if not found, this system will initialize its own Map Boundaries.
//
//
// Features:
//
// -- Dummy Sharing
// When a Dummy List gets low on unit count, it will borrow Dummy Units
// from the Dummy List with the highest unit count. The transfer is not
// instant because the shared Dummy Unit has to turn to the appropriate
// angle of its new Dummy List before it can be recycled.
// See BORROW_REQUEST.
//
// -- Self-balancing recycling algorithm
// Recycled Dummy Units will be thrown to the List having the least number
// of Dummy Units.
//
// -- Recycling least used
// Allows recycling a Dummy from the Dummy List with the highest
// unit count. It is useful when the facing angle of the Dummy Unit
// does not matter.
// See GetRecycledDummyAnyAngle.
//
// -- Self-adaptation
// When there are no free Dummy Units from a Dummy List, it will end up creating
// a new unit instead but that unit will be permanently added as a Dummy
// Unit to be recycled increasing the overall total Dummy Unit count.
//
// -- Count control
// Allows limiting the overall number of Dummy Units.
// See MAX_DUMMY_COUNT.
//
// -- Delayed Recycle
// Allows recycling Dummy Units after some delay to allocate time for the
// death animation of Special Effects to be seen.
// See DummyAddRecycleTimer.
//
// ******************************************************************
// ***************************** API: *******************************
// ******************************************************************
//
// function GetRecycledDummy takes real x, real y, real z, real facing returns unit
// - Retrieve an unused Dummy Unit from the List.
// - The equivalent of CreateUnit.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
// - Use this function if the facing angle of the Dummy doesn't matter to you.
// - It will return a unit from the list having the highest number of unused Dummy Units.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function RecycleDummy takes unit u returns nothing
// - Recycle the Dummy unit for it to be used again later.
// - The equivalent of RemoveUnit.
//
// function DummyAddRecycleTimer takes unit u, real time returns nothing
// - Recycle the Dummy unit after a certain time.
// - Use this to allocate time for the the death animation of an effect attached to the
// Dummy Unit to finish..
// - The equivalent of UnitApplyTimedLife.
//
// function ShowDummy takes unit u, boolean flag returns nothing
// - Shows/hides Dummy Unit without conflicting with the Locust ability.
//
//--------------------
// CREDITS
//--------------------
// Bribe - for the MissileRecycler (vJASS) where I got this concept from
// http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
// - for the optional Table
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
// Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
// http://www.wc3c.net/showthread.php?t=101150
// Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
// http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
// Nestharus - for the data structure
// http://www.hiveworkshop.com/forums/2809461-post7.html
// - for the optional WorldBounds
// http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// =============================================================== //
// ====================== CONFIGURATION ========================== //
// =============================================================== */
globals
//The rawcode of the Dummy Unit
private constant integer DUMMY_ID = 'dumy'
//The owner of the Dummy Unit
private constant player OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
//The number of indexed angle. The higher the value the:
// - Lesser the turning time for the Dummy Units.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 10 (Max difference of 18 degrees)
private constant integer ANGLES_COUNT = 10
//The number of Dummy units per ANGLES_COUNT. The higher the value the:
// - Higher the number of units that can be recycled per angle, when
// no more units are in queue, the system will resort to use CreateUnit.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
private constant integer STORED_UNIT_COUNT = 3
//The maximum number of Dummy units that can exist. When the system resort
//to using CreateUnit, the unit will be permanently added to the Dummy
//List. To avoid spamming Dummy Units and having too much free Dummy
//Units to allocate, the maximum number of Dummy Units is capped.
// Recommended Value: 80 to 120
private constant integer MAX_DUMMY_COUNT = 100
//When a certain angle have less than BORROW_REQUEST units in its list,
//it will start to borrow Dummy Units from the list with the highest
//Dummy Unit count.
// Recommended Value: Half of maximum STORED_UNIT_COUNT
private constant integer BORROW_REQUEST = 5
//It will only return a Dummy if the current dummy is close
//to it's appropriate facing angle. This is to avoid returning
//a Dummy which is still turning to face it's list angle.
private constant real ANGLE_TOLERANCE = 10.0
//An additional option to automatically hide recycled dummy units in the
//corner of the map camera bounds
private constant boolean HIDE_ON_MAP_CORNER = true
endglobals
//Every time a new dummy unit is retrieved, it will apply this resets
//If it is redundant/you dont need it, remove it.
//! textmacro DUMMY_UNIT_RESET
call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
call ShowDummy(bj_lastCreatedUnit, true)
//! endtextmacro
// =============================================================== //
// ==================== END CONFIGURATION ======================== //
// =============================================================== //
globals
private integer dummyCount = ANGLES_COUNT*STORED_UNIT_COUNT
private real array angle
private integer array count
private integer array countHead
private integer array countNext
private integer array countPrev
private integer array next
private integer array prev
private unit array dummy
private integer upper
private integer lower
private integer lastInstance
private constant real FACING_OFFSET = 180.0/ANGLES_COUNT
endglobals
static if HIDE_ON_MAP_CORNER and not LIBRARY_WorldBounds then
private module BoundsInit
readonly static real x
readonly static real y
private static method onInit takes nothing returns nothing
local rect map = GetWorldBounds()
set thistype.x = GetRectMaxX(map)
set thistype.y = GetRectMaxY(map)
call RemoveRect(map)
set map = null
endmethod
endmodule
private struct Bounds extends array
implement BoundsInit
endstruct
endif
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable hash = InitHashtable()
endif
private static method onInit takes nothing returns nothing
local real add = 360.0/ANGLES_COUNT
local real a = 0
local integer this = ANGLES_COUNT
local integer head = 0
local integer cHead = JASS_MAX_ARRAY_SIZE - 1 //avoid allocation collision
local integer i = R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
set upper = STORED_UNIT_COUNT
set lower = STORED_UNIT_COUNT
static if LIBRARY_Table then
set tb = Table.create()
endif
//Initialize countHeads
loop
exitwhen i < 0
set countNext[cHead] = cHead
set countPrev[cHead] = cHead
set countHead[i] = cHead
set cHead = cHead - 1
set i = i - 1
endloop
set cHead = countHead[STORED_UNIT_COUNT] //All heads will be inserted here initially
//Create the Dummy units
loop
exitwhen a >= 360
//Initialize head
set next[head] = head
set prev[head] = head
set count[head] = STORED_UNIT_COUNT
set angle[head] = a
//Insert head in the Count List
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
set i = 0
loop
exitwhen i >= STORED_UNIT_COUNT
//Queued Linked List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, a)
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, Bounds.x, Bounds.y, a)
endif
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, 0, 0, a)
endif
call PauseUnit(dummy[this], true)
static if LIBRARY_Table then
set tb[GetHandleId(dummy[this])] = this
else
call SaveInteger(hash, GetHandleId(dummy[this]), 0, this)
endif
set this = this + 1
set i = i + 1
endloop
set head = head + 1
set a = a + add
endloop
set lastInstance = this
endmethod
endmodule
private struct S extends array
implement M
endstruct
private function GetHead takes integer facing returns integer
if facing < 0 or facing >= 360 then
set facing = facing - (facing/360)*360
if facing < 0 then
set facing = facing + 360
endif
endif
return R2I((facing*ANGLES_COUNT/360.0))
endfunction
function ShowDummy takes unit u, boolean flag returns nothing
if IsUnitHidden(u) == flag then
call ShowUnit(u, flag)
if flag and GetUnitTypeId(u) == DUMMY_ID then
call UnitRemoveAbility(u, 'Aloc')
call UnitAddAbility(u, 'Aloc')
endif
endif
endfunction
function GetRecycledDummy takes real x, real y, real z, real facing returns unit
local integer head = GetHead(R2I(facing + FACING_OFFSET))
local integer this = next[head]
local integer cHead
//If there are Dummy Units in the Queue List already facing close to the appropriate angle
if this != head and RAbsBJ(GetUnitFacing(dummy[this]) - angle[head]) <= ANGLE_TOLERANCE then
//Remove from the Queue List
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//For double free protection
set next[this] = -1
//Unit Properties
set bj_lastCreatedUnit = dummy[this]
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitFacing(bj_lastCreatedUnit, facing)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
//! runtextmacro DUMMY_UNIT_RESET()
//Update Count and Bounds
set count[head] = count[head] - 1
//------------------------------------------------
// Unit Sharing
//------------------------------------------------
if count[head] < BORROW_REQUEST and count[countNext[countHead[upper]]] > count[head] then
set count[head] = count[head] + 1
set this = next[countNext[countHead[upper]]]
call SetUnitFacing(dummy[this], angle[head])
//Remove
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//Add to the Current List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
set head = countNext[countHead[upper]]
set count[head] = count[head] - 1
endif
//---------------------------
//Update Count Lists
//---------------------------
//Remove from the current Count List
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[upper]
if countNext[cHead] == cHead then
set upper = upper - 1
endif
if count[head] < lower then
set lower = count[head]
endif
else
set bj_lastCreatedUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
call PauseUnit(bj_lastCreatedUnit, true)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
if dummyCount < MAX_DUMMY_COUNT then
set this = lastInstance
//For double free protection
set next[this] = -1
set dummy[this] = bj_lastCreatedUnit
static if LIBRARY_Table then
set S.tb[GetHandleId(bj_lastCreatedUnit)] = this
else
call SaveInteger(S.hash, GetHandleId(bj_lastCreatedUnit), 0, this)
endif
set lastInstance = lastInstance + 1
endif
set dummyCount = dummyCount + 1
endif
return bj_lastCreatedUnit
endfunction
function RecycleDummy takes unit u returns nothing
static if LIBRARY_Table then
local integer this = S.tb[GetHandleId(u)]
else
local integer this = LoadInteger(S.hash, GetHandleId(u), 0)
endif
local integer head
local integer cHead
//If the unit is a legit Dummy Unit
if this > 0 and next[this] == -1 then
//Find where to insert based on the list having the least number of units
set head = countNext[countHead[lower]]
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
//Update Status
call SetUnitFacing(u, angle[head])
call PauseUnit(u, true)
call SetUnitOwner(u, OWNER, false)
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
call SetUnitX(u, WorldBounds.maxX)
call SetUnitY(u, WorldBounds.maxY)
else
call SetUnitX(u, Bounds.x)
call SetUnitY(u, Bounds.y)
endif
else
call SetUnitScale(u, 0, 0, 0)
call SetUnitVertexColor(u, 0, 0, 0, 0)
endif
set count[head] = count[head] + 1
//---------------------------
// Update Count Lists
//---------------------------
//Remove
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[lower]
if countNext[cHead] == cHead then
set lower = lower + 1
endif
if count[head] > upper then
set upper = count[head]
endif
elseif this == 0 then
call RemoveUnit(u)
debug elseif next[this] != -1 then
debug call BJDebugMsg("|cffffcc00[DummyRecycler]:|r Attempted to recycle a pending/free Dummy Unit.")
endif
endfunction
private function Expires takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
static if LIBRARY_Table then
call RecycleDummy(S.tb.unit[id])
call S.tb.unit.remove(id)
else
call RecycleDummy(LoadUnitHandle(S.hash, id, 0))
call FlushChildHashtable(S.hash, id)
endif
call DestroyTimer(t)
set t = null
endfunction
function DummyAddRecycleTimer takes unit u, real time returns nothing
local timer t = CreateTimer()
static if LIBRARY_Table then
set S.tb.unit[GetHandleId(t)] = u
else
call SaveUnitHandle(S.hash, GetHandleId(t), 0, u)
endif
call TimerStart(t, time, false, function Expires)
set t = null
endfunction
function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
return GetRecycledDummy(x, y, z, angle[countNext[countHead[upper]]])
endfunction
function GetDummyFacingTarget takes unit u returns unit
local real facing = GetRandomReal(0., bj_PI * 2.)
local real x = GetUnitX(u) + 50. * Cos(facing + bj_PI)
local real y = GetUnitY(u) + 50. * Sin(facing + bj_PI)
return GetRecycledDummy(x, y, 0., facing)
endfunction
// runtextmacro DUMMY_DEBUG_TOOLS()
endlibrary
library UnitAddAbilityTimed requires TimerUtils, DummyRecycler
//! novjass
-------- API --------
call TimedAbility.create(unit Your Unit, integer Ability ID, real Timeout, boolean Recycle Unit)
/*
If you want to recycle the unit right after the ability is removed, set the boolean
(the final argument) to true
*/
//! endnovjass
struct TimedAbility
unit u
integer abil
boolean recycle
private method destroy takes nothing returns nothing
call this.deallocate()
set this.u = null
set this.abil = 0
set this.recycle = false
endmethod
private static method removeAbil takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call UnitRemoveAbility(this.u, this.abil)
if this.recycle then
call RecycleDummy(this.u)
endif
call this.destroy()
call ReleaseTimer(t)
set t = null
endmethod
static method create takes unit source, integer abil_code, real timeout, boolean recycle_source returns TimedAbility
local thistype this = allocate()
local timer t = NewTimerEx(this)
set this.u = source
set this.abil = abil_code
set this.recycle = recycle_source
call TimerStart(t, timeout, false, function thistype.removeAbil)
set t = null
return this
endmethod
endstruct
endlibrary
//TESH.scrollpos=4
//TESH.alwaysfold=0
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
*
* Fields
* -------------------------
*
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
*
* readonly static integer centerX
* readonly static integer centerY
*
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world = GetWorldBounds()
set maxX = R2I(GetRectMaxX(world))
set maxY = R2I(GetRectMaxY(world))
set minX = R2I(GetRectMinX(world))
set minY = R2I(GetRectMinY(world))
set centerX = R2I((maxX + minX)/2)
set centerY = R2I((minY + maxY)/2)
set playMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set playMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set playMinX = GetRectMinX(bj_mapInitialPlayableArea)
set playMinY = GetRectMinY(bj_mapInitialPlayableArea)
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
readonly static real playMaxX
readonly static real playMaxY
readonly static real playMinX
readonly static real playMinY
implement WorldBoundInit
endstruct
endlibrary
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Alloc ~~ By Sevion ~~ Version 1.09 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Alloc?
// - Alloc implements an intuitive allocation method for array structs
//
// =Pros=
// - Efficient.
// - Simple.
// - Less overhead than regular structs.
//
// =Cons=
// - Must use array structs (hardly a con).
// - Must manually call OnDestroy.
// - Must use Delegates for inheritance.
// - No default values for variables (use onInit instead).
// - No array members (use another Alloc struct as a linked list or type declaration).
//
// Methods:
// - struct.allocate()
// - struct.deallocate()
//
// These methods are used just as they should be used in regular structs.
//
// Modules:
// - Alloc
// Implements the most basic form of Alloc. Includes only create and destroy
// methods.
//
// Details:
// - Less overhead than regular structs
//
// - Use array structs when using Alloc. Put the implement at the top of the struct.
//
// - Alloc operates almost exactly the same as default structs in debug mode with the exception of onDestroy.
//
// How to import:
// - Create a trigger named Alloc.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Nestharus for the method of allocation and suggestions on further merging.
// - Bribe for suggestions like the static if and method names.
// - PurgeandFire111 for some suggestions like the merging of Alloc and AllocX as well as OnDestroy stuff.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Alloc
module Alloc
private static integer instanceCount = 0
private thistype recycle
static method allocate takes nothing returns thistype
local thistype this
if (thistype(0).recycle == 0) then
debug if (instanceCount == 8190) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to allocate too many instances!")
debug return 0
debug endif
set instanceCount = instanceCount + 1
set this = instanceCount
else
set this = thistype(0).recycle
set thistype(0).recycle = thistype(0).recycle.recycle
endif
debug set this.recycle = -1
return this
endmethod
method deallocate takes nothing returns nothing
debug if (this.recycle != -1) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to deallocate an invalid instance at [" + I2S(this) + "]!")
debug return
debug endif
set this.recycle = thistype(0).recycle
set thistype(0).recycle = this
endmethod
endmodule
endlibrary
library LineSegmentEnumeration /* v2.2a -- hiveworkshop.com/threads/line-segment-enumeration-v1-1.286552/
Information
¯¯¯¯¯¯¯¯¯¯¯
Allows to enumerate widgets inside a line segment with an offset.
So basicly it will result in a rect, but which is also allowed to be rotated.
Mechanics
¯¯¯¯¯¯¯¯¯
(Issue:)
The problem with normal jass rects is that they aren't defined by 4 points, but only by 4 values: x/y -min/max.
The result is that a jass rect is never rotated, so it's always paralel to the x/y axis.
But when we draw a line from point A to point B we might also create a non-axix-parelel rect, and then
we can't use the normal rect natives from jass anymore to find out if a point is inside the rect.
(Solution:)
To solve this problem the system does following:
jass rect: rectangular defined by 4 values (axis paralel)
custom rect: the real rectangular that is defined by user (not axis parelel)
1. Create a big jass rect that is big enough so we can ensure to enum all widgets that are potentialy inside our custom rect. (Enum_Group)
This Enum_Group will contain all wanted units, but may also contain not wanted units.
2. Construct the custom rect following a form with the same parameters as in this image, but in 2D:
https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Ellipsoide.svg/800px-Ellipsoide.svg.png
3. Loop through Enum_Group and define each widget's coordinates relative to the center of the custom rect as a 2D vector
4. Get the components of the widget's position vector on the local (rotated) x-y axis of the custom rect
5. Check if the projected lengths (absolute value of components) of the widget's position is less than <a> and <b> as described in the
image linked above.
*/
// --- API ---
//! novjass
struct LineSegment
static constant real MAX_UNIT_COLLISION
static method EnumUnitsEx takes group whichgroup, real ax, real ay, real bx, real by, real offset, boolean checkCollision returns nothing
static method EnumUnits takes group whichgroup, real ax, real ay, real bx, real by, real offset returns nothing
static method EnumDestructables takes real ax, real ay, real bx, real by, real offset returns nothing
// after enumerated destructables you have access to:
static integer DestructableCounter // starts with index "0"
static destructable array Destructable
static method EnumItems takes real ax, real ay, real bx, real by, real offset returns nothing
// after enumerated items you have access to:
static integer ItemCounter // starts with index "0"
static destructable array Item
//! endnovjass
// ==== End API ====
struct LineSegment extends array
public static constant real MAX_UNIT_COLLISION = 197.00
private static constant rect RECT = Rect(0, 0, 0, 0)
private static constant group GROUP = CreateGroup()
private static real ox
private static real oy
private static real dx
private static real dy
private static real da
private static real db
private static real ui
private static real uj
private static real wdx
private static real wdy
private static method PrepareRect takes real ax, real ay, real bx, real by, real offset, real offsetCollision returns nothing
local real maxX
local real maxY
local real minX
local real minY
// get center coordinates of rectangle
set ox = 0.5*(ax + bx)
set oy = 0.5*(ay + by)
// get rectangle major axis as vector
set dx = 0.5*(bx - ax)
set dy = 0.5*(by - ay)
// get half of rectangle length (da) and height (db)
set da = SquareRoot(dx*dx + dy*dy)
set db = offset
// get unit vector of the major axis
set ui = dx/da
set uj = dy/da
// Prepare the bounding Jass Rect
set offset = offset + offsetCollision
if ax > bx then
set maxX = ax + offset
set minX = bx - offset
else
set maxX = bx + offset
set minX = ax - offset
endif
if ay > by then
set maxY = ay + offset
set minY = by - offset
else
set maxY = by + offset
set minY = ay - offset
endif
call SetRect(RECT, minX, minY, maxX, maxY)
endmethod
private static method RotateWidgetCoordinates takes widget w returns nothing
// distance of widget from rectangle center in vector form
set wdx = GetWidgetX(w) - ox
set wdy = GetWidgetY(w) - oy
set dx = wdx*ui + wdy*uj // get the component of above vector in the rect's major axis
set dy = wdx*(-uj) + wdy*ui // get the component of above vector in the rect's transverse axis
endmethod
private static method IsWidgetInRect takes widget w returns boolean
call RotateWidgetCoordinates(w)
// Check if the components above are less than half the length and height of the rectangle
// (Square them to compare absolute values)
return dx*dx <= da*da and dy*dy <= db*db
endmethod
private static method IsUnitInRect takes unit u, boolean checkCollision returns boolean
if checkCollision then
call RotateWidgetCoordinates(u)
// Check if the perpendicular distances of the unit from both axes of the rect are less than
// da and db
return IsUnitInRangeXY(u, ox - dy*uj, oy + dy*ui, RAbsBJ(da)) /*
*/ and IsUnitInRangeXY(u, ox + dx*ui, oy + dx*uj, RAbsBJ(db))
endif
return IsWidgetInRect(u)
endmethod
public static method EnumUnitsEx takes group whichgroup, real ax, real ay, real bx, real by, real offset, boolean checkCollision returns nothing
local unit u
if checkCollision then
call PrepareRect(ax, ay, bx, by, offset, MAX_UNIT_COLLISION)
else
call PrepareRect(ax, ay, bx, by, offset, 0.00)
endif
call GroupEnumUnitsInRect(GROUP, RECT, null)
// enum through all tracked units, and check if it's inside bounds
call GroupClear(whichgroup)
loop
set u = FirstOfGroup(GROUP)
exitwhen u == null
if IsUnitInRect(u, checkCollision) then
call GroupAddUnit(whichgroup, u)
endif
call GroupRemoveUnit(GROUP, u)
endloop
endmethod
public static method EnumUnits takes group whichgroup, real ax, real ay, real bx, real by, real offset returns nothing
call EnumUnitsEx(whichgroup, ax, ay, bx, by, offset, false)
endmethod
//! textmacro LSE_WIDGET takes TYPE, NAME
public static integer $NAME$Counter = -1
public static $TYPE$ array $NAME$
private static method on$NAME$Filter takes nothing returns nothing
local $TYPE$ t = GetFilter$NAME$()
if IsWidgetInRect(t) then
set $NAME$Counter = $NAME$Counter + 1
set $NAME$[$NAME$Counter] = t
endif
set t = null
endmethod
public static method Enum$NAME$s takes real ax, real ay, real bx, real by, real offset returns nothing
call PrepareRect(ax, ay, bx, by, offset, 0.00)
set $NAME$Counter = -1
call Enum$NAME$sInRect(RECT, Filter(function thistype.on$NAME$Filter), null)
endmethod
//! endtextmacro
//! runtextmacro LSE_WIDGET("destructable", "Destructable")
//! runtextmacro LSE_WIDGET("item", "Item")
endstruct
endlibrary
library NewBonus requires CooldownReduction, DTCUtils
/* ----------------------- NewBonus v2.2 by Chopinski ----------------------- */
//! novjass
Since ObjectMerger is broken and we still have no means to edit
bonus values (green values) i decided to create a light weight
Bonus library that works in the same way that the original Bonus Mod
by Earth Fury did. NewBonus requires patch 1.30+.
Credits to Earth Fury for the original Bonus idea
How to Import?
Importing bonus mod is really simple. Just copy the 9 abilities with the
prefix "NewBonus" from the Object Editor into your map and match their new raw
code to the bonus types in the global block below. Then create a trigger called
NewBonus, convert it to custom text and paste this code there. You done!
API:
function GetUnitBonus takes unit u, integer bonus_type returns real
-> Returns the specified bonus amount for the unit
-> Example: set amount = GetUnitBonus(GetTriggerUnit(), BONUS_AGILITY)
function SetUnitBonus takes unit u, integer bonus_type, real amount returns real
-> Set the specified bonus type to amount for the unit
-> Example: call SetUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)
function RemoveUnitBonus takes unit u, integer bonus_type returns nothing
-> Removes the Specified bonus type from unit
-> Example: call RemoveUnitBonus(GetTriggerUnit(), BONUS_AGILITY)
function AddUnitBonus takes unit u, integer bonus_type, real amount returns real
-> Add the specified amount for the specified bonus tyte for unit
-> Example: call AddUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* Configuration */
/* --------------------------------------------------------------------------
*/
globals
//The bonus types
//Integer Values
constant integer BONUS_DAMAGE = 1
constant integer BONUS_ARMOR = 2
constant integer BONUS_AGILITY = 3
constant integer BONUS_STRENGTH = 4
constant integer BONUS_INTELLIGENCE = 5
constant integer BONUS_HEALTH = 6
constant integer BONUS_MANA = 7
constant integer BONUS_MOVEMENT_SPEED = 8
constant integer BONUS_SIGHT_RANGE = 9
constant integer BONUS_SPELL_POWER = 42
//Real Values
constant integer BONUS_HEALTH_REGEN = 50
constant integer BONUS_MANA_REGEN = 51
constant integer BONUS_ATTACK_SPEED = 52
constant integer BONUS_MAGIC_RESISTANCE = 53
constant integer BONUS_EVASION_CHANCE = 54
constant integer BONUS_CRITICAL_CHANCE = 55
constant integer BONUS_CRITICAL_DAMAGE = 56
constant integer BONUS_LIFE_STEAL = 57
constant integer BONUS_COOLDOWN_REDUCTION = 62
constant integer BONUS_COOLDOWN_REDUCTION_FLAT = 63
constant integer BONUS_COOLDOWN_OFFSET = 64
constant integer BONUS_SPELL_HASTE = 71 //% more casts. 100 of this = 50% BONUS_COOLDOWN_REDUCTION_FLAT
constant integer BONUS_SPELL_POWER_MULTIPLIER = 72
constant integer BONUS_ARMOR_PEN_FLAT = 73
constant integer BONUS_ARMOR_PEN_PERCENT = 74
//The abilities codes for each bonus
//When pasting the abilities over to your map
//their raw code should match the bonus here
private constant integer DAMAGE_ABILITY = 'Z001'
private constant integer ARMOR_ABILITY = 'Z002'
private constant integer STATS_ABILITY = 'Z003'
private constant integer HEALTH_ABILITY = 'Z004'
private constant integer MANA_ABILITY = 'Z005'
private constant integer HEALTHREGEN_ABILITY = 'Z006'
private constant integer MANAREGEN_ABILITY = 'Z007'
private constant integer ATTACKSPEED_ABILITY = 'Z008'
private constant integer MOVEMENTSPEED_ABILITY = 'Z009'
private constant integer SIGHT_RANGE_ABILITY = 'Z00A'
private constant integer MAGIC_RESISTANCE_ABILITY = 'Z00B'
//private constant integer CRITICAL_STRIKE_ABILITY = 'Z00C'
private constant integer EVASION_ABILITY = 'Z00D'
private constant integer LIFE_STEAL_ABILITY = 'Z00E'
//The abilities fields that are modified. For the sake of readability
private constant abilityintegerlevelfield DAMAGE_FIELD = ABILITY_ILF_ATTACK_BONUS
private constant abilityintegerlevelfield ARMOR_FIELD = ABILITY_ILF_DEFENSE_BONUS_IDEF
private constant abilityintegerlevelfield AGILITY_FIELD = ABILITY_ILF_AGILITY_BONUS
private constant abilityintegerlevelfield STRENGTH_FIELD = ABILITY_ILF_STRENGTH_BONUS_ISTR
private constant abilityintegerlevelfield INTELLIGENCE_FIELD = ABILITY_ILF_INTELLIGENCE_BONUS
private constant abilityintegerlevelfield HEALTH_FIELD = ABILITY_ILF_MAX_LIFE_GAINED
private constant abilityintegerlevelfield MANA_FIELD = ABILITY_ILF_MAX_MANA_GAINED
private constant abilityintegerlevelfield MOVEMENTSPEED_FIELD = ABILITY_ILF_MOVEMENT_SPEED_BONUS
private constant abilityintegerlevelfield SIGHT_RANGE_FIELD = ABILITY_ILF_SIGHT_RANGE_BONUS
private constant abilityreallevelfield HEALTHREGEN_FIELD = ABILITY_RLF_AMOUNT_OF_HIT_POINTS_REGENERATED
private constant abilityreallevelfield MANAREGEN_FIELD = ABILITY_RLF_AMOUNT_REGENERATED
private constant abilityreallevelfield ATTACKSPEED_FIELD = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1
private constant abilityreallevelfield MAGIC_RESISTANCE_FIELD = ABILITY_RLF_DAMAGE_REDUCTION_ISR2
private constant abilityreallevelfield CRITICAL_CHANCE_FIELD = ABILITY_RLF_CHANCE_TO_CRITICAL_STRIKE
private constant abilityreallevelfield CRITICAL_DAMAGE_FIELD = ABILITY_RLF_DAMAGE_MULTIPLIER_OCR2
private constant abilityreallevelfield EVASION_FIELD = ABILITY_RLF_CHANCE_TO_EVADE_EEV1
private constant abilityreallevelfield LIFE_STEAL_FIELD = ABILITY_RLF_LIFE_STOLEN_PER_ATTACK
endglobals
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
struct NewBonus
static method SetUnitAbilityBonusI takes unit u, integer abilCode, abilityintegerlevelfield field, integer amount returns integer
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
endif
if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), field, 0, amount) then
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
endif
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), field, 0)
endmethod
static method SetUnitAbilityBonusR takes unit u, integer abilCode, abilityreallevelfield field, real amount returns real
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
endif
if BlzSetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), field, 0, amount) then
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
endif
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), field, 0)
endmethod
static method Get takes unit u, integer bonus_type returns integer
if bonus_type == BONUS_DAMAGE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, DAMAGE_ABILITY), DAMAGE_FIELD, 0)
elseif bonus_type == BONUS_ARMOR then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, ARMOR_ABILITY), ARMOR_FIELD, 0)
elseif bonus_type == BONUS_HEALTH then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, HEALTH_ABILITY), HEALTH_FIELD, 0)
elseif bonus_type == BONUS_MANA then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MANA_ABILITY), MANA_FIELD, 0)
elseif bonus_type == BONUS_AGILITY then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), AGILITY_FIELD, 0)
elseif bonus_type == BONUS_STRENGTH then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), STRENGTH_FIELD, 0)
elseif bonus_type == BONUS_INTELLIGENCE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), INTELLIGENCE_FIELD, 0)
elseif bonus_type == BONUS_MOVEMENT_SPEED then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MOVEMENTSPEED_ABILITY), MOVEMENTSPEED_FIELD, 0)
elseif bonus_type == BONUS_SIGHT_RANGE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, SIGHT_RANGE_ABILITY), SIGHT_RANGE_FIELD, 0)
elseif bonus_type == BONUS_SPELL_POWER then
return GetSpellPowerBaseAmount(u)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1
endmethod
static method Set takes unit u, integer bonus_type, integer amount returns integer
local real p
if bonus_type == BONUS_DAMAGE then
return SetUnitAbilityBonusI(u, DAMAGE_ABILITY, DAMAGE_FIELD, amount)
elseif bonus_type == BONUS_ARMOR then
return SetUnitAbilityBonusI(u, ARMOR_ABILITY, ARMOR_FIELD, amount)
elseif bonus_type == BONUS_HEALTH then
set p = GetUnitLifePercent(u)
call BlzSetUnitMaxHP(u, (BlzGetUnitMaxHP(u) + amount - Get(u, bonus_type)))
call SetUnitLifePercentBJ(u, p)
return SetUnitAbilityBonusI(u, HEALTH_ABILITY, HEALTH_FIELD, amount)
elseif bonus_type == BONUS_MANA then
set p = GetUnitManaPercent(u)
call BlzSetUnitMaxMana(u, (BlzGetUnitMaxMana(u) + amount - Get(u, bonus_type)))
call SetUnitManaPercentBJ(u, p)
return SetUnitAbilityBonusI(u, MANA_ABILITY, MANA_FIELD, amount)
elseif bonus_type == BONUS_AGILITY then
return SetUnitAbilityBonusI(u, STATS_ABILITY, AGILITY_FIELD, amount)
elseif bonus_type == BONUS_STRENGTH then
return SetUnitAbilityBonusI(u, STATS_ABILITY, STRENGTH_FIELD, amount)
elseif bonus_type == BONUS_INTELLIGENCE then
return SetUnitAbilityBonusI(u, STATS_ABILITY, INTELLIGENCE_FIELD, amount)
elseif bonus_type == BONUS_MOVEMENT_SPEED then
return SetUnitAbilityBonusI(u, MOVEMENTSPEED_ABILITY, MOVEMENTSPEED_FIELD, amount)
elseif bonus_type == BONUS_SIGHT_RANGE then
call BlzSetUnitRealField(u, UNIT_RF_SIGHT_RADIUS, (BlzGetUnitRealField(u, UNIT_RF_SIGHT_RADIUS) + amount - Get(u, bonus_type)))
return SetUnitAbilityBonusI(u, SIGHT_RANGE_ABILITY, SIGHT_RANGE_FIELD, amount)
elseif bonus_type == BONUS_SPELL_POWER then
call SetSpellPowerBaseAmount(u, amount)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1
endmethod
static method Add takes unit u, integer bonus_type, integer amount returns integer
local integer current_amount = Get(u, bonus_type)
if amount > 0 and current_amount > 2147483647 - amount then
set amount = 2147483647 - current_amount
elseif amount < 0 and current_amount < -2147483648 - amount then
set amount = -2147483648 - current_amount
endif
call Set(u, bonus_type, (current_amount + amount))
return amount
endmethod
static method GetR takes unit u, integer bonus_type returns real
if bonus_type == BONUS_HEALTH_REGEN then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, HEALTHREGEN_ABILITY), HEALTHREGEN_FIELD, 0)
elseif bonus_type == BONUS_MANA_REGEN then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, MANAREGEN_ABILITY), MANAREGEN_FIELD, 0)
elseif bonus_type == BONUS_ATTACK_SPEED then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, ATTACKSPEED_ABILITY), ATTACKSPEED_FIELD, 0)
elseif bonus_type == BONUS_MAGIC_RESISTANCE then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, MAGIC_RESISTANCE_ABILITY), MAGIC_RESISTANCE_FIELD, 0)
elseif bonus_type == BONUS_CRITICAL_CHANCE then
return GetCriticalHitChance(u)
elseif bonus_type == BONUS_CRITICAL_DAMAGE then
return GetCriticalHitDamage(u)
elseif bonus_type == BONUS_EVASION_CHANCE then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, EVASION_ABILITY), EVASION_FIELD, 0)
elseif bonus_type == BONUS_LIFE_STEAL then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, LIFE_STEAL_ABILITY), LIFE_STEAL_FIELD, 0)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION then
return GetUnitCooldownReduction(u)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION_FLAT then
return GetUnitCooldownReductionFlat(u)
elseif bonus_type == BONUS_COOLDOWN_OFFSET then
return GetUnitCooldownOffset(u)
elseif bonus_type == BONUS_SPELL_HASTE then
//Lol Formula for AH
//https://www.reddit.com/r/leagueoflegends/comments/i5m8m6/i_made_a_chart_to_convert_cdr_to_ability_haste/
return 100. * (1. / (1. - GetUnitCooldownReductionFlat(u)) - 1.0)
elseif bonus_type == BONUS_SPELL_POWER_MULTIPLIER then
return GetSpellPowerMultiplier(u)
elseif bonus_type == BONUS_ARMOR_PEN_FLAT then
return ArmorPenetrationFlat.Get(u)
elseif bonus_type == BONUS_ARMOR_PEN_PERCENT then
return ArmorPenetrationPercent.Get(u)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1.
endmethod
static method SetR takes unit u, integer bonus_type, real amount returns real
if bonus_type == BONUS_HEALTH_REGEN then
call SetUnitAbilityBonusR(u, HEALTHREGEN_ABILITY, HEALTHREGEN_FIELD, amount)
elseif bonus_type == BONUS_MANA_REGEN then
call SetUnitAbilityBonusR(u, MANAREGEN_ABILITY, MANAREGEN_FIELD, amount)
elseif bonus_type == BONUS_ATTACK_SPEED then
call SetUnitAbilityBonusR(u, ATTACKSPEED_ABILITY, ATTACKSPEED_FIELD, amount)
elseif bonus_type == BONUS_MAGIC_RESISTANCE then
call SetUnitAbilityBonusR(u, MAGIC_RESISTANCE_ABILITY, MAGIC_RESISTANCE_FIELD, amount)
elseif bonus_type == BONUS_CRITICAL_CHANCE then
call SetCriticalHitChance(u, amount)
elseif bonus_type == BONUS_CRITICAL_DAMAGE then
call SetCriticalHitDamage(u, amount)
elseif bonus_type == BONUS_EVASION_CHANCE then
call SetUnitAbilityBonusR(u, EVASION_ABILITY, EVASION_FIELD, amount)
elseif bonus_type == BONUS_LIFE_STEAL then
call SetUnitAbilityBonusR(u, LIFE_STEAL_ABILITY, LIFE_STEAL_FIELD, amount)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION then
call SetUnitCooldownReduction(u, amount)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION_FLAT then
call SetUnitCooldownReductionFlat(u, amount)
elseif bonus_type == BONUS_COOLDOWN_OFFSET then
call SetUnitCooldownOffset(u, amount)
elseif bonus_type == BONUS_SPELL_HASTE then
//Lol Formula for AH
//https://www.reddit.com/r/leagueoflegends/comments/i5m8m6/i_made_a_chart_to_convert_cdr_to_ability_haste/
call SetUnitCooldownReductionFlat(u, 1. - 1. / (1. + amount / 100.))
elseif bonus_type == BONUS_SPELL_POWER_MULTIPLIER then
call SetSpellPowerMultiplier(u, amount)
elseif bonus_type == BONUS_ARMOR_PEN_FLAT then
return ArmorPenetrationFlat.Set(u, amount)
elseif bonus_type == BONUS_ARMOR_PEN_PERCENT then
return ArmorPenetrationPercent.Set(u, amount)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return amount
endmethod
static method AddR takes unit u, integer bonus_type, real amount returns real
if bonus_type >= BONUS_HEALTH_REGEN and bonus_type <= BONUS_ARMOR_PEN_PERCENT then
//if bonus_type == BONUS_SPELL_HASTE then
// call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Spell haste before=" + R2SW(GetR(u, bonus_type),0,1) + ", adding amount=" + R2SW(amount,0,1))
//endif
call SetR(u, bonus_type, GetR(u, bonus_type) + amount)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return amount
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* --------------------------------------------------------------------------
*/
function GetUnitBonus takes unit u, integer bonus_type returns real
if bonus_type < BONUS_HEALTH_REGEN then
return I2R(NewBonus.Get(u, bonus_type))
else
return NewBonus.GetR(u, bonus_type)
endif
endfunction
function SetUnitBonus takes unit u, integer bonus_type, real amount returns real
if bonus_type < BONUS_HEALTH_REGEN then
return I2R(NewBonus.Set(u, bonus_type, R2I(amount)))
else
return NewBonus.SetR(u, bonus_type, amount)
endif
endfunction
function RemoveUnitBonus takes unit u, integer bonus_type returns nothing
if bonus_type < BONUS_HEALTH_REGEN then
call NewBonus.Set(u, bonus_type, 0)
else
if bonus_type == BONUS_CRITICAL_DAMAGE then
call NewBonus.SetR(u, bonus_type, 1)
else
call NewBonus.SetR(u, bonus_type, 0)
endif
if bonus_type == BONUS_LIFE_STEAL then
call UnitRemoveAbility(u, LIFE_STEAL_ABILITY)
endif
endif
endfunction
function AddUnitBonus takes unit u, integer bonus_type, real amount returns real
if bonus_type < BONUS_HEALTH_REGEN then
return I2R(NewBonus.Add(u, bonus_type, R2I(amount)))
else
return NewBonus.AddR(u, bonus_type, amount)
endif
endfunction
endlibrary
//TESH.scrollpos=205
//TESH.alwaysfold=0
function K2DItemCheckXY takes real x, real y returns boolean
call SetItemPosition(udg_K2DItem, x, y)
return GetWidgetX(udg_K2DItem) == x and GetWidgetY(udg_K2DItem) == y
endfunction
function K2DItemCheckAxis takes real x, real y returns boolean
local real x2 = x*udg_K2DRadius[udg_UDex]
local real y2 = y*udg_K2DRadius[udg_UDex]
set x = udg_K2DX + x2
set y = udg_K2DY + y2
if K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
set x = udg_K2DX - x2
set y = udg_K2DY - y2
return K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endif
return false
endfunction
function K2DItemCheck takes nothing returns boolean
local boolean result = K2DItemCheckXY(udg_K2DX, udg_K2DY)
//Only perform additional pathing checks if the unit has a larger collision.
if result and udg_Knockback2DRobustPathing > 0 and udg_K2DRadius[udg_UDex] > 0 then
//Check horizontal axis of unit to make sure nothing is going to collide
set result = K2DItemCheckAxis(udg_K2DCosH[udg_UDex], udg_K2DSinH[udg_UDex])
//Check vertical axis of unit to ensure nothing will collide
set result = result and K2DItemCheckAxis(udg_K2DCos[udg_UDex], udg_K2DSin[udg_UDex])
if result and udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
//Check diagonal axis of unit if more thorough pathing is desired
set result = K2DItemCheckAxis(udg_K2DCosD1[udg_UDex], udg_K2DSinD1[udg_UDex])
set result = result and K2DItemCheckAxis(udg_K2DCosD2[udg_UDex], udg_K2DSinD2[udg_UDex])
endif
endif
//Reset item so it won't interfere with the map
call SetItemPosition(udg_K2DItem, udg_K2DMaxX, udg_K2DMaxY)
call SetItemVisible(udg_K2DItem, false)
return result
endfunction
function K2DItemFilter takes nothing returns boolean
//Check for visible items, temporarily hide them and add them to the filter.
if IsItemVisible(GetFilterItem()) then
call SetItemVisible(GetFilterItem(), false)
return true
endif
return false
endfunction
function K2DItemCode takes nothing returns nothing
//Perform the item-pathing check only once, then unhide those filtered items
if not udg_K2DItemsFound then
set udg_K2DItemsFound = true
set udg_K2DItemOffset = K2DItemCheck()
endif
call SetItemVisible(GetEnumItem(), true)
endfunction
function K2DKillDest takes nothing returns nothing
local real x
local real y
//Handle destruction of debris
set bj_destRandomCurrentPick = GetEnumDestructable()
if GetWidgetLife(bj_destRandomCurrentPick) > 0.405 and IssueTargetOrder(udg_K2DDebrisKiller, udg_Knockback2DTreeOrDebris, bj_destRandomCurrentPick) then
set x = GetWidgetX(bj_destRandomCurrentPick) - udg_K2DX
set y = GetWidgetY(bj_destRandomCurrentPick) - udg_K2DY
if x*x + y*y <= udg_K2DDestRadius[udg_UDex] then
call KillDestructable(bj_destRandomCurrentPick)
endif
endif
endfunction
function K2DEnumDests takes nothing returns nothing
call MoveRectTo(udg_K2DRegion, udg_K2DX, udg_K2DY)
if udg_K2DKillTrees[udg_UDex] then
call SetUnitX(udg_K2DDebrisKiller, udg_K2DX)
call SetUnitY(udg_K2DDebrisKiller, udg_K2DY)
call EnumDestructablesInRect(udg_K2DRegion, null, function K2DKillDest)
endif
endfunction
function Knockback2DCheckXY takes real x, real y returns boolean
set udg_K2DX = x + udg_K2DVelocity[udg_UDex]*udg_K2DCos[udg_UDex]
set udg_K2DY = y + udg_K2DVelocity[udg_UDex]*udg_K2DSin[udg_UDex]
if udg_K2DSimple[udg_UDex] then
//A "pull" effect or a missile system does not require complex pathing.
if udg_K2DX <= udg_K2DMaxX and udg_K2DX >= udg_K2DMinX and udg_K2DY <= udg_K2DMaxY and udg_K2DY >= udg_K2DMinY then
call K2DEnumDests()
return true
endif
return false
elseif udg_K2DFlying[udg_UDex] then
return not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLYABILITY)
elseif not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_WALKABILITY) then
call K2DEnumDests()
set udg_K2DItemOffset = false
call EnumItemsInRect(udg_K2DRegion, Filter(function K2DItemFilter), function K2DItemCode)
if udg_K2DItemsFound then
//If items were found, the check was already performed.
set udg_K2DItemsFound = false
else
//Otherwise, perform the check right now.
set udg_K2DItemOffset = K2DItemCheck()
endif
return udg_K2DItemOffset
endif
return udg_K2DAmphibious[udg_UDex] and not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLOATABILITY)
endfunction
function Knockback2DApplyAngle takes real angle returns nothing
set angle = ModuloReal(angle, udg_Radians_Turn)
set udg_K2DCos[udg_UDex] = Cos(angle)
set udg_K2DSin[udg_UDex] = Sin(angle)
set udg_K2DAngle[udg_UDex] = angle
if udg_Knockback2DRobustPathing > 0 then
set angle = ModuloReal(angle + udg_Radians_QuarterTurn, udg_Radians_Turn)
set udg_K2DCosH[udg_UDex] = Cos(angle)
set udg_K2DSinH[udg_UDex] = Sin(angle)
if udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
set angle = ModuloReal(angle + udg_Radians_QuarterPi, udg_Radians_Turn)
set udg_K2DCosD1[udg_UDex] = Cos(angle)
set udg_K2DSinD1[udg_UDex] = Sin(angle)
set angle = ModuloReal(angle + udg_Radians_QuarterTurn, udg_Radians_Turn)
set udg_K2DCosD2[udg_UDex] = Cos(angle)
set udg_K2DSinD2[udg_UDex] = Sin(angle)
endif
endif
endfunction
function Knockback2DLooper takes nothing returns nothing
local integer i = 0
local unit u
local real x
local real y
call PauseUnit(udg_K2DDebrisKiller, false)
loop
set i = udg_K2DNext[i]
exitwhen i == 0
set udg_UDex = i
set udg_K2DTimeLeft[i] = udg_K2DTimeLeft[i] - udg_K2DTimeout
set udg_K2DDistanceLeft[i] = udg_K2DDistanceLeft[i] - udg_K2DVelocity[i]
set u = udg_UDexUnits[i]
if udg_K2DTimeLeft[i] > 0.00 then
if udg_K2DTimeLeft[i] < udg_K2DHeightThreshold[i] and udg_K2DHeightThreshold[i] != 0.00 then
call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u), GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)/udg_K2DHeightThreshold[i])
set udg_K2DHeightThreshold[i] = 0.00
endif
if udg_K2DPause[i] then
set x = udg_K2DLastX[i]
set y = udg_K2DLastY[i]
else
set x = GetUnitX(u)
set y = GetUnitY(u)
endif
if not Knockback2DCheckXY(x, y) then
if not udg_K2DFreeze[i] and IsTriggerEnabled(udg_K2DImpact[i]) and TriggerEvaluate(udg_K2DImpact[i]) then
call TriggerExecute(udg_K2DImpact[i])
endif
if udg_K2DBounce[i] then
call Knockback2DApplyAngle(udg_Radians_Turn - udg_K2DAngle[i])
if not Knockback2DCheckXY(x, y) then
call Knockback2DApplyAngle(udg_K2DAngle[i] + bj_PI)
if not Knockback2DCheckXY(x, y) then
call Knockback2DApplyAngle(udg_Radians_Turn - udg_K2DAngle[i])
set udg_K2DX = x
set udg_K2DY = y
endif
endif
else
set udg_K2DX = x
set udg_K2DY = y
set udg_K2DFreeze[i] = true
endif
endif
call SetUnitX(u, udg_K2DX)
call SetUnitY(u, udg_K2DY)
set udg_K2DLastX[i] = udg_K2DX
set udg_K2DLastY[i] = udg_K2DY
if udg_K2DFXModel[i] != "" then
set udg_K2DFXTimeLeft[i] = udg_K2DFXTimeLeft[i] - udg_K2DTimeout
if udg_K2DFXTimeLeft[i] <= 0.00 then
set udg_K2DFXTimeLeft[i] = udg_K2DFXRate[i]
if udg_K2DFlying[i] then
call DestroyEffect(AddSpecialEffectTarget(udg_K2DFXModel[i], u, "origin"))
else
call DestroyEffect(AddSpecialEffect(udg_K2DFXModel[i], udg_K2DX, udg_K2DY))
endif
endif
endif
if udg_K2DCollision[i] >= 0.00 then
set udg_Knockback2DSource = u
call GroupEnumUnitsInRange(bj_lastCreatedGroup, udg_K2DX, udg_K2DY, 200.00, null)
call GroupRemoveUnit(bj_lastCreatedGroup, u)
loop
set udg_Knockback2DUnit = FirstOfGroup(bj_lastCreatedGroup)
exitwhen udg_Knockback2DUnit == null
call GroupRemoveUnit(bj_lastCreatedGroup, udg_Knockback2DUnit)
if IsUnitInRange(udg_Knockback2DUnit, u, udg_K2DCollision[i]) and udg_K2DFlying[i] == IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_FLYING) and (not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_STRUCTURE)) and not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_DEAD) and (udg_K2DUnbiasedCollision[i] or IsUnitAlly(udg_Knockback2DUnit, GetOwningPlayer(u))) and TriggerEvaluate(gg_trg_Knockback_2D) then
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(GetUnitY(udg_Knockback2DUnit) - udg_K2DY, GetUnitX(udg_Knockback2DUnit) - udg_K2DX)
set udg_Knockback2DDistance = udg_K2DDistanceLeft[i]
set udg_Knockback2DBounces = udg_K2DBounce[i]
set udg_Knockback2DCollision = udg_K2DCollision[i]
if udg_K2DHeight[i] != 0.00 then
set udg_Knockback2DHeight = GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)
endif
set udg_Knockback2DLoopFX = udg_K2DFXModel[i]
set udg_Knockback2DTime = udg_K2DTimeLeft[i]
set udg_Knockback2DUnbiasedCollision = udg_K2DUnbiasedCollision[i]
call TriggerExecute(gg_trg_Knockback_2D)
set udg_Knockback2DSource = u //in case of a recursive knockback
endif
endloop
endif
set udg_K2DVelocity[i] = udg_K2DVelocity[i] - udg_K2DFriction[i]
else
call TriggerExecute(gg_trg_Knockback_2D_Destroy)
endif
endloop
set u = null
//Disable dummy after the loop finishes so it doesn't interfere with the map
call PauseUnit(udg_K2DDebrisKiller, true)
endfunction
//===========================================================================
function StartKnockback2DTimer takes nothing returns nothing
call TimerStart(udg_K2DTimer, udg_K2DTimeout, true, function Knockback2DLooper)
endfunction
function InitTrig_Knockback_2D_System takes nothing returns nothing
endfunction
library CooldownReduction requires RegisterPlayerUnitEvent, Table, Alloc
/* ------------------ Cooldown Reduction v1.9 by Chopinski ------------------ */
// Intro
// This library intension in to introduce to warcraft an easy way to
// manipulate abilities cooldowns based on a cooldown reduction value that
// is unique for each unit.
// How it Works?
// When casting an ability, its "new" cooldown is calculated based on the
// amount of cooldown reduction of the casting unit. the formula for
// calculation is:
// Cooldown = (Default Cooldown - Cooldown Offset) * [(1 - source1)*(1 - source2)*...] * (1 - Cooldown Reduction Flat)
// The system also allow negative values for CDR, resulting in increased
// ability cooldown.
// It does not acumulate because the abilities are registered automatically
// on the first cast, saving its base cooldown (Object Editor values) and
// always using this base value for calculation, so you can still edit
// the ability via the editor and the system takes care of the rest.
// How to Import
// simply copy the CooldownReduction folder over to your map, and start
// use the API functions
// Requirements
// CooldownReduction requires RegisterPlayerUnitEvent, Alloc and a Unit Indexer.
// Credits to Magtheridon96 for RegisterPlayerUnitEvent and to Bribe for
// the UnitIndexer. It also requires patch 1.31+.
// RegisterPlayerUnitEvent: www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/
// UnitIndexer: www.hiveworkshop.com/threads/gui-unit-indexer-1-4-0-0.197329/#resource-45899
// Alloc: www.hiveworkshop.com/threads/snippet-alloc.192348/
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* Configuration */
/* --------------------------------------------------------------------------
*/
// Use this function to filter out units you dont want to have abilities registered.
// By default dummy units do not trigger the system.
private function UnitFilter takes unit source returns boolean
return GetUnitAbilityLevel(source, 'Aloc') == 0
endfunction
private function AbilityFilter takes integer abilityId returns boolean
return abilityId != 'A04I'
endfunction
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
private module List
readonly thistype next
readonly thistype prev
method init takes nothing returns thistype
set next = this
set prev = this
return this
endmethod
method push takes thistype node returns thistype
set node.prev = prev
set node.next = this
set prev.next = node
set prev = node
return node
endmethod
method pop takes nothing returns nothing
set prev.next = next
set next.prev = prev
endmethod
endmodule
private struct AbilityList extends array
implement Alloc
implement List
unit unit
ability ability
Table defaults
integer id
integer levels
method destroy takes nothing returns nothing
local thistype node = this.next
loop
exitwhen node == this
set node.ability = null
call node.defaults.destroy()
call node.pop()
call node.deallocate()
set node = node.next
endloop
call deallocate()
set unit = null
endmethod
method insert takes integer id returns thistype
local thistype node = push(allocate())
local integer i = 0
set node.id = id
set node.ability = BlzGetUnitAbility(unit, id)
set node.levels = BlzGetAbilityIntegerField(node.ability, ABILITY_IF_LEVELS)
set node.defaults = Table.create()
loop
exitwhen i >= node.levels
set node.defaults.real[i] = BlzGetAbilityRealLevelField(node.ability, ABILITY_RLF_COOLDOWN, i)
set i = i + 1
endloop
return node
endmethod
method update takes integer count, real normal, real flat, real offset returns nothing
local thistype node = this.next
local real cooldown
local integer i
loop
exitwhen node == this
set i = 0
loop
exitwhen i >= node.levels
if count > 0 then
set cooldown = ((node.defaults.real[i] - offset) * normal * (1 - flat))
else
set cooldown = ((node.defaults.real[i] - offset) * (1 - flat))
endif
call BlzSetAbilityRealLevelField(node.ability, ABILITY_RLF_COOLDOWN, i, cooldown)
call IncUnitAbilityLevel(unit, node.id)
call DecUnitAbilityLevel(unit, node.id)
set i = i + 1
endloop
set node = node.next
endloop
endmethod
method addDefaultCDR takes integer abilityId, real amount returns nothing
local thistype node = this.next
local integer i
loop
exitwhen node == this
if node.id == abilityId then
set i = 0
loop
exitwhen i >= node.levels
set node.defaults.real[i] = node.defaults.real[i] + amount
set i = i + 1
endloop
endif
set node = node.next
endloop
endmethod
method calculate takes integer id, integer level, real cooldown, integer count, real normal, real flat, real offset returns nothing
if count > 0 then
call BlzSetAbilityRealLevelField(BlzGetUnitAbility(unit, id), ABILITY_RLF_COOLDOWN, level, ((cooldown - offset) * normal * (1 - flat)))
else
call BlzSetAbilityRealLevelField(BlzGetUnitAbility(unit, id), ABILITY_RLF_COOLDOWN, level, ((cooldown - offset) * (1 - flat)))
endif
call IncUnitAbilityLevel(unit, id)
call DecUnitAbilityLevel(unit, id)
endmethod
method simulate takes real cooldown, integer count, real normal, real flat, real offset returns real
local real cd
if count > 0 then
set cd = ((cooldown - offset) * normal * (1 - flat))
else
set cd = ((cooldown - offset) * (1 - flat))
endif
return cd
endmethod
static method create takes unit source returns thistype
local thistype this = thistype(allocate()).init()
set unit = source
return this
endmethod
endstruct
struct CDR
readonly static hashtable hashtable = InitHashtable()
private static AbilityList array n
private static integer array count
readonly static real array normal
readonly static real array flat
readonly static real array offset
static method getInstance takes unit source returns AbilityList
local integer i = GetUnitUserData(source)
if n[i] == 0 then
set n[i] = AbilityList.create(source)
endif
return n[i]
endmethod
static method update takes unit u returns nothing
local integer id = GetUnitUserData(u)
local AbilityList list = getInstance(u)
call list.update(count[id], normal[id], flat[id], offset[id])
endmethod
private static method calculate takes unit u returns real
local integer idx = GetUnitUserData(u)
local integer id = GetHandleId(u)
local integer i = 0
local real cdr = 0
local real aux
loop
exitwhen i > count[idx]
set aux = LoadReal(hashtable, id, i)
if i > 0 then
set cdr = cdr * (1 - aux)
else
set cdr = 1 - aux
endif
set i = i + 1
endloop
return cdr
endmethod
static method get takes unit u, integer types returns real
if types == 0 then
return normal[GetUnitUserData(u)]
elseif types == 1 then
return flat[GetUnitUserData(u)]
else
return offset[GetUnitUserData(u)]
endif
endmethod
static method Set takes unit u, real value, integer types returns nothing
if types == 0 then
set normal[GetUnitUserData(u)] = value
elseif types == 1 then
set flat[GetUnitUserData(u)] = value
else
set offset[GetUnitUserData(u)] = value
endif
call update(u)
endmethod
static method add takes unit u, real amount returns nothing
local integer i = GetUnitUserData(u)
if amount != 0 then
call SaveReal(hashtable, GetHandleId(u), count[i], amount)
set normal[i] = calculate(u)
set count[i] = count[i] + 1
call update(u)
endif
endmethod
static method remove takes unit u, real amount returns nothing
local integer idx = GetUnitUserData(u)
local integer id = GetHandleId(u)
local integer i = 0
local real aux
if amount == 0 then
return
endif
loop
exitwhen i > count[idx] - 1
set aux = LoadReal(hashtable, id, i)
if aux == amount then
call RemoveSavedReal(hashtable, id, i)
if i != count[idx] - 1 then
set aux = LoadReal(hashtable, id, count[idx] - 1)
call SaveReal(hashtable, id, i, aux)
call RemoveSavedReal(hashtable, id, count[idx] - 1)
endif
set count[idx] = count[idx] - 1
set normal[idx] = calculate(u)
set i = count[idx] + 1
call update(u)
else
set i = i + 1
endif
endloop
endmethod
static method calculateCooldown takes unit u, integer id, integer level, real cooldown returns nothing
local integer i = GetUnitUserData(u)
local AbilityList list = getInstance(u)
call list.calculate(id, level - 1, cooldown, count[i], normal[i], flat[i], offset[i])
endmethod
static method simulateCooldown takes unit u, real cooldown returns real
local integer i = GetUnitUserData(u)
local AbilityList list = getInstance(u)
return list.simulate(cooldown, count[i], normal[i], flat[i], offset[i])
endmethod
static method register takes unit u, integer id returns nothing
local AbilityList list
local integer i
if UnitFilter(u) and AbilityFilter(id) then
set list = getInstance(u)
set i = GetUnitUserData(u)
if not LoadBoolean(hashtable, list, id) then
call list.insert(id)
call SaveBoolean(hashtable, list, id, true)
if count[i] > 0 or normal[i] != 0 or flat[i] != 0 or offset[i] != 0 then
call update(u)
endif
endif
endif
endmethod
private static method onCast takes nothing returns nothing
call register(GetTriggerUnit(), GetSpellAbilityId())
endmethod
private static method onLevel takes nothing returns nothing
call register(GetTriggerUnit(), GetLearnedSkill())
endmethod
private static method onDeindex takes nothing returns nothing
local unit source = udg_UDexUnits[udg_UDex]
local integer i = GetUnitUserData(source)
local AbilityList list = getInstance(source)
set n[i] = 0
set normal[i] = 0
set flat[i] = 0
set offset[i] = 0
set count[i] = 0
if list != 0 then
call list.destroy()
call FlushChildHashtable(hashtable, list)
endif
call FlushChildHashtable(hashtable, GetHandleId(source))
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Condition(function thistype.onDeindex))
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_SKILL, function thistype.onLevel)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* --------------------------------------------------------------------------
*/
function GetUnitCooldownReduction takes unit u returns real
return 1 - CDR.get(u, 0)
endfunction
function GetUnitCooldownReductionFlat takes unit u returns real
return CDR.get(u, 1)
endfunction
function GetUnitCooldownOffset takes unit u returns real
return CDR.get(u, 2)
endfunction
function SetUnitCooldownReduction takes unit u, real value returns nothing
call CDR.Set(u, value, 0)
endfunction
function SetUnitCooldownReductionFlat takes unit u, real value returns nothing
call CDR.Set(u, value, 1)
endfunction
function SetUnitCooldownOffset takes unit u, real value returns nothing
call CDR.Set(u, value, 2)
endfunction
function UnitAddCooldownReduction takes unit u, real value returns nothing
call CDR.add(u, value)
endfunction
function UnitAddCooldownReductionFlat takes unit u, real value returns nothing
call CDR.Set(u, CDR.get(u, 1) + value, 1)
endfunction
function UnitAddCooldownOffset takes unit u, real value returns nothing
call CDR.Set(u, CDR.get(u, 2) + value, 2)
endfunction
function UnitRemoveCooldownReduction takes unit u, real value returns nothing
call CDR.remove(u, value)
endfunction
function CalculateAbilityCooldown takes unit u, integer id, integer level, real cooldown returns nothing
call CDR.calculateCooldown(u, id, level, cooldown)
endfunction
function SimulateAbilityCooldown takes unit u, real cooldown returns real
return CDR.simulateCooldown(u, cooldown)
endfunction
function RegisterAbility takes unit u, integer id returns nothing
call CDR.register(u, id)
endfunction
function ModifyCDRDefaultCooldown takes unit u, integer abilityId, real cdModifierSeconds returns nothing
call CDR.getInstance(u).addDefaultCDR(abilityId, cdModifierSeconds)
call CDR.update(u)
endfunction
endlibrary
library CooldownReductionUtils requires CooldownReduction
/* --------------- Cooldown Reduction Utils v1.8 by Chopinski --------------- */
// Intro
// Utility Library that include a few extra functions to deal with
// Cooldown Reduction
// JASS API
// function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
// -> Add to the amount of cdr of a unit for a given duration. Accepts positive and negative values.
// -> It handles removing the bonus automatically
// function UnitAddCooldownReductionFlatTimed takes unit u, real value, real duration returns nothing
// -> Add to the amount of cdr flat of a unit for a given period. Accepts positive and negative values.
// -> It handles removing the bonus automatically
// function UnitAddCooldownOffsetTimed takes unit u, real value, real duration returns nothing
// -> Add to the amount of cdr offset of a unit for a given period. Accepts positive and negative values.
// -> It handles removing the bonus automatically
// function GetUnitCooldownReductionEx takes unit u returns string
// -> Returns the amount of cdr a unit has as a string factored by 100
// -> example of return: 10.50 -> 0.105 internally.
// function GetUnitCooldownReductionFlatEx takes unit u returns string
// -> Returns the amount of cdr flat a unit has as a string factored by 100
// -> example of return: 10.50 -> 0.105 internally.
// function GetUnitCooldownOffsetEx takes unit u returns string
// -> Returns the amount of cdr offset a unit has as a string
// function GetAbilityTable takes nothing returns hashtable
// -> Returns the hashtable that holds the units default cooldown reduction values.
// -> Use with caution! you might break stuff
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private struct CDRUtils extends CDR
static timer t = CreateTimer()
//----------------------------------------------
static integer didx = -1
static thistype array data
//----------------------------------------------
unit u
real ticks
real amount
integer tipo
method destroy takes nothing returns nothing
if didx == -1 then
call PauseTimer(t)
endif
set .u = null
set .ticks = 0
call .deallocate()
endmethod
private static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
set .ticks = .ticks - 1
if .ticks <= 0 then
if .tipo == 0 then
call remove(.u, .amount)
elseif .tipo == 1 then
call Set(.u, get(.u, 1) - .amount, 1)
else
call Set(.u, get(.u, 2) - .amount, 2)
endif
set data[i] = data[didx]
set didx = didx - 1
set i = i - 1
call .destroy()
endif
set i = i + 1
endloop
endmethod
static method addTimed takes unit u, real amount, real duration, integer tipo returns nothing
local thistype this = thistype.allocate()
set .u = u
set .amount = amount
set .tipo = tipo
set .ticks = duration/0.03125000
set didx = didx + 1
set data[didx] = this
if tipo == 0 then
call add(u, amount)
elseif tipo == 1 then
call Set(u, get(u, 1) + amount, 1)
else
call Set(u, get(u, 2) + amount, 2)
endif
if didx == 0 then
call TimerStart(t, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* -------------------------------------------------------------------------- */
function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
call CDRUtils.addTimed(u, value, duration, 0)
endfunction
function UnitAddCooldownReductionFlatTimed takes unit u, real value, real duration returns nothing
call CDRUtils.addTimed(u, value, duration, 1)
endfunction
function UnitAddCooldownOffsetTimed takes unit u, real value, real duration returns nothing
call CDRUtils.addTimed(u, value, duration, 2)
endfunction
function GetUnitCooldownReductionEx takes unit u returns string
return R2SW(CDRUtils.get(u, 0)*100, 1, 2)
endfunction
function GetUnitCooldownReductionFlatEx takes unit u returns string
return R2SW(CDRUtils.get(u, 1)*100, 1, 2)
endfunction
function GetUnitCooldownOffsetEx takes unit u returns string
return R2SW(CDRUtils.get(u, 2), 1, 2)
endfunction
function GetAbilityTable takes nothing returns hashtable
return CDR.hashtable
endfunction
endlibrary
scope MissilesW
private struct Missile extends Missiles
method onHit takes unit hit returns boolean
if hit != .source and UnitAlive(hit) then
call KillUnit(hit)
endif
return false
endmethod
method onDestructable takes destructable dest returns boolean
call KillDestructable(dest)
return false
endmethod
endstruct
private struct Test
timer t
unit c
real fx
real fy
real fz
real tx
real ty
integer count
integer i = 1
private static method GetRandomRange takes real radius returns real
local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
if r > 1 then
return (2 - r)*radius
endif
return r*radius
endmethod
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real maxRange
local real theta
local real radius
local real toX
local real toY
local Missile missile
if count > 0 then
set i = -i
set count = count - 1
set theta = 2*bj_PI*GetRandomReal(0, 1)
set radius = GetRandomRange(350)
set toX = tx + radius*Cos(theta)
set toY = ty + radius*Sin(theta)
set fx = GetUnitX(c)
set fy = GetUnitY(c)
set missile = Missile.create(fx, fy, fz, toX, toY, 0)
set missile.source = c
set missile.model = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
set missile.speed = 800
set missile.collision = 75
set missile.arc = GetRandomReal(40, 45)
set missile.curve = GetRandomReal(5, 20)*i
call missile.launch()
else
call ReleaseTimer(t)
set t = null
set c = null
call deallocate()
endif
endmethod
static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
set t = NewTimerEx(this)
set c = GetTriggerUnit()
set fx = GetUnitX(c)
set fy = GetUnitY(c)
set fz = GetUnitFlyHeight(c) + 50
set tx = GetSpellTargetX()
set ty = GetSpellTargetY()
set count = 50
call TimerStart(.t, 0.1, true, function thistype.onLoop)
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A000', function thistype.onCast)
endmethod
endstruct
endscope
library Missiles requires MissileEffect, TimerUtils, WorldBounds
/* ----------------------- Missiles v2.6 by Chopinski ----------------------- */
// Thanks and Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
// this Missiles library. Credits and thanks to AGD and for the effect orientation ideas.
// This version of Missiles requires patch 1.31+
// How to Import:
// 1 - Copy this, MissileEffect and optionaly the MissileUtils libraries to your map
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
globals
// The update period of the system
public constant real PERIOD = 1./40.
// The max amount of Missiles processed in a PERIOD
// You can play around with both these values to find
// your sweet spot. If equal to 0, the system will
// process all missiles at once every period.
public constant real SWEET_SPOT = 150
// the avarage collision size compensation when detecting collisions
private constant real COLLISION_SIZE = 128.
// item size used in z collision
private constant real ITEM_SIZE = 16.
// Raw code of the dummy unit used for vision
private constant integer DUMMY = 'dumi'
// Needed, don't touch.
private location LOC = Location(0., 0.)
endglobals
private interface MissileEvents
method onHit takes unit hit returns boolean defaults false
method onMissile takes Missiles missile returns boolean defaults false
method onDestructable takes destructable dest returns boolean defaults false
method onItem takes item i returns boolean defaults false
method onCliff takes nothing returns boolean defaults false
method onTerrain takes nothing returns boolean defaults false
method onTileset takes integer tileset returns boolean defaults false
method onPeriod takes nothing returns boolean defaults false
method onFinish takes nothing returns boolean defaults false
method onBoundaries takes nothing returns boolean defaults false
method onPause takes nothing returns boolean defaults false
method onResume takes nothing returns boolean defaults false
method onRemove takes nothing returns nothing defaults nothing
endinterface
private function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
private function GetUnitZ takes unit u returns real
return GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
endfunction
private function SetUnitZ takes unit u, real z returns nothing
call SetUnitFlyHeight(u, z - GetLocZ(GetUnitX(u), GetUnitY(u)), 0)
endfunction
private function GetMapCliffLevel takes nothing returns integer
return GetTerrainCliffLevel(WorldBounds.maxX, WorldBounds.maxY)
endfunction
private struct Pool
private static player player = Player(PLAYER_NEUTRAL_PASSIVE)
private static group group = CreateGroup()
timer timer
unit unit
static method recycle takes unit dummy returns nothing
if GetUnitTypeId(dummy) == DUMMY then
call GroupAddUnit(group, dummy)
call SetUnitX(dummy, WorldBounds.maxX)
call SetUnitY(dummy, WorldBounds.maxY)
call SetUnitOwner(dummy, player, false)
call PauseUnit(dummy, true)
endif
endmethod
static method retrieve takes real x, real y, real z, real face returns unit
if BlzGroupGetSize(group) > 0 then
set bj_lastCreatedUnit = FirstOfGroup(group)
call PauseUnit(bj_lastCreatedUnit, false)
call GroupRemoveUnit(group, bj_lastCreatedUnit)
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitZ(bj_lastCreatedUnit, z)
//call BlzSetUnitFacingEx(bj_lastCreatedUnit, face)
else
set bj_lastCreatedUnit = CreateUnit(player, DUMMY, x, y, face)
call SetUnitZ(bj_lastCreatedUnit, z)
call UnitRemoveAbility(bj_lastCreatedUnit, 'Amrf')
endif
return bj_lastCreatedUnit
endmethod
private static method onExpire takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call recycle(unit)
call ReleaseTimer(timer)
set timer = null
set unit = null
call deallocate()
endmethod
static method recycleTimed takes unit dummy, real delay returns nothing
local thistype this
if GetUnitTypeId(dummy) != DUMMY then
debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
else
set this = thistype.allocate()
set timer = NewTimerEx(this)
set unit = dummy
call TimerStart(timer, delay, false, function thistype.onExpire)
endif
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
local unit u
loop
exitwhen i == SWEET_SPOT
set u = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
call PauseUnit(u, false)
call GroupAddUnit(group, u)
call UnitRemoveAbility(u, 'Amrf')
set i = i + 1
endloop
set u = null
endmethod
endstruct
private struct Coordinates
readonly real x
readonly real y
readonly real z
readonly real angle
readonly real distance
readonly real square
readonly real slope
readonly real alpha
// Creates an origin - impact link.
private thistype ref
private static method math takes thistype a, thistype b returns nothing
local real dx
local real dy
loop
set dx = b.x - a.x
set dy = b.y - a.y
set dx = dx*dx + dy*dy
set dy = SquareRoot(dx)
exitwhen dx != 0. and dy != 0.
set b.x = b.x + .01
set b.z = b.z - GetLocZ(b.x -.01, b.y) + GetLocZ(b.x, b.y)
endloop
set a.square = dx
set a.distance = dy
set a.angle = Atan2(b.y - a.y, b.x - a.x)
set a.slope = (b.z - a.z)/dy
set a.alpha = Atan(a.slope)
// Set b.
if b.ref == a then
set b.angle = a.angle + bj_PI
set b.distance = dy
set b.slope = -a.slope
set b.alpha = -a.alpha
set b.square = dx
endif
endmethod
static method link takes thistype a, thistype b returns nothing
set a.ref = b
set b.ref = a
call math(a, b)
endmethod
method move takes real toX, real toY, real toZ returns nothing
set x = toX
set y = toY
set z = toZ + GetLocZ(toX, toY)
if ref != this then
call math(this, ref)
endif
endmethod
method destroy takes nothing returns nothing
call .deallocate()
endmethod
static method create takes real x, real y, real z returns Coordinates
local thistype this = thistype.allocate()
set ref = this
call move(x, y, z)
return this
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private module OnHit
set o = origin
set h = height
set c = open
set d = o.distance
if .onHit.exists then
if allocated and collision > 0 then
call GroupEnumUnitsInRange(group, x, y, collision + COLLISION_SIZE, null)
loop
set u = FirstOfGroup(group)
exitwhen u == null
if not HaveSavedBoolean(table, this, GetHandleId(u)) then
if IsUnitInRangeXY(u, x, y, collision) then
if collideZ then
set dx = GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
set dy = BlzGetUnitCollisionSize(u)
if dx + dy >= z - collision and dx <= z + collision then
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
else
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
endif
endif
call GroupRemoveUnit(group, u)
endloop
endif
endif
endmodule
private module OnMissile
if .onMissile.exists then
if allocated and collision > 0 then
set k = 0
loop
exitwhen k > count
set missile = collection[k]
if missile != this then
if not HaveSavedBoolean(table, this, missile) then
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= collision then
call SaveBoolean(table, this, missile, true)
if allocated and .onMissile(missile) then
call terminate()
exitwhen true
endif
endif
endif
endif
set k = k + 1
endloop
endif
endif
endmodule
private module OnDestructable
if .onDestructable.exists then
if allocated and collision > 0 then
set dx = collision
call SetRect(rect, x - dx, y - dx, x + dx, y + dx)
call EnumDestructablesInRect(rect, null, function thistype.onDest)
endif
endif
endmodule
private module OnItem
if .onItem.exists then
if allocated and collision > 0 then
set dx = collision
call SetRect(rect, x - dx, y - dx, x + dx, y + dx)
call EnumItemsInRect(rect, null, function thistype.onItems)
endif
endif
endmodule
private module OnCliff
if .onCliff.exists then
set dx = GetTerrainCliffLevel(nextX, nextY)
set dy = GetTerrainCliffLevel(x, y)
if dy < dx and z < (dx - GetMapCliffLevel())*bj_CLIFFHEIGHT then
if allocated and .onCliff() then
call terminate()
endif
endif
endif
endmodule
private module OnTerrain
if .onTerrain.exists then
if GetLocZ(x, y) > z then
if allocated and .onTerrain() then
call terminate()
endif
endif
endif
endmodule
private module OnTileset
if .onTileset.exists then
set k = GetTerrainType(x, y)
if k != tileset then
if allocated and .onTileset(k) then
call terminate()
endif
endif
set tileset = k
endif
endmodule
private module OnPeriod
if .onPeriod.exists then
if allocated and .onPeriod() then
call terminate()
endif
endif
endmodule
private module OnOrient
// Homing or not
set u = target
if u != null and GetUnitTypeId(u) != 0 then
call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + toZ)
set dx = impact.x - nextX
set dy = impact.y - nextY
set a = Atan2(dy, dx)
set travel = o.distance - SquareRoot(dx*dx + dy*dy)
else
set a = o.angle
set target = null
endif
// turn rate
if turn != 0 and not (Cos(cA-a) >= Cos(turn)) then
if Sin(a-cA) >= 0 then
set cA = cA + turn
else
set cA = cA - turn
endif
else
set cA = a
endif
set vel = veloc*dilation
set yaw = cA
set s = travel + vel
set veloc = veloc + acceleration
set travel = s
set pitch = o.alpha
set prevX = x
set prevY = y
set prevZ = z
set x = nextX
set y = nextY
set z = nextZ
set nextX = x + vel*Cos(yaw)
set nextY = y + vel*Sin(yaw)
// arc calculation
if h != 0 or o.slope != 0 then
set nextZ = 4*h*s*(d-s)/(d*d) + o.slope*s + o.z
set pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))
endif
// curve calculation
if c != 0 then
set dx = 4*c*s*(d-s)/(d*d)
set a = yaw + bj_PI/2
set x = x + dx*Cos(a)
set y = y + dx*Sin(a)
set yaw = yaw + Atan(-((4*c)*(2*s - d))/(d*d))
endif
endmodule
private module OnFinish
if s >= d - 0.0001 then
set finished = true
if .onFinish.exists then
if allocated and .onFinish() then
call terminate()
//else
// if travel > 0 and not paused then
// call terminate()
// endif
endif
else
call terminate()
endif
else
if not roll then
call effect.orient(yaw, -pitch, 0)
else
call effect.orient(yaw, -pitch, Atan2(c, h))
endif
endif
endmodule
private module OnBoundaries
if not effect.move(x, y, z) then
if .onBoundaries.exists then
if allocated and .onBoundaries() then
call terminate()
endif
endif
else
if dummy != null then
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
endif
endif
endmodule
private module OnPause
set pid = pid + 1
set pkey = pid
set frozen[pid] = this
if .onPause.exists then
if allocated and .onPause() then
call terminate()
endif
endif
endmodule
private module OnResume
local thistype aux
set paused = flag
if not paused and pkey != -1 then
set id = id + 1
set missiles[id] = this
set aux = frozen[pid]
set aux.pkey = pkey
set frozen[pkey] = frozen[pid]
set pid = pid - 1
set pkey = -1
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if id == 0 then
call TimerStart(timer, PERIOD, true, function thistype.move)
endif
if .onResume.exists then
if allocated and .onResume() then
call terminate()
else
if finished then
call terminate()
endif
endif
else
if finished then
call terminate()
endif
endif
endif
endmodule
private module OnRemove
local thistype aux
if allocated and launched then
set allocated = false
if pkey != -1 then
set aux = frozen[pid]
set aux.pkey = pkey
set frozen[pkey] = frozen[pid]
set pid = pid - 1
set pkey = -1
endif
if .onRemove.exists then
call .onRemove()
endif
if dummy != null then
call Pool.recycle(dummy)
endif
set aux = collection[count]
set aux.index = index
set collection[index] = collection[count]
set count = count - 1
set index = -1
call origin.destroy()
call impact.destroy()
call effect.destroy()
call reset()
call FlushChildHashtable(table, this)
endif
endmodule
private module Operators
/* -------------------------- Model of the missile -------------------------- */
method operator model= takes string fx returns nothing
call DestroyEffect(effect.effect)
set effect.path = fx
set effect.effect = AddSpecialEffect(fx, origin.x, origin.y)
call BlzSetSpecialEffectZ(effect.effect, origin.z)
call BlzSetSpecialEffectYaw(effect.effect, cA)
endmethod
method operator model takes nothing returns string
return effect.path
endmethod
/* ----------------------------- Curved movement ---------------------------- */
method operator curve= takes real value returns nothing
set open = Tan(value*bj_DEGTORAD)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)*bj_RADTODEG
endmethod
/* ----------------------------- Arced Movement ----------------------------- */
method operator arc= takes real value returns nothing
set height = Tan(value*bj_DEGTORAD)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)*bj_RADTODEG
endmethod
/* ------------------------------ Effect scale ------------------------------ */
method operator scale= takes real value returns nothing
set effect.size = value
call effect.scale(effect.effect, value)
endmethod
method operator scale takes nothing returns real
return effect.size
endmethod
/* ------------------------------ Missile Speed ----------------------------- */
method operator speed= takes real newspeed returns nothing
local real d = origin.distance
local real s
local real vel
set veloc = newspeed*PERIOD
set vel = veloc*dilation
set s = travel + vel
set nextX = x + vel*Cos(cA)
set nextY = y + vel*Sin(cA)
if height != 0 or origin.slope != 0 then
set nextZ = 4*height*s*(d-s)/(d*d) + origin.slope*s + origin.z
set z = nextZ
endif
endmethod
method operator speed takes nothing returns real
return veloc/PERIOD
endmethod
/* ------------------------------- Flight Time ------------------------------ */
method operator duration= takes real flightTime returns nothing
local real d = origin.distance
local real s
local real vel
set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
set time = flightTime
set vel = veloc*dilation
set s = travel + vel
set nextX = x + vel*Cos(cA)
set nextY = y + vel*Sin(cA)
if height != 0 or origin.slope != 0 then
set nextZ = 4*height*s*(d-s)/(d*d) + origin.slope*s + origin.z
set z = nextZ
endif
endmethod
method operator duration takes nothing returns real
return time
endmethod
/* ------------------------------- Sight Range ------------------------------ */
method operator vision= takes real sightRange returns nothing
set sight = sightRange
if dummy == null then
if owner == null then
if source != null then
set dummy = Pool.retrieve(x, y, z, 0)
call SetUnitOwner(dummy, GetOwningPlayer(source), false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
else
set dummy = Pool.retrieve(x, y, z, 0)
call SetUnitOwner(dummy, owner, false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
else
call SetUnitOwner(dummy, owner, false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
endmethod
method operator vision takes nothing returns real
return sight
endmethod
/* ------------------------------- Time Scale ------------------------------- */
method operator timeScale= takes real newTimeScale returns nothing
set effect.timeScale = newTimeScale
endmethod
method operator timeScale takes nothing returns real
return effect.timeScale
endmethod
/* ---------------------------------- Alpha --------------------------------- */
method operator alpha= takes integer newAlpha returns nothing
set effect.alpha = newAlpha
endmethod
method operator alpha takes nothing returns integer
return effect.alpha
endmethod
/* ------------------------------ Player Color ------------------------------ */
method operator playerColor= takes integer playerId returns nothing
set effect.playerColor = playerId
endmethod
method operator playerColor takes nothing returns integer
return effect.playerColor
endmethod
/* -------------------------------- Animation ------------------------------- */
method operator animation= takes integer animType returns nothing
set effect.animation = animType
endmethod
method operator animation takes nothing returns integer
return effect.animation
endmethod
endmodule
private module Methods
/* --------------------------- Bounce and Deflect --------------------------- */
method bounce takes nothing returns nothing
call origin.move(x, y, z - GetLocZ(x, y))
set travel = 0
set finished = false
endmethod
method deflect takes real tx, real ty, real tz returns nothing
local real locZ = GetLocZ(x, y)
set target = null
set toZ = tz
if z < locZ then
set nextX = prevX
set nextY = prevY
set nextZ = prevZ
endif
call impact.move(tx, ty, tz)
call origin.move(x, y, z - locZ)
set travel = 0
set finished = false
endmethod
method deflectTarget takes unit u returns nothing
call deflect(GetUnitX(u), GetUnitY(u), toZ)
set target = u
endmethod
/* ---------------------------- Flush hit targets --------------------------- */
method flushAll takes nothing returns nothing
call FlushChildHashtable(table, this)
endmethod
method flush takes widget w returns nothing
if w != null then
call RemoveSavedBoolean(table, this, GetHandleId(w))
endif
endmethod
method hitted takes widget w returns boolean
return HaveSavedBoolean(table, this, GetHandleId(w))
endmethod
/* ----------------------- Missile attachment methods ----------------------- */
method attach takes string model, real dx, real dy, real dz, real scale returns effect
return effect.attach(model, dx, dy, dz, scale)
endmethod
method detach takes effect attachment returns nothing
if attachment != null then
call effect.detach(attachment)
endif
endmethod
/* ------------------------------ Missile Pause ----------------------------- */
method pause takes boolean flag returns nothing
implement OnResume
endmethod
/* ---------------------------------- Color --------------------------------- */
method color takes integer red, integer green, integer blue returns nothing
call effect.setColor(red, green, blue)
endmethod
/* ---------------------- Destructable collision method --------------------- */
static method onDest takes nothing returns nothing
local thistype this = temp
local destructable d = GetEnumDestructable()
local real dz
local real tz
if not HaveSavedBoolean(table, this, GetHandleId(d)) then
if collideZ then
set dz = GetLocZ(GetWidgetX(d), GetWidgetY(d))
set tz = GetDestructableOccluderHeight(d)
if dz + tz >= z - collision and dz <= z + collision then
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
else
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
endif
set d = null
endmethod
/* -------------------------- Item collision method ------------------------- */
static method onItems takes nothing returns nothing
local thistype this = temp
local item i = GetEnumItem()
local real dz
if not HaveSavedBoolean(table, this, GetHandleId(i)) then
if collideZ then
set dz = GetLocZ(GetItemX(i), GetItemY(i))
if dz + ITEM_SIZE >= z - collision and dz <= z + collision then
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
else
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
endif
set i = null
endmethod
/* -------------------------------- Terminate ------------------------------- */
method terminate takes nothing returns nothing
implement OnRemove
endmethod
endmodule
struct Missiles extends MissileEvents
private static timer timer = CreateTimer()
private static group group = CreateGroup()
private static rect rect = Rect(0., 0., 0., 0.)
private static hashtable table = InitHashtable()
private static integer last = 0
private static thistype temp = 0
private static integer id = -1
private static integer pid = -1
private static thistype array missiles
private static thistype array frozen
private static real dilation = 1
readonly static thistype array collection
readonly static integer count = -1
private real cA
private real height
private real open
private real toZ
private real time
private real sight
private unit dummy
private integer pkey
private integer index
Coordinates impact
Coordinates origin
MissileEffect effect
readonly real x
readonly real y
readonly real z
readonly real prevX
readonly real prevY
readonly real prevZ
readonly real nextX
readonly real nextY
readonly real nextZ
readonly real turn
readonly real veloc
readonly real travel
readonly boolean launched
readonly boolean allocated
readonly boolean finished
readonly boolean paused
readonly integer tileset
unit source
unit target
player owner
boolean collideZ
real collision
real damage
real acceleration
integer data
integer type
boolean roll
implement Operators
implement Methods
/* ------------------------------ Reset members ----------------------------- */
private method reset takes nothing returns nothing
set launched = false
set finished = false
set collideZ = false
set paused = false
set roll = false
set source = null
set target = null
set owner = null
set dummy = null
set open = 0.
set height = 0.
set veloc = 0.
set acceleration = 0.
set collision = 0.
set damage = 0.
set travel = 0.
set turn = 0.
set time = 0.
set sight = 0.
set data = 0
set type = 0
set tileset = 0
set pkey = -1
set index = -1
endmethod
/* -------------------------- Destroys the missile -------------------------- */
private method remove takes integer i returns integer
if paused then
implement OnPause
else
implement OnRemove
endif
set missiles[i] = missiles[id]
set id = id - 1
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1
endif
if id == -1 then
call PauseTimer(timer)
endif
if not allocated then
call deallocate()
endif
return i - 1
endmethod
/* ---------------------------- Missiles movement --------------------------- */
private static method move takes nothing returns nothing
local integer j = 0
local integer i
local integer k
local unit u
local real a
local real d
local real s
local real h
local real c
local real dx
local real dy
local real vel
local real yaw
local real pitch
local Missiles missile
local Coordinates o
local thistype this
if SWEET_SPOT > 0 then
set i = last
else
set i = 0
endif
loop
exitwhen ((j >= SWEET_SPOT and SWEET_SPOT > 0) or j > id)
set this = missiles[i]
set temp = this
if allocated and not paused then
implement OnHit
implement OnMissile
implement OnDestructable
implement OnItem
implement OnCliff
implement OnTerrain
implement OnTileset
implement OnPeriod
implement OnOrient
implement OnFinish
implement OnBoundaries
else
set i = remove(i)
set j = j - 1
endif
set i = i + 1
set j = j + 1
if i > id and SWEET_SPOT > 0 then
set i = 0
endif
endloop
set last = i
set u = null
endmethod
/* --------------------------- Launch the Missile --------------------------- */
method launch takes nothing returns nothing
if not launched and allocated then
set launched = true
set id = id + 1
set missiles[id] = this
set count = count + 1
set index = count
set collection[count] = this
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if id == 0 then
call TimerStart(timer, PERIOD, true, function thistype.move)
endif
endif
endmethod
/* --------------------------- Main Creator method -------------------------- */
static method create takes real x, real y, real z, real toX, real toY, real toZ returns thistype
local thistype this = thistype.allocate()
call .reset()
set .origin = Coordinates.create(x, y, z)
set .impact = Coordinates.create(toX, toY, toZ)
set .effect = MissileEffect.create(x, y, origin.z)
call Coordinates.link(origin, impact)
set .allocated = true
set .cA = origin.angle
set .x = x
set .y = y
set .z = impact.z
set .prevX = x
set .prevY = y
set .prevZ = impact.z
set .nextX = x
set .nextY = y
set .nextZ = impact.z
set .toZ = toZ
return this
endmethod
endstruct
endlibrary
library MissileEffect requires WorldBounds, Alloc
/* -------------------- Missile Effect v2.6 by Chopinski -------------------- */
// This is a simple helper library for the Relativistic Missiles system.
// Credits:
// Sevion for the Alloc module
// - www.hiveworkshop.com/threads/snippet-alloc.192348/
// Nestharus for World Bounds Library
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private module LinkedList
readonly thistype next
readonly thistype prev
method init takes nothing returns thistype
set next = this
set prev = this
return this
endmethod
method pushBack takes thistype node returns thistype
set node.prev = prev
set node.next = this
set prev.next = node
set prev = node
return node
endmethod
method pushFront takes thistype node returns thistype
set node.prev = this
set node.next = next
set next.prev = node
set next = node
return node
endmethod
method pop takes nothing returns nothing
set prev.next = next
set next.prev = prev
endmethod
endmodule
private struct Effect extends array
implement LinkedList
implement Alloc
real x
real y
real z
real size
real yaw
real pitch
real roll
string path
effect effect
method remove takes nothing returns nothing
call DestroyEffect(effect)
call pop()
call deallocate()
set effect = null
endmethod
method insert takes string fxpath, real x, real y, real z, real scale returns thistype
local thistype node = pushBack(allocate())
set node.x = x
set node.y = y
set node.z = z
set node.yaw = 0.
set node.pitch = 0.
set node.roll = 0.
set node.path = fxpath
set node.size = scale
set node.effect = AddSpecialEffect(fxpath, x, y)
call BlzSetSpecialEffectZ(node.effect, z)
call BlzSetSpecialEffectScale(node.effect, scale)
return node
endmethod
static method create takes nothing returns thistype
return thistype(allocate()).init()
endmethod
endstruct
struct MissileEffect
real size
real yaw
real pitch
real roll
real time
integer transparency
integer animtype
integer playercolor
string path
effect effect
Effect attachments
/* -------------------------------- Operators ------------------------------- */
method operator timeScale= takes real newTimeScale returns nothing
set time = newTimeScale
call BlzSetSpecialEffectTimeScale(effect, time)
endmethod
method operator timeScale takes nothing returns real
return time
endmethod
method operator alpha= takes integer newAlpha returns nothing
set transparency = newAlpha
call BlzSetSpecialEffectAlpha(effect, transparency)
endmethod
method operator alpha takes nothing returns integer
return transparency
endmethod
method operator playerColor= takes integer playerId returns nothing
set playercolor = playerId
call BlzSetSpecialEffectColorByPlayer(effect, Player(playerId))
endmethod
method operator playerColor takes nothing returns integer
return playercolor
endmethod
method operator animation= takes integer animType returns nothing
set animtype = animType
call BlzPlaySpecialEffect(effect, ConvertAnimType(animtype))
endmethod
method operator animation takes nothing returns integer
return animtype
endmethod
/* --------------------------------- Methods -------------------------------- */
method scale takes effect sfx, real scale returns nothing
set size = scale
call BlzSetSpecialEffectScale(sfx, scale)
endmethod
method orient takes real yaw, real pitch, real roll returns nothing
local Effect node = attachments.next
set .yaw = yaw
set .pitch = pitch
set .roll = roll
call BlzSetSpecialEffectOrientation(effect, yaw, pitch, roll)
loop
exitwhen node == attachments
set node.yaw = yaw
set node.pitch = pitch
set node.roll = roll
call BlzSetSpecialEffectOrientation(node.effect, yaw, pitch, roll)
set node = node.next
endloop
endmethod
method move takes real x, real y, real z returns boolean
local Effect node = attachments.next
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call BlzSetSpecialEffectPosition(effect, x, y, z)
loop
exitwhen node == attachments
call BlzSetSpecialEffectPosition(node.effect, x - node.x, y - node.y, z - node.z)
set node = node.next
endloop
return true
endif
return false
endmethod
method attach takes string fxpath, real dx, real dy, real dz, real scale returns effect
local Effect node = attachments.insert(fxpath, dx, dy, dz, scale)
call BlzSetSpecialEffectPosition(node.effect, BlzGetLocalSpecialEffectX(effect) - dx, BlzGetLocalSpecialEffectY(effect) - dy, BlzGetLocalSpecialEffectZ(effect) - dz)
return node.effect
endmethod
method detach takes effect sfx returns nothing
local Effect node = attachments.next
loop
exitwhen node == attachments
if GetHandleId(node.effect) == GetHandleId(sfx) then
call node.remove()
exitwhen true
endif
set node = node.next
endloop
endmethod
method setColor takes integer red, integer green, integer blue returns nothing
call BlzSetSpecialEffectColor(effect, red, green, blue)
endmethod
/* -------------------------- Contructor/Destructor ------------------------- */
method destroy takes nothing returns nothing
local Effect node = attachments.next
loop
exitwhen node == attachments
call node.remove()
set node = node.next
endloop
call DestroyEffect(effect)
call attachments.deallocate()
set effect = null
set path = null
set size = 1.
call deallocate()
endmethod
static method create takes real x, real y, real z returns thistype
local thistype this = thistype.allocate()
set effect = AddSpecialEffect("", x, y)
set path = ""
set size = 1
set time = 0
set transparency = 0
set animtype = 0
set playercolor = 0
set attachments = Effect.create()
call BlzSetSpecialEffectZ(effect, z)
return this
endmethod
endstruct
endlibrary
library MissileUtils requires Missiles
/* -------------------- Missile Utils v2.6 by Chopinski -------------------- */
// This is a simple Utils library for the Relativistic Missiles system.
// Credits:
// Sevion for the Alloc module
// - www.hiveworkshop.com/threads/snippet-alloc.192348/
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private module LinkedList
readonly thistype next
readonly thistype prev
method init takes nothing returns thistype
set next = this
set prev = this
return this
endmethod
method pushBack takes thistype node returns thistype
set node.prev = prev
set node.next = this
set prev.next = node
set prev = node
return node
endmethod
method pushFront takes thistype node returns thistype
set node.prev = this
set node.next = next
set next.prev = node
set next = node
return node
endmethod
method pop takes nothing returns nothing
set prev.next = next
set next.prev = prev
endmethod
endmodule
struct MGroup extends array
implement LinkedList
implement Alloc
public Missiles missile
method remove takes nothing returns nothing
call pop()
call deallocate()
endmethod
method insert takes Missiles m returns thistype
local thistype node = pushBack(allocate())
set node.missile = m
return node
endmethod
static method create takes nothing returns thistype
return thistype(allocate()).init()
endmethod
endstruct
struct MissileGroup
MGroup group
integer size
method destroy takes nothing returns nothing
call group.deallocate()
call deallocate()
endmethod
method missileAt takes integer i returns Missiles
local MGroup node = group.next
local integer j = 0
if size > 0 and i <= size - 1 then
loop
exitwhen j == i
set node = node.next
set j = j + 1
endloop
return node.missile
else
return 0
endif
endmethod
method remove takes Missiles missile returns nothing
local MGroup node = group.next
loop
exitwhen node == group
if node.missile == missile then
set size = size - 1
call node.remove()
exitwhen true
endif
set node = node.next
endloop
endmethod
method insert takes Missiles missile returns nothing
set size = size + 1
call group.insert(missile)
endmethod
method clear takes nothing returns nothing
local MGroup node = group.next
loop
exitwhen node == group
call node.remove()
set node = node.next
endloop
set size = 0
endmethod
method contains takes Missiles missile returns boolean
local MGroup node = group.next
local boolean found = false
loop
exitwhen node == group
if node.missile == missile then
set found = true
exitwhen true
endif
set node = node.next
endloop
return found
endmethod
method addGroup takes MissileGroup source returns nothing
local MGroup node = source.group.next
loop
exitwhen node == source.group
if not contains(node.missile) then
call insert(node.missile)
endif
set node = node.next
endloop
endmethod
method removeGroup takes MissileGroup source returns nothing
local MGroup node = source.group.next
loop
exitwhen node == source.group
if contains(node.missile) then
call remove(node.missile)
endif
set node = node.next
endloop
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set group = MGroup.create()
set size = 0
return this
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* -------------------------------------------------------------------------- */
function CreateMissileGroup takes nothing returns MissileGroup
return MissileGroup.create()
endfunction
function DestroyMissileGroup takes MissileGroup missiles returns nothing
if missiles != 0 then
call missiles.clear()
call missiles.destroy()
endif
endfunction
function MissileGroupGetSize takes MissileGroup missiles returns integer
if missiles != 0 then
return missiles.size
else
return 0
endif
endfunction
function GroupMissileAt takes MissileGroup missiles, integer position returns Missiles
if missiles != 0 then
return missiles.missileAt(position)
else
return 0
endif
endfunction
function ClearMissileGroup takes MissileGroup missiles returns nothing
if missiles != 0 then
call missiles.clear()
endif
endfunction
function IsMissileInGroup takes Missiles missile, MissileGroup missiles returns boolean
if missiles != 0 and missile != 0 then
if missiles.size > 0 then
return missiles.contains(missile)
else
return false
endif
else
return false
endif
endfunction
function GroupRemoveMissile takes MissileGroup missiles, Missiles missile returns nothing
if missiles != 0 and missile != 0 then
if missiles.size > 0 then
call missiles.remove(missile)
endif
endif
endfunction
function GroupAddMissile takes MissileGroup missiles, Missiles missile returns nothing
if missiles != 0 and missile != 0 then
if not missiles.contains(missile) then
call missiles.insert(missile)
endif
endif
endfunction
function GroupPickRandomMissile takes MissileGroup missiles returns Missiles
if missiles != 0 then
if missiles.size > 0 then
return missiles.missileAt(GetRandomInt(0, missiles.size - 1))
else
return 0
endif
else
return 0
endif
endfunction
function FirstOfMissileGroup takes MissileGroup missiles returns Missiles
if missiles != 0 then
if missiles.size > 0 then
return missiles.group.next.missile
else
return 0
endif
else
return 0
endif
endfunction
function GroupAddMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
if source != 0 and destiny != 0 then
if source.size > 0 and source != destiny then
call destiny.addGroup(source)
endif
endif
endfunction
function GroupRemoveMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
if source != 0 and destiny != 0 then
if source == destiny then
call source.clear()
elseif source.size > 0 then
call destiny.removeGroup(source)
endif
endif
endfunction
function GroupEnumMissilesOfType takes MissileGroup missiles, integer whichType returns nothing
local integer i
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if missile.type == whichType then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfTypeCounted takes MissileGroup missiles, integer whichType, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if missile.type == whichType then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfPlayer takes MissileGroup missiles, player p returns nothing
local integer i
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if missile.owner == p then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfPlayerCounted takes MissileGroup missiles, player p, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if missile.owner == p then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRect takes MissileGroup missiles, rect r returns nothing
local integer i
local Missiles missile
if missiles != 0 and r != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if GetRectMinX(r) <= missile.x and missile.x <= GetRectMaxX(r) and GetRectMinY(r) <= missile.y and missile.y <= GetRectMaxY(r) then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRectCounted takes MissileGroup missiles, rect r, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and r != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if GetRectMinX(r) <= missile.x and missile.x <= GetRectMaxX(r) and GetRectMinY(r) <= missile.y and missile.y <= GetRectMaxY(r) then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeOfLoc takes MissileGroup missiles, location loc, real radius returns nothing
local real dx
local real dy
local integer i
local Missiles missile
if missiles != 0 and radius > 0 and loc != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
set dx = missile.x - GetLocationX(loc)
set dy = missile.y - GetLocationY(loc)
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeOfLocCounted takes MissileGroup missiles, location loc, real radius, integer amount returns nothing
local real dx
local real dy
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and radius > 0 and loc != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
set dx = missile.x - GetLocationX(loc)
set dy = missile.y - GetLocationY(loc)
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRange takes MissileGroup missiles, real x, real y, real radius returns nothing
local real dx
local real dy
local integer i
local Missiles missile
if missiles != 0 and radius > 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeCounted takes MissileGroup missiles, real x, real y, real radius, integer amount returns nothing
local real dx
local real dy
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and radius > 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
endlibrary
//===========================================================================
//
// Damage Engine 5.7.1.2 - update requires replacing the JASS script.
//
/*
Three GUI Damage systems for the community of The Hive,
Seven vJass Damage systems for the JASS-heads on their pedestals high,
Nine competing Damage systems, doomed to die,
One for Bribe on his dark throne
In the Land of the Hive where the Workshop lies.
One Damage Engine to rule them all, One Damage Engine to find them,
One Damage Engine to bring them all and in cross-compatibility unite them.
*/
//! novjass
JASS API (work in progress - I have a lot of documentation to go through):
struct Damage extends array
readonly static unit source //stores udg_DamageEventSource
readonly static unit target //stores udg_DamageEventTarget
static real amount //stores udg_DamageEventAmount
readonly unit sourceUnit //stores udg_DamageEventSource by index
readonly unit targetUnit //stores udg_DamageEventTarget by index
real damage //stores udg_DamageEventAmount by index
readonly real prevAmt //stores udg_DamageEventPrevAmt by index
attacktype attackType //stores udg_DamageEventAttackT by index
damagetype damageType //stores udg_DamageEventDamageT by index
weapontype weaponType //stores udg_DamageEventWeaponT by index
integer userType //stores udg_DamageEventType by index
readonly integer eFilter //replaces the previous eventFilter variable
readonly boolean isAttack //stores udg_IsDamageAttack by index
readonly boolean isCode //stores udg_IsDamageCode by index
readonly boolean isMelee //stores udg_IsDamageMelee by index
readonly boolean isRanged //stores udg_IsDamageRanged by index
readonly boolean isSpell //stores udg_IsDamageSpell by index
real armorPierced //stores udg_DamageEventArmorPierced by index
integer armorType //stores udg_DamageEventArmorT by index
integer defenseType //stores udg_DamageEventDefenseT by index
static boolean operator enabled
- Set to false to disable the damage event triggers/false to reverse that
static method apply takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns Damage
- 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 applySpell takes unit src, unit tgt, real amt, damagetype dt returns Damage
- A simplified version of the above function that autofills in the booleans, attack type and weapon type.
static method applyAttack takes unit src, unit tgt, real amt, boolean ranged, attacktype at, weapontype wt returns Damage
- A different variation of the above which autofills the "attack" boolean and sets the damagetype to DAMAGE_TYPE_NORMAL.
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:
- global integer DamageEngine_FILTER_ATTACK
- global integer DamageEngine_FILTER_MELEE
- global integer DamageEngine_FILTER_OTHER
- global integer DamageEngine_FILTER_RANGED
- global integer DamageEngine_FILTER_SPELL
- global integer DamageEngine_FILTER_CODE
boolean configured //set to True after configuring any filters listed below.
method configure takes nothing returns nothing
// 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
//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 getIndex takes trigger t, string eventName, real value returns integer
static method registerTrigger takes trigger whichTrig, string var, real weight returns nothing
static method unregister takes trigger t, string eventName, real value, boolean reset returns boolean
static method operator [] takes code c returns trigger
// Converts a code argument to a trigger, while checking if the same code had already been registered before.
//The accepted strings here use the same criteria as DamageTrigger.getIndex/registerTrigger/unregister
function TriggerRegisterDamageEngineEx takes trigger whichTrig, string eventName, real value, integer f returns nothing
function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns nothing
function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns nothing
function RegisterDamageEngine takes code c, string eventName, real value returns nothing
//! endnovjass
//===========================================================================
library DamageEngine
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 AOEDamageEvent, 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
private constant integer LIMBO = 16 //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
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 Blizz ever changes this, it'll be a quick fix here.
private timer alarm = CreateTimer()
private boolean alarmSet = false
//Values to track the original pre-spirit Link/defensive damage values
private Damage lastInstance = 0
private boolean canKick = true
private boolean totem = false
private boolean array attacksImmune
private boolean array damagesImmune
//Made global in order to use enable/disable behavior.
private trigger t1 = CreateTrigger()
private trigger t2 = CreateTrigger()
private trigger t3 = CreateTrigger() //Catches, stores recursive events
//These variables coincide with Blizzard's "limitop" type definitions so as to enable users (GUI in particular) with some nice performance perks.
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
public boolean inception = false //When true, it allows your trigger to potentially go recursive up to LIMBO. However it must be set per-trigger throughout the game and not only once per trigger during map initialization.
private boolean dreaming = false
private integer sleepLevel = 0
private group proclusGlobal = CreateGroup() //track sources of recursion
private group fischerMorrow = CreateGroup() //track targets of recursion
private boolean kicking = false
private boolean eventsRun = false
private keyword run
private keyword trigFrozen
private keyword levelsDeep
private keyword inceptionTrig
private boolean hasLethal = false
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 "checkConfiguration" 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
*/
struct DamageTrigger extends array
//Map-makers should comment-out any booleans they will never need to check for.
method checkConfiguration takes nothing returns boolean
if 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.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 udg_DamageEventAmount > this.damageMin then
return true
endif
return false
endmethod
//The below variables are 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 configured
boolean usingGUI
//The below variables are private
private thistype next
private trigger rootTrig
boolean trigFrozen //Whether the trigger is currently disabled due to recursion
integer levelsDeep //How deep the user recursion currently is.
boolean inceptionTrig //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
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
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.sourceBuff = udg_DamageFilterSourceB
set this.targetBuff = udg_DamageFilterTargetB
set this.userType = udg_DamageFilterType
set this.damageMin = udg_DamageFilterMinAmount
set udg_DamageFilterAttackT =-1
set udg_DamageFilterDamageT =-1
set udg_DamageFilterSource = null
set udg_DamageFilterTarget = null
set udg_DamageFilterSourceT = 0
set udg_DamageFilterTargetT = 0
set udg_DamageFilterType = 0
set udg_DamageFilterSourceB = 0
set udg_DamageFilterTargetB = 0
set udg_DamageFilterMinAmount=0.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
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" then
if root >= 4 then
set root= SHIELD //4.00 or higher
else
set root= MOD //Less than 4.00
endif
elseif var == "udg_DamageEvent" then
if root == 2 or root == 0 then
set root= ZERO
else
set root= DAMAGE //Above 0.00 but less than 2.00, generally would just be 1.00
endif
elseif var == "udg_AfterDamageEvent" then
set root = AFTER
elseif var == "udg_LethalDamageEvent" then
set root = LETHAL
elseif var == "udg_AOEDamageEvent" 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 f returns nothing
set this = this*FILTER_MAX
if f == FILTER_OTHER then
call this.toggleAllFilters(true)
else
if f == 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 + f] = 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
set filters[lastRegistered*FILTER_MAX + filt] = true //allows GUI to register multiple different types of Damage filters to the same trigger
return 0
endif
if not hasLethal and index == LETHAL then
set hasLethal = true
endif
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
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
static method unregister takes trigger t, string eventName, real lbs, boolean reset returns boolean
local thistype index = getIndex(t, eventName, lbs)
if index == 0 then
return false
endif
set prev.next = index.next
set trigIndexStack[index] = trigIndexStack[0]
set trigIndexStack[0] = index
if reset then
call index.configure()
set index.configured = false
set index = index*FILTER_MAX
call index.toggleAllFilters(false)
endif
return true
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 dreaming then
return
endif
set dreaming = true
call DisableTrigger(t1)
call DisableTrigger(t2)
call EnableTrigger(t3)
//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.checkConfiguration()) 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
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(t3)
call EnableTrigger(t1)
call EnableTrigger(t2)
set dreaming = false
endmethod
static trigger array autoTriggers
static boolexpr array autoFuncs
static integer autoN = 0
static method operator [] takes code c returns trigger
local integer i = 0
local boolexpr b = Filter(c)
loop
if i == autoN then
set autoTriggers[i] = CreateTrigger()
set autoFuncs[i] = b
call TriggerAddCondition(autoTriggers[i], b)
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 //stores udg_DamageEventSource
readonly unit targetUnit //stores udg_DamageEventTarget
real damage //stores udg_DamageEventAmount
readonly real prevAmt //stores udg_DamageEventPrevAmt
attacktype attackType //stores udg_DamageEventAttackT
damagetype damageType //stores udg_DamageEventDamageT
weapontype weaponType //stores udg_DamageEventWeaponT
integer userType //stores udg_DamageEventType
readonly boolean isAttack //stores udg_IsDamageAttack
readonly boolean isCode //stores udg_IsDamageCode
readonly boolean isSpell //stores udg_IsDamageSpell
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 pierce != 0.00 then
call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
endif
if Damage.index.prevArmorT != udg_DamageEventArmorT then
call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, at)
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
if udg_DamageEventAOE > 1 then
call DamageTrigger.AOE.run()
endif
set udg_DamageEventAOE = 0
set udg_DamageEventLevel = 0
set udg_EnhancedDamageTarget = null
set udg_AOEDamageSource = null
call GroupClear(udg_DamageEventAOEGroup)
endmethod
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
private static method afterDamage takes nothing returns nothing
if udg_DamageEventPrevAmt != 0.00 and udg_DamageEventDamageT != 0 then
call DamageTrigger.AFTER.run()
set udg_DamageEventDamageT = 0
set udg_DamageEventPrevAmt = 0.00
endif
endmethod
private method doPreEvents 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(proclusGlobal, udg_DamageEventSource)
call GroupAddUnit(fischerMorrow, 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()
if 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
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.levelsDeep = 0
endloop
call EnableTrigger(t1)
call EnableTrigger(t2)
set kicking = false
set damageStack = 0
set prepped = 0
set dreaming = false
set sleepLevel = 0
call GroupClear(proclusGlobal)
call GroupClear(fischerMorrow)
//call BJDebugMsg("Cleared up the groups")
endmethod
static method finish takes nothing returns nothing
local Damage i = 0
local integer exit
if eventsRun then
set eventsRun = false
call afterDamage()
endif
if canKick and not kicking then
if damageStack != 0 then
set kicking = true
loop
set sleepLevel = sleepLevel + 1
set exit = damageStack
loop
set prepped = i.stackRef
if UnitAlive(prepped.targetUnit) then //Added just in case dead units had issues.
call prepped.doPreEvents(false) //don't evaluate the pre-event
if prepped.damage > 0.00 then
call DisableTrigger(t1) //Force only the after armor event to run.
call EnableTrigger(t2) //in case the user forgot to re-enable this
set totem = true
call UnitDamageTarget(prepped.sourceUnit, prepped.targetUnit, prepped.damage, prepped.isAttack, prepped.isRanged, prepped.attackType, prepped.damageType, prepped.weaponType)
else
//No new events run at all in this case
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
endif
if prepped.damage < 0.00 then
//No need for BlzSetEventDamage here
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 canKick = true
set kicking = false
set totem = false
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
set eventsRun = true
endif
call finish()
endmethod
static method operator enabled= takes boolean b returns nothing
if b then
if dreaming then
call EnableTrigger(t3)
else
call EnableTrigger(t1)
call EnableTrigger(t2)
endif
else
if dreaming then
call DisableTrigger(t3)
else
call DisableTrigger(t1)
call DisableTrigger(t2)
endif
endif
endmethod
static method operator enabled takes nothing returns boolean
return IsTriggerEnabled(t1)
endmethod
private static boolean arisen = false
private static method getOutOfBed takes nothing returns nothing
if totem then
call failsafeClear() //WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
else
set canKick = true
set kicking = false
call finish()
endif
static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call onAOEEnd()
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set arisen = true
endmethod
private static method wakeUp takes nothing returns nothing
set dreaming = false
set Damage.enabled = true
call ForForce(bj_FORCE_PLAYER[0], function thistype.getOutOfBed) //Moved to a new thread in case of a thread crash
if not arisen then
//call BJDebugMsg("DamageEngine issue: thread crashed!")
call unfreeze()
else
set arisen = false
endif
set Damage.count = 0
set Damage.index = 0
set alarmSet = false
//call BJDebugMsg("Timer wrapped up")
endmethod
private method addRecursive takes nothing returns nothing
if this.damage != 0.00 then
set this.recursiveTrig = DamageTrigger.eventIndex
if not this.isCode then
set this.isCode = true
set this.userType = TYPE_CODE
endif
set inception = inception or DamageTrigger.eventIndex.inceptionTrig
if kicking and IsUnitInGroup(this.sourceUnit, proclusGlobal) and IsUnitInGroup(this.targetUnit, fischerMorrow) then
if not inception then
set DamageTrigger.eventIndex.trigFrozen = true
elseif not DamageTrigger.eventIndex.trigFrozen then
set DamageTrigger.eventIndex.inceptionTrig = true
if DamageTrigger.eventIndex.levelsDeep < sleepLevel then
set DamageTrigger.eventIndex.levelsDeep = DamageTrigger.eventIndex.levelsDeep + 1
if DamageTrigger.eventIndex.levelsDeep >= LIMBO then
set DamageTrigger.eventIndex.trigFrozen = true
endif
endif
endif
endif
set damageStack.stackRef = this
set damageStack = damageStack + 1
//call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(DamageTrigger.eventIndex.levelsDeep) + " sleepLevel: " + I2S(sleepLevel))
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 a, 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.attackType = at
set d.damageType = dt
set d.weaponType = wt
set d.isAttack = udg_NextDamageIsAttack or a
set d.isSpell = d.attackType == null and not d.isAttack
return d
endmethod
private static method createFromEvent takes nothing returns Damage
local Damage d = create(GetEventDamageSource(), GetTriggerUnit(), GetEventDamage(), BlzGetEventDamageType() == DAMAGE_TYPE_NORMAL, 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.damage != 0.00 and d.damageType == DAMAGE_TYPE_UNKNOWN)
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
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
set d.isMelee = d.weaponType != null // Melee units play a sound when damaging
set d.isRanged = not d.isMelee // In the case where a unit is both ranged and melee, the ranged attack plays no sound.
endif
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// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set d.isMelee = false
set d.isRanged = false
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endif
endif
call clearNexts()
return d
endmethod
private static method onRecursion takes nothing returns boolean //New in 5.7
local Damage d = Damage.createFromEvent()
call d.addRecursive()
call BlzSetEventDamage(0.00)
return false
endmethod
private static method onDamaging takes nothing returns boolean
local Damage d = Damage.createFromEvent()
//call BJDebugMsg("Pre-damage event running for " + GetUnitName(GetTriggerUnit()))
if alarmSet then
if totem 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 totem = false
set lastInstance= Damage.index
set canKick = false
else
call failsafeClear() //Not an overlapping event - just wrap it up
endif
else
call finish() //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
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(alarm, 0.00, false, function Damage.wakeUp)
set alarmSet = 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.doPreEvents(true) then
call DamageTrigger.ZERO.run()
set canKick = true
call finish()
endif
set totem = 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 onDamaged 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 dreaming or d.prevAmt == 0.00 then
return false
elseif totem then
set totem = false
else
//This should only happen for stuff like Spirit Link or Thorns Aura/Carapace
call afterDamage()
set Damage.index = lastInstance
set lastInstance = 0
set d = Damage.index
set canKick = true
call DamageTrigger.setGUIFromStruct(true)
endif
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call d.setArmor(true)
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
static if USE_SCALING then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if udg_DamageEventAmount != 0.00 and r != 0.00 then
set udg_DamageScalingWC3 = r / udg_DamageEventAmount
elseif udg_DamageEventAmount > 0.00 then
set udg_DamageScalingWC3 = 0.00
else
set udg_DamageScalingWC3 = 1.00
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 hasLethal or udg_DamageEventType < 0 then
set udg_LethalDamageHP = GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
if udg_LethalDamageHP <= DEATH_VAL then
if hasLethal 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 eventsRun = true
if udg_DamageEventAmount == 0.00 then
call finish()
endif
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 dreaming 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 finish()
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
//===========================================================================
private static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(t1, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(t1, Filter(function Damage.onDamaging))
call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DAMAGED)
call TriggerAddCondition(t2, Filter(function Damage.onDamaged))
//For recursion
call TriggerRegisterAnyUnitEventBJ(t3, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(t3, Filter(function Damage.onRecursion))
call DisableTrigger(t3)
//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
public function DebugStr takes nothing returns nothing
local integer i = 0
loop
set udg_CONVERTED_ATTACK_TYPE[i] = ConvertAttackType(i)
exitwhen i == 6
set i = i + 1
endloop
set i = 0
loop
set udg_CONVERTED_DAMAGE_TYPE[i] = ConvertDamageType(i)
exitwhen i == 26
set i = i + 1
endloop
set udg_AttackTypeDebugStr[0] = "SPELLS" //ATTACK_TYPE_NORMAL in JASS
set udg_AttackTypeDebugStr[1] = "NORMAL" //ATTACK_TYPE_MELEE in JASS
set udg_AttackTypeDebugStr[2] = "PIERCE"
set udg_AttackTypeDebugStr[3] = "SIEGE"
set udg_AttackTypeDebugStr[4] = "MAGIC"
set udg_AttackTypeDebugStr[5] = "CHAOS"
set udg_AttackTypeDebugStr[6] = "HERO"
set udg_DamageTypeDebugStr[0] = "UNKNOWN"
set udg_DamageTypeDebugStr[4] = "NORMAL"
set udg_DamageTypeDebugStr[5] = "ENHANCED"
set udg_DamageTypeDebugStr[8] = "FIRE"
set udg_DamageTypeDebugStr[9] = "COLD"
set udg_DamageTypeDebugStr[10] = "LIGHTNING"
set udg_DamageTypeDebugStr[11] = "POISON"
set udg_DamageTypeDebugStr[12] = "DISEASE"
set udg_DamageTypeDebugStr[13] = "DIVINE"
set udg_DamageTypeDebugStr[14] = "MAGIC"
set udg_DamageTypeDebugStr[15] = "SONIC"
set udg_DamageTypeDebugStr[16] = "ACID"
set udg_DamageTypeDebugStr[17] = "FORCE"
set udg_DamageTypeDebugStr[18] = "DEATH"
set udg_DamageTypeDebugStr[19] = "MIND"
set udg_DamageTypeDebugStr[20] = "PLANT"
set udg_DamageTypeDebugStr[21] = "DEFENSIVE"
set udg_DamageTypeDebugStr[22] = "DEMOLITION"
set udg_DamageTypeDebugStr[23] = "SLOW_POISON"
set udg_DamageTypeDebugStr[24] = "SPIRIT_LINK"
set udg_DamageTypeDebugStr[25] = "SHADOW_STRIKE"
set udg_DamageTypeDebugStr[26] = "UNIVERSAL"
set udg_WeaponTypeDebugStr[0] = "NONE" //WEAPON_TYPE_WHOKNOWS in JASS
set udg_WeaponTypeDebugStr[1] = "METAL_LIGHT_CHOP"
set udg_WeaponTypeDebugStr[2] = "METAL_MEDIUM_CHOP"
set udg_WeaponTypeDebugStr[3] = "METAL_HEAVY_CHOP"
set udg_WeaponTypeDebugStr[4] = "METAL_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[5] = "METAL_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[6] = "METAL_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[7] = "METAL_MEDIUM_BASH"
set udg_WeaponTypeDebugStr[8] = "METAL_HEAVY_BASH"
set udg_WeaponTypeDebugStr[9] = "METAL_MEDIUM_STAB"
set udg_WeaponTypeDebugStr[10] = "METAL_HEAVY_STAB"
set udg_WeaponTypeDebugStr[11] = "WOOD_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[12] = "WOOD_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[13] = "WOOD_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[14] = "WOOD_LIGHT_BASH"
set udg_WeaponTypeDebugStr[15] = "WOOD_MEDIUM_BASH"
set udg_WeaponTypeDebugStr[16] = "WOOD_HEAVY_BASH"
set udg_WeaponTypeDebugStr[17] = "WOOD_LIGHT_STAB"
set udg_WeaponTypeDebugStr[18] = "WOOD_MEDIUM_STAB"
set udg_WeaponTypeDebugStr[19] = "CLAW_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[20] = "CLAW_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[21] = "CLAW_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[22] = "AXE_MEDIUM_CHOP"
set udg_WeaponTypeDebugStr[23] = "ROCK_HEAVY_BASH"
set udg_DefenseTypeDebugStr[0] = "LIGHT"
set udg_DefenseTypeDebugStr[1] = "MEDIUM"
set udg_DefenseTypeDebugStr[2] = "HEAVY"
set udg_DefenseTypeDebugStr[3] = "FORTIFIED"
set udg_DefenseTypeDebugStr[4] = "NORMAL" //Typically deals flat damage to all armor types
set udg_DefenseTypeDebugStr[5] = "HERO"
set udg_DefenseTypeDebugStr[6] = "DIVINE"
set udg_DefenseTypeDebugStr[7] = "UNARMORED"
set udg_ArmorTypeDebugStr[0] = "NONE" //ARMOR_TYPE_WHOKNOWS in JASS, added in 1.31
set udg_ArmorTypeDebugStr[1] = "FLESH"
set udg_ArmorTypeDebugStr[2] = "METAL"
set udg_ArmorTypeDebugStr[3] = "WOOD"
set udg_ArmorTypeDebugStr[4] = "ETHEREAL"
set udg_ArmorTypeDebugStr[5] = "STONE"
endfunction
//===========================================================================
//
// Setup of automatic events from GUI and custom ones from JASS alike
//
//===========================================================================
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 f returns DamageTrigger
return DamageTrigger.registerVerbose(whichTrig, DamageTrigger.getVerboseStr(eventName), value, false, f)
endfunction
function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns DamageTrigger
return DamageTrigger.registerTrigger(whichTrig, eventName, value)
endfunction
function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns DamageTrigger
return TriggerRegisterDamageEngineEx(DamageTrigger[c], eventName, value, f)
endfunction
//Similar to TriggerRegisterDamageEvent, although takes code instead of trigger as the first argument.
function RegisterDamageEngine takes code c, string eventName, real value returns DamageTrigger
return RegisterDamageEngineEx(c, eventName, value, FILTER_OTHER)
endfunction
//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()
if not DamageTrigger.eventIndex.checkConfiguration() then
return
endif
endif
//! endtextmacro
endlibrary
scope DarkBladeOfSoulReapingScope
struct DarkBladeOfSoulReaping extends OnHit
private boolean onCooldown = false
private integer hitCounter = 0
private static method ShouldBeExecuted takes unit target, real sourceHpExecuteRequirement returns boolean
local real targetHp = GetUnitState(target, UNIT_STATE_LIFE)
return targetHp < sourceHpExecuteRequirement
endmethod
private static method AoEEffect takes unit source, unit primaryTargetOfAttack, real damageAoE, real sourceHpExecuteRequirement, group instaKilled returns integer
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
local real targetCurrentHp
local integer timeTriggered = 1
local real x = GetUnitX(primaryTargetOfAttack)
local real y = GetUnitY(primaryTargetOfAttack)
call DestroyEffect(AddSpecialEffect("Items\\Vfx\\Soul Discharge Purple.mdx", x, y))
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 250., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if BlzIsUnitSelectable(u) and not IsUnitAlly(u, owner) and UnitAlive(u) and not IsUnitInGroup(u, instaKilled) and IsUnitInRangeXY(u, x, y, 200.) then
call Damage.applySpell(source, u, damageAoE, DAMAGE_TYPE_DEATH)
if ShouldBeExecuted(u, sourceHpExecuteRequirement) then
call GroupAddUnit(instaKilled, u)
call Damage.applySpell(source, u, GetUnitState(u, UNIT_STATE_MAX_LIFE), DAMAGE_TYPE_DEATH)
set timeTriggered = timeTriggered + AoEEffect(source, u, damageAoE, sourceHpExecuteRequirement, instaKilled)
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
return timeTriggered
endmethod
private static method resetCooldown takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set onCooldown = false
call ReleaseTimer(t)
set t = null
endmethod
private static method baseHealAmount takes unit source returns real
if IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER) then
return 9. + 0.0275 * MissingHp(source)
else
return 9. + 0.020 * MissingHp(source)
endif
endmethod
private static method runEffect takes unit source returns real
local real baseHealAmount = baseHealAmount(source)
local real baseManaAmount = 7 //5 + GetHeroLevel(source) / 10
local real damageAoE = 0.25 * Damage.index.prevAmt + 4 * (baseHealAmount + baseManaAmount)
local real executeThreshhold = 0.25 * GetUnitStateSwap(UNIT_STATE_LIFE, source)
local group chainKillGroup = CreateGroup()
local integer timesTriggered = AoEEffect(source, Damage.target, damageAoE, executeThreshhold, chainKillGroup)
call DestroyGroup(chainKillGroup)
set chainKillGroup = null
call RestoreManaWithText(source, timesTriggered * baseManaAmount, 0.85)
return timesTriggered * baseHealAmount
endmethod
method onHit takes unit source, integer commonHitCounter returns real
local real healAmount = 0.0
if not onCooldown then
if BlzBitAnd(hitCounter, 3) == 0 then
set onCooldown = true
call TimerStart(NewTimerEx(this), 1.0, false, function thistype.resetCooldown)
set hitCounter = 1
set healAmount = runEffect(source)
else
set hitCounter = hitCounter + 1
endif
endif
return healAmount
endmethod
endstruct
endscope
struct BlackNavaja extends OnHit
method onHit takes unit source, integer hitCounter returns real
local real healAmount = 0.0
if Mod(hitCounter, 4) == 0 then
call SetHasFiered()
call RestoreManaWithText(source, 4.0, 0.85)
if IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER) then
set healAmount = 7 + 0.021 * MissingHp(source)
else
set healAmount = 7 + 0.014 * MissingHp(source)
endif
set Damage.amount = Damage.amount + (healAmount + 3.) * 3.
endif
return healAmount
endmethod
endstruct
struct DeathDirkOfBlood extends OnHit
method onHit takes unit source, integer hitCounter returns real
local real healAmount
local real critModifier = 1.0
local real recentHitModifier = 1.0
if HasFiered() then
set recentHitModifier = 0.3
endif
call SetHasFiered()
if IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER) then
set healAmount = 8.0
else
set healAmount = 6.0
endif
if udg_hit_is_crit then
set critModifier = 1.0 + GetUnitBonus(source, BonusCriticalDamage.typeid)
endif
set Damage.amount = Damage.amount + recentHitModifier * critModifier * 0.02 * MissingHp(Damage.target)
return healAmount * critModifier
endmethod
endstruct
struct AxeOfTheBruteOnHit extends OnHit
method onHit takes unit source, integer hitCounter returns real
if not HasFiered() then
call AddUnitBonusTimed(source, BONUS_HEALTH_REGEN, 2.5, 4.0)
else
call AddUnitBonusTimed(source, BONUS_HEALTH_REGEN, 0.25, 4.0)
endif
return 0.0
endmethod
endstruct
struct SpikedSmasherOfArmorDestructionOnHit extends OnHit
private static timer array timers
private static integer array shreddedArmor
private static method timeout takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer index = GetTimerData(t)
local unit u = udg_UDexUnits[index]
call AddUnitBonus(u, BONUS_ARMOR, -shreddedArmor[index])
set shreddedArmor[index] = 0
set timers[index] = null
call ReleaseTimer(t)
set t = null
set u = null
endmethod
method onHit takes unit source, integer hitCounter returns real
local integer index = GetUnitUserData(Damage.target)
local integer armorReduction
if timers[index] == null then
set timers[index] = NewTimerEx(index)
endif
if not HasFiered() then
call AddUnitBonusTimed(source, BONUS_HEALTH_REGEN, 4.0, 5.0)
set armorReduction = -6
else
set armorReduction = -2
call AddUnitBonusTimed(source, BONUS_HEALTH_REGEN, 0.4, 5.0)
endif
call AddUnitBonus(Damage.target, BONUS_ARMOR, armorReduction)
set shreddedArmor[index] = shreddedArmor[index] + armorReduction
call TimerStart(timers[index], 7.0, false, function thistype.timeout)
return 0.0
endmethod
endstruct
scope ItemsDefinitions
globals
boolean writeBonusesToItemText = false
boolean isRefreshAction = false
//BASIC ITEMS (Permanent):
constant integer AXE_OF_ATTACK = 'I00Q'
constant integer SWORD_OF_ATTACK = 'I00R'
constant integer LEATHER_PANTS = 'I00P'
constant integer SCALE_ARMOR = 'I00M'
constant integer STRANGE_AMULET = 'I02S'
constant integer SLIGHTLY_MAGICAL_WAND = 'I02R'
constant integer BOOTS_OF_SPEED = 'I00H'
constant integer DAGGER = 'I00G'
constant integer SHIV_OF_CRITICAL_STABBING = 'I02F'
constant integer GEM_OF_LIFE = 'I00U'
constant integer GEM_OF_MANA = 'I00S'
constant integer REGENERATING_BRANCH = 'I00I'
constant integer MANA_POACH = 'I00F'
//Stats
constant integer TABI_OF_AGILITY = 'I005'
constant integer CLOAK_OF_AGILITY = 'I00C'
constant integer CLUB_OF_STRENGTH = 'I004'
constant integer HAMMER_OF_STRENGTH = 'I00D'
constant integer WIZARD_HAT_OF_INTELLIGENCE = 'I009'
constant integer STAFF_OF_INTELLIGENCE = 'I00E'
constant integer DIADEM_OF_NOBILITY = 'I00A'
//Consumables (Charged):
constant integer HEALING_POTION = 'I047'
constant integer HEALING_SALVE = 'I01Y'
constant integer MANA_POTION = 'I046'
constant integer MOON_ORCHID = 'I048'
constant integer REJUVENATION_POTION = 'I049'
constant integer STONE_SKIN_POTION = 'I04A'
//POWER UP
constant integer EXTRA_LIFE = 'I00K'
constant integer MEDKIT = 'I01E'
//ARTIFACTS
constant integer AMULET_OF_AMPLIFICATION = 'I04Q'
constant integer AMULET_OF_FIRE = 'I03M'
constant integer ARCANE_ARMOR_OF_BLASTING = 'I03C'
constant integer ARCANE_RING_OF_AMILIFICATION = 'I006'
constant integer ARCANE_RING_OF_AMILIFICATION_OFF = 'I007'
constant integer ARCANE_STAFF_OF_ENERGY = 'I01A'
constant integer AXE_OF_THE_BRUTE = 'I04Y'
constant integer BLACK_NAVAJA = 'I02B'
constant integer BLOOD_TOOTH = 'I03N'
constant integer BOOTS_OF_OCCASIONAL_FULMINATION = 'I03K'
constant integer BOOTS_OF_OCCASIONAL_LIGHTNING = 'I03I'
constant integer BROOCH_OF_AGILE_SWIFTNESS = 'I026'
constant integer BROOCH_OF_FORCEFUL_SWIFTNESS = 'I025'
constant integer BROOCH_OF_INSIGHTFUL_SWIFTNESS = 'I027'
constant integer BROOCH_OF_SWIFTNESS = 'I01Q'
constant integer CLEAVING_AXE = 'I00N'
constant integer CRITICAL_DEATH_MASK = 'I02N'
constant integer CRITICAL_SLICER = 'I02L'
constant integer CUP_OF_BLOOD = 'I03E'
constant integer DAGGER_OF_FAST_STABBING = 'I014'
constant integer DARK_BLADE_OF_SOUL_REAPING = 'I042'
constant integer DEATH_DEALER = 'I018'
constant integer DEATH_DIRK_OF_BLOOD = 'I03U'
constant integer DRAGON_STAFF_OF_FLAMES = 'I03Y'
constant integer ENT_BARK_SHIELD = 'I01H'
constant integer ENDLESS_ELIXIR_HEALING = 'I00J'
constant integer ENDLESS_ELIXIR_MANA = 'I02D'
constant integer FLAMING_PAULDRONS_OF_RADIANT_HEAT = 'I016'
constant integer FROZEN_SKULL_OF_NOVA = 'I038'
constant integer GNARLT_STINGER_OF_ACUTE_TOXICITY = 'I04B'
constant integer GOBLET_OF_FIRE = 'I01U'
constant integer GOLDEN_GOBLET_OF_GORE = 'I03G'
constant integer GOLDEN_SHIELD_OF_AEGIS = 'I03W'
constant integer HEAVY_PLATE_ARMOR = 'I00X'
constant integer HEAVY_SHIELD_OF_STRENGTH = 'I015'
constant integer INCONVENIENTLY_HEAVY_SHIELD = 'I02X'
constant integer INFINITE_VIAL_HEALING = 'I00B'
constant integer INFINITE_VIAL_MANA = 'I01V'
constant integer LIFE_STAFF = 'I03Q'
constant integer MANA_STEEL_SWORD = 'I04H'
constant integer MAGE_ARMOR = 'I03A'
constant integer MAGE_BLADE = 'I01P'
constant integer MAGE_BLADE_OFF = 'I01Z'
constant integer MAGI_SWORD = 'I020'
constant integer MAGI_SWORD_OFF = 'I01W'
constant integer MAGICAL_AMULET = 'I030'
constant integer MAGICAL_DAGGER = 'I03Z'
constant integer MANAFLOWIFICATOR = 'I032'
constant integer MAUL_OF_FORCEFUL_IMPACT = 'I011'
constant integer MAUL_OF_TREMENDOUS_IMPACT = 'I022'
constant integer MERCURIAL_SCIMITAR_OF_INFINITE_SHARPNESS = 'I044'
constant integer MIGHTY_SKULL_OF_INSIGHT = 'I000'
constant integer MECHANICAL_DEVICE_OF_CONVINSING_CLONING = 'I04T'
constant integer OBSERVING_STAFF_OF_THE_GREAT_EYE = 'I04P'
constant integer OMNI_GEM = 'I04V'
constant integer PHOENIX_AXE = 'I00L'
constant integer POWER_HAMMER_OF_FABRICATION = 'I04N'
constant integer REACTIVE_ARMOR_OF_ARCANE_REPULSION = 'I04D'
constant integer RING_OF_LIFE = 'I01M'
constant integer RING_OF_MANA = 'I01L'
constant integer RING_OF_POWER = 'I01O'
constant integer SMALL_SHIELD = 'I02P'
constant integer SPIKED_PLATE_OF_VENGEANCE = 'I03S'
constant integer SPIKED_SMASHER_OF_ARMOR_DESTRUCTION = 'I04X'
constant integer STAFF_OF_INSIGHT = 'I00Z'
constant integer STAFF_OF_SUPREME_INSIGHT = 'I028'
constant integer SUPREME_DAGGER_OF_SWIFT_STABBING = 'I02V'
constant integer SWIFT_BLADE_OF_QUICKENING = 'I04F'
constant integer SOUL_SIPHONING_SWORD_OF_SLAUGHTER = 'I04K'
constant integer THUNDER_CLAWS = 'I02J'
constant integer THUNDER_DEATH_BLADE = 'I02T'
constant integer WAND_OF_BLASTING = 'I034'
endglobals
private struct ItemsDefinitions
static method IsBootsMSNotBlockedBy takes unit u, integer newBoots, integer itemToCheckFor returns boolean
return (newBoots == itemToCheckFor and CountItemsOfType(u, newBoots) == 1) or (newBoots != itemToCheckFor and CountItemsOfType(u, itemToCheckFor) == 0)
endmethod
static method BootsMovementSpeedAllowed takes unit u, integer newBoots returns boolean
local boolean checkedBoS = IsBootsMSNotBlockedBy(u, newBoots, BOOTS_OF_SPEED)
local boolean checkedBrooches = IsBootsMSNotBlockedBy(u, newBoots, BROOCH_OF_SWIFTNESS) and IsBootsMSNotBlockedBy(u, newBoots, BROOCH_OF_AGILE_SWIFTNESS)
local boolean checkedBrooches2 = IsBootsMSNotBlockedBy(u, newBoots, BROOCH_OF_FORCEFUL_SWIFTNESS) and IsBootsMSNotBlockedBy(u, newBoots, BROOCH_OF_INSIGHTFUL_SWIFTNESS)
local boolean checkedLightningBoots = IsBootsMSNotBlockedBy(u, newBoots, BOOTS_OF_OCCASIONAL_LIGHTNING) and IsBootsMSNotBlockedBy(u, newBoots, BOOTS_OF_OCCASIONAL_FULMINATION)
return checkedBoS and checkedBrooches and checkedBrooches2 and checkedLightningBoots
endmethod
static method RefreshItem takes unit u, item i returns nothing
local integer itemTypeId = GetItemTypeId(i)
if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
return
endif
if itemTypeId == CUP_OF_BLOOD then
call RemoveSpecificBonusFromItem(i, BonusSpellPower.typeid)
call LinkBonusToItem(u, BonusSpellPower.typeid, 5 + 0.1 * GetItemCharges(i), i)
elseif itemTypeId == GOLDEN_GOBLET_OF_GORE then
call RemoveSpecificBonusFromItem(i, BonusSpellPower.typeid)
call RemoveSpecificBonusFromItem(i, BonusSpellHaste.typeid)
call LinkBonusToItem(u, BonusSpellPower.typeid, 40 + 0.1 * GetItemCharges(i), i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 0.04 * GetItemCharges(i), i)
elseif itemTypeId == MAGE_BLADE then
call RemoveSpecificBonusFromItem(i, BonusDamage.typeid)
if GetUnitStateSwap(UNIT_STATE_MANA, u) > 0.2 * MaxMana(u) then
call LinkBonusToItem(u, BonusDamage.typeid, 25 + 0.02 * MaxMana(u), i)
else
call LinkBonusToItem(u, BonusDamage.typeid, 25, i)
endif
elseif itemTypeId == MAGI_SWORD then
call RemoveSpecificBonusFromItem(i, BonusDamage.typeid)
if GetUnitStateSwap(UNIT_STATE_MANA, u) > 0.2 * MaxMana(u) then
call LinkBonusToItem(u, BonusDamage.typeid, 45 + 0.04 * MaxMana(u), i)
else
call LinkBonusToItem(u, BonusDamage.typeid, 45, i)
endif
elseif itemTypeId == MAUL_OF_TREMENDOUS_IMPACT then
call RemoveSpecificBonusFromItem(i, BonusDamage.typeid)
call LinkBonusToItem(u, BonusDamage.typeid, 0.0175 * MaxHp(u), i)
elseif itemTypeId == MERCURIAL_SCIMITAR_OF_INFINITE_SHARPNESS then
call RemoveSpecificBonusFromItem(i, BonusDamage.typeid)
call RemoveSpecificBonusFromItem(i, BonusCriticalDamage.typeid)
call LinkBonusToItem(u, BonusDamage.typeid, 35 + 4 * GetItemCharges(i), i)
call LinkBonusToItem(u, BonusCriticalDamage.typeid, 0.01 * GetItemCharges(i), i)
elseif itemTypeId == STAFF_OF_SUPREME_INSIGHT then
if CountItemsOfType(u, STAFF_OF_SUPREME_INSIGHT) == 1 then
call RemoveSpecificBonusFromItem(i, BonusSpellPower.typeid)
call LinkBonusToItem(u, BonusSpellPower.typeid, 0.024 * MaxMana(u), i)
endif
elseif itemTypeId == SUPREME_DAGGER_OF_SWIFT_STABBING then
if CountItemsOfType(u, SUPREME_DAGGER_OF_SWIFT_STABBING) == 1 then
call RemoveSpecificBonusFromItem(i, BonusDamage.typeid)
call LinkBonusToItem(u, BonusDamage.typeid, 1.5 * GetHeroBonusAgi(u), i)
endif
endif
endmethod
static method OnDropItem takes nothing returns nothing
local unit u = GetManipulatingUnit()
local item i = GetManipulatedItem()
local integer itemTypeId = GetItemTypeId(i)
local group g
if not isRefreshAction and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and CountItemsOfType(u, itemTypeId) == 1 then
if itemTypeId == RING_OF_POWER then
set g = LoadGroupHandle(udg_item_hash, StringHash("Ring of Power wearers"), 0)
call GroupRemoveUnit(g, u)
if BlzGroupGetSize(g) == 0 then
call DisableTrigger(gg_trg_RingOfPowerEvent)
endif
elseif itemTypeId == BLACK_NAVAJA then
call RemoveCustomOnHit(u, BlackNavaja.typeid)
elseif itemTypeId == DARK_BLADE_OF_SOUL_REAPING then
call RemoveCustomOnHit(u, DarkBladeOfSoulReaping.typeid)
elseif itemTypeId == DEATH_DIRK_OF_BLOOD then
call RemoveCustomOnHit(u, DeathDirkOfBlood.typeid)
elseif itemTypeId == AXE_OF_THE_BRUTE then
call RemoveCustomOnHit(u, AxeOfTheBruteOnHit.typeid)
elseif itemTypeId == SPIKED_SMASHER_OF_ARMOR_DESTRUCTION then
call RemoveCustomOnHit(u, SpikedSmasherOfArmorDestructionOnHit.typeid)
endif
endif
set u = null
set i = null
set g = null
endmethod
static method OnPickUp takes nothing returns nothing
local unit u = GetManipulatingUnit()
local item i = GetManipulatedItem()
local boolean isMelee = IsUnitType(u, UNIT_TYPE_MELEE_ATTACKER)
local integer itemTypeId = GetItemTypeId(i)
if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
set u = null
set i = null
return
endif
//call BJDebugMsg("Pick up")
//----- PERMANENT ITEMS ------
if itemTypeId == AXE_OF_ATTACK then
call LinkBonusToItem(u, BonusDamage.typeid, 8, i)
elseif itemTypeId == SWORD_OF_ATTACK then
call LinkBonusToItem(u, BonusDamage.typeid, 20, i)
elseif itemTypeId == LEATHER_PANTS then
call LinkBonusToItem(u, BonusArmor.typeid, 8, i)
elseif itemTypeId == SCALE_ARMOR then
call LinkBonusToItem(u, BonusArmor.typeid, 20, i)
elseif itemTypeId == STRANGE_AMULET then
call LinkBonusToItem(u, BonusSpellPower.typeid, 8, i)
elseif itemTypeId == SLIGHTLY_MAGICAL_WAND then
call LinkBonusToItem(u, BonusSpellPower.typeid, 30, i)
elseif itemTypeId == BOOTS_OF_SPEED then
if BootsMovementSpeedAllowed(u, BOOTS_OF_SPEED) then
call LinkBonusToItem(u, BONUS_MOVEMENT_SPEED, 40, i)
endif
elseif itemTypeId == DAGGER then
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.10, i)
elseif itemTypeId == SHIV_OF_CRITICAL_STABBING then
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.03, i)
elseif itemTypeId == GEM_OF_LIFE then
call LinkBonusToItem(u, BonusHealth.typeid, 125, i)
elseif itemTypeId == GEM_OF_MANA then
call LinkBonusToItem(u, BonusMana.typeid, 150, i)
elseif itemTypeId == REGENERATING_BRANCH then
call LinkBonusToItem(u, BonusHealthRegen.typeid, 0.75, i)
elseif itemTypeId == MANA_POACH then
call LinkBonusToItem(u, BonusManaRegen.typeid, 0.5, i)
elseif itemTypeId == TABI_OF_AGILITY then
call LinkBonusToItem(u, BonusAgility.typeid, 3, i)
elseif itemTypeId == CLOAK_OF_AGILITY then
call LinkBonusToItem(u, BonusAgility.typeid, 10, i)
elseif itemTypeId == CLUB_OF_STRENGTH then
call LinkBonusToItem(u, BonusStrength.typeid, 3, i)
elseif itemTypeId == HAMMER_OF_STRENGTH then
call LinkBonusToItem(u, BonusStrength.typeid, 10, i)
elseif itemTypeId == WIZARD_HAT_OF_INTELLIGENCE then
call LinkBonusToItem(u, BonusIntelligence.typeid, 3, i)
elseif itemTypeId == STAFF_OF_INTELLIGENCE then
call LinkBonusToItem(u, BonusIntelligence.typeid, 10, i)
elseif itemTypeId == DIADEM_OF_NOBILITY then
call LinkBonusToItem(u, BonusStrength.typeid, 2, i)
call LinkBonusToItem(u, BonusAgility.typeid, 2, i)
call LinkBonusToItem(u, BonusIntelligence.typeid, 2, i)
//----- ARTIFACT ITEMS ------
elseif itemTypeId == AMULET_OF_AMPLIFICATION then
call LinkBonusToItem(u, BonusSpellPower.typeid, 40, i)
call LinkBonusToItem(u, BonusSpellPowerMultiplier.typeid, 0.07, i)
elseif itemTypeId == AMULET_OF_FIRE then
call LinkBonusToItem(u, BonusHealth.typeid, 200, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 15, i)
elseif itemTypeId == ARCANE_ARMOR_OF_BLASTING then
call LinkBonusToItem(u, BonusArmor.typeid, 45, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 65, i)
call LinkBonusToItem(u, BonusIntelligence.typeid, 18, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 15, i)
elseif itemTypeId == ARCANE_STAFF_OF_ENERGY then
call LinkBonusToItem(u, BonusMana.typeid, 350, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 100, i)
call LinkBonusToItem(u, BonusSpellPowerMultiplier.typeid, 0.25, i)
elseif itemTypeId == ARCANE_RING_OF_AMILIFICATION then
call LinkBonusToItem(u, BonusMana.typeid, 450, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 50, i)
call LinkBonusToItem(u, BonusSpellPowerMultiplier.typeid, 0.5, i)
elseif itemTypeId == ARCANE_RING_OF_AMILIFICATION_OFF then
call LinkBonusToItem(u, BonusMana.typeid, 450, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 50, i)
elseif itemTypeId == AXE_OF_THE_BRUTE then
call LinkBonusToItem(u, BonusDamage.typeid, 15, i)
call LinkBonusToItem(u, BonusHealth.typeid, 150, i)
call AddUniqueOnHitOrDestroy(u, AxeOfTheBruteOnHit.create())
elseif itemTypeId == BROOCH_OF_SWIFTNESS then
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.2, i)
if BootsMovementSpeedAllowed(u, BROOCH_OF_SWIFTNESS) then
call LinkBonusToItem(u, BONUS_MOVEMENT_SPEED, 60, i)
endif
elseif itemTypeId == BROOCH_OF_AGILE_SWIFTNESS then
call LinkBonusToItem(u, BonusAgility.typeid, 25, i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.35, i)
if BootsMovementSpeedAllowed(u, BROOCH_OF_AGILE_SWIFTNESS) then
call LinkBonusToItem(u, BONUS_MOVEMENT_SPEED, 80, i)
endif
elseif itemTypeId == BROOCH_OF_FORCEFUL_SWIFTNESS then
call LinkBonusToItem(u, BonusStrength.typeid, 25, i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.35, i)
if BootsMovementSpeedAllowed(u, BROOCH_OF_FORCEFUL_SWIFTNESS) then
call LinkBonusToItem(u, BONUS_MOVEMENT_SPEED, 80, i)
endif
elseif itemTypeId == BROOCH_OF_INSIGHTFUL_SWIFTNESS then
call LinkBonusToItem(u, BonusIntelligence.typeid, 25, i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.35, i)
if BootsMovementSpeedAllowed(u, BROOCH_OF_INSIGHTFUL_SWIFTNESS) then
call LinkBonusToItem(u, BONUS_MOVEMENT_SPEED, 80., i)
endif
elseif itemTypeId == BLACK_NAVAJA then
call LinkBonusToItem(u, BonusDamage.typeid, 22., i)
call LinkBonusToItem(u, BonusAgility.typeid, 5., i)
call LinkBonusToItem(u, BonusHealthRegen.typeid, 1.4, i)
call AddUniqueOnHitOrDestroy(u, BlackNavaja.create())
elseif itemTypeId == BLOOD_TOOTH then
call LinkBonusToItem(u, BonusDamage.typeid, 20., i)
call LinkBonusToItem(u, BonusHealthRegen.typeid, 1.1, i)
if isMelee then
call LinkBonusToItem(u, BonusLifeOnHit.typeid, 4.5, i)
else
call LinkBonusToItem(u, BonusLifeOnHit.typeid, 3.5, i)
endif
elseif itemTypeId == BOOTS_OF_OCCASIONAL_LIGHTNING then
call LinkBonusToItem(u, BonusSpellPower.typeid, 25, i)
if BootsMovementSpeedAllowed(u, BOOTS_OF_OCCASIONAL_LIGHTNING) then
call LinkBonusToItem(u, BONUS_MOVEMENT_SPEED, 60, i)
endif
elseif itemTypeId == BOOTS_OF_OCCASIONAL_FULMINATION then
call LinkBonusToItem(u, BonusSpellPower.typeid, 65, i)
if BootsMovementSpeedAllowed(u, BOOTS_OF_OCCASIONAL_FULMINATION) then
call LinkBonusToItem(u, BONUS_MOVEMENT_SPEED, 80, i)
endif
elseif itemTypeId == CLEAVING_AXE then
call LinkBonusToItem(u, BonusDamage.typeid, 15, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 10, i)
elseif itemTypeId == CRITICAL_DEATH_MASK then
call LinkBonusToItem(u, BonusDamage.typeid, 50, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.10, i)
call LinkBonusToItem(u, BonusCriticalDamage.typeid, 0.35, i)
elseif itemTypeId == CRITICAL_SLICER then
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.03, i)
call LinkBonusToItem(u, BonusCriticalDamage.typeid, 0.12, i)
elseif itemTypeId == CUP_OF_BLOOD then
call LinkBonusToItem(u, BonusHealth.typeid, 225, i)
if GetItemCharges(i) < 100 then
call SetItemCharges(i, 100) //Initial charge-count
endif
call LinkBonusToItem(u, BonusSpellPower.typeid, 5 + 0.1 * GetItemCharges(i), i)
elseif itemTypeId == DAGGER_OF_FAST_STABBING then
call LinkBonusToItem(u, BonusAgility.typeid, 6, i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.18, i)
call LinkBonusToItem(u, BonusDamage.typeid, 18, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 8, i)
call EnableTrigger( gg_trg_DaggerOfFastStabbing_onhit )
elseif itemTypeId == DARK_BLADE_OF_SOUL_REAPING then
call LinkBonusToItem(u, BonusDamage.typeid, 35., i)
call LinkBonusToItem(u, BonusAgility.typeid, 18., i)
call LinkBonusToItem(u, BonusHealthRegen.typeid, 1.8, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 10., i)
call AddUniqueOnHitOrDestroy(u, DarkBladeOfSoulReaping.create())
elseif itemTypeId == DEATH_DEALER then
call LinkBonusToItem(u, BonusDamage.typeid, 25, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.05, i)
elseif itemTypeId == DEATH_DIRK_OF_BLOOD then
call LinkBonusToItem(u, BonusDamage.typeid, 55, i)
call LinkBonusToItem(u, BonusHealthRegen.typeid, 2.0, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.08, i)
call LinkBonusToItem(u, BonusCriticalDamage.typeid, 0.25, i)
call AddUniqueOnHitOrDestroy(u, DeathDirkOfBlood.create())
elseif itemTypeId == DRAGON_STAFF_OF_FLAMES then
call LinkBonusToItem(u, BonusDamage.typeid, 35, i)
call LinkBonusToItem(u, BonusHealth.typeid, 350, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 45, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 10, i)
elseif itemTypeId == ENT_BARK_SHIELD then
call LinkBonusToItem(u, BonusHealth.typeid, 500, i)
call LinkBonusToItem(u, BonusHealthRegen.typeid, 2.0, i)
call LinkBonusToItem(u, BonusArmor.typeid, 25, i)
call LinkBonusToItem(u, BonusHealthStone.typeid, 0.0125, i)
elseif itemTypeId == ENDLESS_ELIXIR_HEALING then
call LinkBonusToItem(u, BonusAgility.typeid, 14, i)
call LinkBonusToItem(u, BonusStrength.typeid, 14, i)
call LinkBonusToItem(u, BonusIntelligence.typeid, 14, i)
call LinkBonusToItem(u, BonusHealth.typeid, 400, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 40, i)
elseif itemTypeId == ENDLESS_ELIXIR_MANA then
call LinkBonusToItem(u, BonusAgility.typeid, 14, i)
call LinkBonusToItem(u, BonusStrength.typeid, 14, i)
call LinkBonusToItem(u, BonusIntelligence.typeid, 14, i)
call LinkBonusToItem(u, BonusMana.typeid, 400, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 40, i)
elseif itemTypeId == FLAMING_PAULDRONS_OF_RADIANT_HEAT then
call LinkBonusToItem(u, BonusArmor.typeid, 45, i)
call LinkBonusToItem(u, BonusHealth.typeid, 450, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 50, i)
elseif itemTypeId == FROZEN_SKULL_OF_NOVA then
call LinkBonusToItem(u, BonusStrength.typeid, 15, i)
call LinkBonusToItem(u, BonusMana.typeid, 400, i)
call LinkBonusToItem(u, BonusManaRegen.typeid, 2.0, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 50, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 20, i)
elseif itemTypeId == GNARLT_STINGER_OF_ACUTE_TOXICITY then
call LinkBonusToItem(u, BonusDamage.typeid, 30, i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.35, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.12, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 10, i)
elseif itemTypeId == GOBLET_OF_FIRE then
call LinkBonusToItem(u, BonusHealth.typeid, 400, i)
call LinkBonusToItem(u, BonusMana.typeid, 350, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 75, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 20, i)
elseif itemTypeId == GOLDEN_GOBLET_OF_GORE then
if GetItemCharges(i) < 250 then
call SetItemCharges(i, 250) //Initial charge-count
endif
call LinkBonusToItem(u, BonusSpellPower.typeid, 40 + 0.1 * GetItemCharges(i), i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 0.04 * GetItemCharges(i), i)
call LinkBonusToItem(u, BonusHealth.typeid, 650, i)
call LinkBonusToItem(u, BonusLifeOnKillFlat.typeid, 1.0, i)
call LinkBonusToItem(u, BonusLifeOnKillMissing.typeid, 0.003, i)
elseif itemTypeId == GOLDEN_SHIELD_OF_AEGIS then
call LinkBonusToItem(u, BonusArmor.typeid, 30, i)
call LinkBonusToItem(u, BonusHealth.typeid, 375, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 20, i)
elseif itemTypeId == HEAVY_PLATE_ARMOR then
call LinkBonusToItem(u, BonusArmor.typeid, 35, i)
elseif itemTypeId == HEAVY_SHIELD_OF_STRENGTH then
call LinkBonusToItem(u, BonusHealth.typeid, 225, i)
call LinkBonusToItem(u, BonusStrength.typeid, 5, i)
call LinkBonusToItem(u, BonusArmor.typeid, 20, i)
elseif itemTypeId == INCONVENIENTLY_HEAVY_SHIELD then
call LinkBonusToItem(u, BonusHealth.typeid, 500, i)
call LinkBonusToItem(u, BonusStrength.typeid, 18, i)
call LinkBonusToItem(u, BonusArmor.typeid, 35, i)
elseif itemTypeId == INFINITE_VIAL_HEALING then
call LinkBonusToItem(u, BonusAgility.typeid, 5, i)
call LinkBonusToItem(u, BonusStrength.typeid, 5, i)
call LinkBonusToItem(u, BonusIntelligence.typeid, 5, i)
elseif itemTypeId == INFINITE_VIAL_MANA then
call LinkBonusToItem(u, BonusAgility.typeid, 5, i)
call LinkBonusToItem(u, BonusStrength.typeid, 5, i)
call LinkBonusToItem(u, BonusIntelligence.typeid, 5, i)
elseif itemTypeId == LIFE_STAFF then
call LinkBonusToItem(u, BonusDamage.typeid, 30, i)
call LinkBonusToItem(u, BonusHealth.typeid, 400, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 40, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 15, i)
elseif itemTypeId == MANA_STEEL_SWORD then
call LinkBonusToItem(u, BonusDamage.typeid, 25, i)
call LinkBonusToItem(u, BonusMana.typeid, 250, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 12, i)
elseif itemTypeId == MAGE_ARMOR then
call LinkBonusToItem(u, BonusIntelligence.typeid, 5, i)
call LinkBonusToItem(u, BonusArmor.typeid, 25, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 12, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 10, i)
elseif itemTypeId == MAGE_BLADE then
call LinkBonusToItem(u, BonusIntelligence.typeid, 6, i)
call LinkBonusToItem(u, BonusMana.typeid, 400, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 12, i)
if GetUnitStateSwap(UNIT_STATE_MANA, u) > 0.2 * MaxMana(u) then
call LinkBonusToItem(u, BonusDamage.typeid, 25 + 0.02 * MaxMana(u), i)
else
call LinkBonusToItem(u, BonusDamage.typeid, 25, i)
endif
call EnableTrigger( gg_trg_MageBlade_onhit )
elseif itemTypeId == MAGE_BLADE_OFF then
call LinkBonusToItem(u, BonusIntelligence.typeid, 6, i)
call LinkBonusToItem(u, BonusMana.typeid, 400, i)
call LinkBonusToItem(u, BonusDamage.typeid, 25, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 12, i)
elseif itemTypeId == MAGI_SWORD then
call LinkBonusToItem(u, BonusIntelligence.typeid, 15, i)
call LinkBonusToItem(u, BonusMana.typeid, 600, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 12, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.07, i)
if GetUnitStateSwap(UNIT_STATE_MANA, u) > 0.2 * MaxMana(u) then
call LinkBonusToItem(u, BonusDamage.typeid, 45 + 0.04 * MaxMana(u), i)
else
call LinkBonusToItem(u, BonusDamage.typeid, 45, i)
endif
call EnableTrigger( gg_trg_MagiSword_onhit )
elseif itemTypeId == MAGI_SWORD_OFF then
call LinkBonusToItem(u, BonusIntelligence.typeid, 15, i)
call LinkBonusToItem(u, BonusMana.typeid, 600, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 12, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.07, i)
call LinkBonusToItem(u, BonusDamage.typeid, 45, i)
elseif itemTypeId == MAGICAL_AMULET then
call LinkBonusToItem(u, BonusMana.typeid, 200, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 15, i)
elseif itemTypeId == MAGICAL_DAGGER then
call LinkBonusToItem(u, BonusDamage.typeid, 15, i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.15, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 5, i)
elseif itemTypeId == MANAFLOWIFICATOR then
call LinkBonusToItem(u, BonusMana.typeid, 800, i)
call LinkBonusToItem(u, BonusManaStone.typeid, 0.01, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 25, i)
elseif itemTypeId == MAUL_OF_FORCEFUL_IMPACT then
call LinkBonusToItem(u, BonusStrength.typeid, 7., i)
call LinkBonusToItem(u, BonusHealthRegen.typeid, 1.5, i)
call LinkBonusToItem(u, BonusDamage.typeid, 15., i)
elseif itemTypeId == MAUL_OF_TREMENDOUS_IMPACT then
call LinkBonusToItem(u, BonusStrength.typeid, 25, i)
call LinkBonusToItem(u, BonusHealthRegen.typeid, 2.5, i)
call LinkBonusToItem(u, BonusDamage.typeid, 0.0175 * MaxHp(u), i)
elseif itemTypeId == MERCURIAL_SCIMITAR_OF_INFINITE_SHARPNESS then
call LinkBonusToItem(u, BonusDamage.typeid, 35 + 4 * GetItemCharges(i), i)
call LinkBonusToItem(u, BonusCriticalDamage.typeid, 0.01 * GetItemCharges(i), i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.35, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.08, i)
call EnableTrigger( gg_trg_Mercurial_Scimitar_of_Infinite_Sharpness_onhit )
elseif itemTypeId == MIGHTY_SKULL_OF_INSIGHT then
call LinkBonusToItem(u, BonusStrength.typeid, 5, i)
call LinkBonusToItem(u, BonusManaRegen.typeid, 1.0, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 12, i)
elseif itemTypeId == MECHANICAL_DEVICE_OF_CONVINSING_CLONING then
call LinkBonusToItem(u, BonusHealth.typeid, 350, i)
call LinkBonusToItem(u, BonusMana.typeid, 400, i)
call LinkBonusToItem(u, BonusArmor.typeid, 40, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 35, i)
elseif itemTypeId == OBSERVING_STAFF_OF_THE_GREAT_EYE then
call LinkBonusToItem(u, BonusSpellPower.typeid, 60, i)
call LinkBonusToItem(u, BonusSpellPowerMultiplier.typeid, 0.15, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 30, i)
call LinkBonusToItem(u, BonusArmor.typeid, 35, i)
elseif itemTypeId == OMNI_GEM then
call LinkBonusToItem(u, BonusHealth.typeid, 175, i)
call LinkBonusToItem(u, BonusMana.typeid, 175, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 17, i)
elseif itemTypeId == PHOENIX_AXE then
call LinkBonusToItem(u, BonusDamage.typeid, 40, i)
call LinkBonusToItem(u, BonusHealth.typeid, 400, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 25, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 18, i)
if not HaveSavedInteger(udg_item_hash, GetHandleId(u), StringHash("phoenixaxe")) then
call SaveInteger(udg_item_hash, GetHandleId(u), StringHash("phoenixaxe"), 0)
call EnableTrigger( gg_trg_PhoenixAxeOnHit )
endif
elseif itemTypeId == POWER_HAMMER_OF_FABRICATION then
call LinkBonusToItem(u, BonusDamage.typeid, 45, i)
call LinkBonusToItem(u, BonusMana.typeid, 500, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 45, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 15, i)
elseif itemTypeId == REACTIVE_ARMOR_OF_ARCANE_REPULSION then
call LinkBonusToItem(u, BonusHealth.typeid, 400, i)
call LinkBonusToItem(u, BonusArmor.typeid, 40, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 30, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 20, i)
elseif itemTypeId == RING_OF_LIFE then
call LinkBonusToItem(u, BonusHealth.typeid, 200, i)
call LinkBonusToItem(u, BonusHealthStone.typeid, 0.0075, i)
call LinkBonusToItem(u, BonusHealthRegen.typeid, 1.0, i)
elseif itemTypeId == RING_OF_MANA then
call LinkBonusToItem(u, BonusMana.typeid, 250, i)
call LinkBonusToItem(u, BonusManaStone.typeid, 0.0075, i)
call LinkBonusToItem(u, BonusManaRegen.typeid, 1.0, i)
elseif itemTypeId == RING_OF_POWER then
call LinkBonusToItem(u, BonusHealth.typeid, 750, i)
call LinkBonusToItem(u, BonusMana.typeid, 750, i)
call LinkBonusToItem(u, BonusManaStone.typeid, 0.0125, i)
call LinkBonusToItem(u, BonusHealthStone.typeid, 0.0125, i)
call GroupAddUnit(LoadGroupHandle(udg_item_hash, StringHash("Ring of Power wearers"), 0), u)
call EnableTrigger( gg_trg_RingOfPowerEvent )
elseif itemTypeId == SMALL_SHIELD then
call LinkBonusToItem(u, BonusArmor.typeid, 12, i)
call LinkBonusToItem(u, BonusHealth.typeid, 200, i)
elseif itemTypeId == SPIKED_PLATE_OF_VENGEANCE then
call LinkBonusToItem(u, BonusArmor.typeid, 70, i)
call LinkBonusToItem(u, BonusHealth.typeid, 350, i)
elseif itemTypeId == STAFF_OF_INSIGHT then
call LinkBonusToItem(u, BonusIntelligence.typeid, 7, i)
call LinkBonusToItem(u, BonusMana.typeid, 240, i)
call LinkBonusToItem(u, BonusManaRegen.typeid, 1.25, i)
call LinkBonusToItem(u, BonusSpellPower.typeid, 12, i)
elseif itemTypeId == STAFF_OF_SUPREME_INSIGHT then
call LinkBonusToItem(u, BonusIntelligence.typeid, 24, i)
call LinkBonusToItem(u, BonusMana.typeid, 360, i)
call LinkBonusToItem(u, BonusManaRegen.typeid, 2.0, i)
if CountItemsOfType(u, STAFF_OF_SUPREME_INSIGHT) == 1 then
call LinkBonusToItem(u, BonusSpellPower.typeid, 0.024 * MaxMana(u), i)
endif
elseif itemTypeId == SUPREME_DAGGER_OF_SWIFT_STABBING then
call LinkBonusToItem(u, BonusAgility.typeid, 24, i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.35, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 12, i)
if CountItemsOfType(u, SUPREME_DAGGER_OF_SWIFT_STABBING) == 1 then
call LinkBonusToItem(u, BonusDamage.typeid, 1.5 * GetHeroBonusAgi(u), i)
endif
call EnableTrigger( gg_trg_SupremeDaggerOfSwiftStabbing_onhit )
elseif itemTypeId == SWIFT_BLADE_OF_QUICKENING then
call LinkBonusToItem(u, BonusDamage.typeid, 30, i)
call LinkBonusToItem(u, BonusAttackSpeed.typeid, 0.3, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.05, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 15, i)
call EnableTrigger( gg_trg_Swift_Blade_of_Quickening_OnHit )
elseif itemTypeId == SOUL_SIPHONING_SWORD_OF_SLAUGHTER then
call LinkBonusToItem(u, BonusDamage.typeid, 70, i)
call LinkBonusToItem(u, BonusSpellHaste.typeid, 20, i)
call LinkBonusToItem(u, BonusMana.typeid, 600, i)
call LinkBonusToItem(u, BonusManaStone.typeid, 0.01, i)
elseif itemTypeId == SPIKED_SMASHER_OF_ARMOR_DESTRUCTION then
call LinkBonusToItem(u, BonusDamage.typeid, 40, i)
call LinkBonusToItem(u, BonusHealth.typeid, 275, i)
call LinkBonusToItem(u, BonusArmorPenetrationPercent.typeid, 0.20, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.06, i)
call AddUniqueOnHitOrDestroy(u, SpikedSmasherOfArmorDestructionOnHit.create())
elseif itemTypeId == THUNDER_CLAWS then
call LinkBonusToItem(u, BonusAgility.typeid, 5, i)
call LinkBonusToItem(u, BonusManaRegen.typeid, 1.0, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.05, i)
call LinkBonusToItem(u, BonusCriticalDamage.typeid, 0.15, i)
elseif itemTypeId == THUNDER_DEATH_BLADE then
call LinkBonusToItem(u, BonusAgility.typeid, 16, i)
call LinkBonusToItem(u, BonusDamage.typeid, 30, i)
call LinkBonusToItem(u, BonusManaRegen.typeid, 2.0, i)
call LinkBonusToItem(u, BonusCriticalChance.typeid, 0.10, i)
call LinkBonusToItem(u, BonusCriticalDamage.typeid, 0.24, i)
elseif itemTypeId == WAND_OF_BLASTING then
call LinkBonusToItem(u, BonusSpellPower.typeid, 40, i)
endif
set i = null
set u = null
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.OnPickUp)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.OnDropItem)
endmethod
endstruct
function RefreshItem takes unit u, item i returns nothing
call ItemsDefinitions.RefreshItem(u, i)
endfunction
endscope
function UpdateItemsForUnit takes unit u, unit unused returns nothing
local integer i = 0
//call DisableTrigger( gg_trg_ItemUpdate )
set isRefreshAction = true
loop
call RefreshItem(u, UnitItemInSlot(u, i))
set i = i + 1
exitwhen i > bj_MAX_INVENTORY
endloop
set isRefreshAction = false
//call EnableTrigger( gg_trg_ItemUpdate )
endfunction
function Item_ClearThunderDeathBladeDelayed takes unit target, unit source returns nothing
call ClearFlag(target, "ThunderDeathBladeCooldown")
endfunction
function Item_ClearThunderClawsDelayed takes unit target, unit source returns nothing
call ClearFlag(target, "ThunderClawsCooldown")
endfunction
function Item_ClearLifeStaffDelayed takes unit target, unit source returns nothing
call ClearFlag(target, "LifeStaffCooldown")
endfunction
function Item_ClearReactiveArmorOfArcaneRepulsionOnHitDelayed takes unit target, unit source returns nothing
call ClearFlag(target, "ReactiveArmorOfArcaneRepulsionOnHitCooldown")
endfunction
function Item_ClearSwiftBladeofQuickeningDelayed takes unit target, unit source returns nothing
call ClearFlag(target, "Swift Blade of Quickening Cooldown")
endfunction
function Item_ClearDarkBladeOfSoulReapingDelayed takes unit target, unit source returns nothing
call ClearFlag(target, "Dark Blade of Soul Reaping Cooldown")
endfunction
function Item_ClearMercurialScimitarOfInfiniteSharpnessDelayed takes unit target, unit source returns nothing
call ClearFlag(target, "Mercurial Scimitar of Infinite Sharpness Cooldown")
endfunction
function Item_ClearGobletOfFireDelayed takes unit target, unit source returns nothing
call ClearFlag(target, "Goblet of Fire Cooldown")
endfunction
scope DragonStaffOfFlames
private struct FireBolt extends Missiles
static group unitsWithProjectiles = CreateGroup()
method dealDamage takes nothing returns nothing
local group targetsWithinRadius = CreateGroup()
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 150., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if BlzIsUnitSelectable(u) and IsUnitEnemy(u, owner) and IsUnitInRangeXY(u, x, y, 75.) then
call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup(targetsWithinRadius)
call GroupRemoveUnit(unitsWithProjectiles, source)
set targetsWithinRadius = null
set u = null
endmethod
method onFinish takes nothing returns boolean
call dealDamage()
return true
endmethod
method onHit takes unit hit returns boolean
if BlzIsUnitSelectable(hit) and IsUnitEnemy(hit, owner) then
call dealDamage()
return true
endif
return false
endmethod
static method fireNewMissile takes nothing returns nothing
local unit c = udg_DamageEventSource
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real z = GetUnitFlyHeight(c) + 70.
local real tx = GetUnitX(udg_DamageEventTarget)
local real ty = GetUnitY(udg_DamageEventTarget)
local real tz = GetUnitFlyHeight(udg_DamageEventTarget) + 70.
local thistype this = thistype.create(x, y, z, tx, ty, tz)
call GroupAddUnit(unitsWithProjectiles, c)
set source = c
set model = "Vfx\\Firebolt Minor.mdx"
set speed = 675
set arc = 10
set collision = 24
if IsUnitType(c, UNIT_TYPE_HERO) then
set damage = 30. + 0.20 * GetTotalDamage(c) + 0.35 * GetSpellPower(c)
else
set damage = 30. + 0.20 * GetTotalDamage(c)
endif
set owner = GetOwningPlayer(c)
call launch()
call BlzSetSpecialEffectScale( effect.effect, 0.50 )
set c = null
endmethod
endstruct
function fireDragonStaffOfFlamesFireBoltIfPossible takes nothing returns nothing
if not IsUnitInGroup(udg_DamageEventSource, FireBolt.unitsWithProjectiles) then
call FireBolt.fireNewMissile()
endif
endfunction
endscope
struct Item_GnarlyStingerOfAcuteToxicityDoT
unit target
unit source
real damagePerTick
timer tTick
timer tEnd
method destroy takes nothing returns nothing
call ReleaseTimer(tTick)
call ReleaseTimer(tEnd)
set tTick = null
set tEnd = null
set target = null
set source = null
call .deallocate()
endmethod
static method tearDown takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call .destroy()
endmethod
method dealDamage takes nothing returns nothing
local real angle = GetRandomReal(0, 2 * bj_PI)
local real x = GetUnitX(target) + 36 * Cos(angle)
local real y = GetUnitY(target) + 36 * Sin(angle)
local texttag text = CreateTextTag()
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, 0.70)
call SetTextTagFadepoint(text, 0.38)
call SetTextTagVelocityBJ(text, 128, GetRandomReal(80, 100))
call SetTextTagColor(text, 190, 215, 66, 170)
if damagePerTick < 100 then
call SetTextTagText(text, R2SW(damagePerTick, 0, 1), TextTagSize2Height(5.5))
else
call SetTextTagText(text, R2SW(damagePerTick, 0, 0), TextTagSize2Height(5.5))
endif
call SetTextTagPos(text, x, y, GetRandomReal(96, 128))
set udg_NextDamageType = udg_DamageTypePure
call UnitDamageTarget(source, target, damagePerTick, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_POISON, null)
set text = null
endmethod
static method tick takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if UnitAlive(target) then
call .dealDamage()
else
call .destroy()
endif
endmethod
static method setup takes unit target, unit source, real totalDamagedealt returns nothing
local thistype this = thistype.allocate()
set .tTick = NewTimerEx(this)
set .tEnd = NewTimerEx(this)
set .target = target
set .source = source
set .damagePerTick = 0.33 * totalDamagedealt / 10.0 //6 seconds, 2 ticks per second.
call TimerStart(tTick, 0.5, true, function thistype.tick)
call TimerStart(tEnd, 5.1, false, function thistype.tearDown)
endmethod
endstruct
function MageBladeUpdateOnHit takes unit u returns nothing
local integer i = 0
local item tempItem
if GetUnitStateSwap(UNIT_STATE_MANA, u) < 0.2 * MaxMana(u) then
call DisableTrigger( gg_trg_ItemUpdate )
loop
set tempItem = UnitItemInSlot(u, i)
if GetItemTypeId(tempItem) == MAGE_BLADE then
call RefreshItem(u, tempItem)
endif
set i = i + 1
exitwhen i > bj_MAX_INVENTORY
endloop
set tempItem = null
call EnableTrigger( gg_trg_ItemUpdate )
endif
endfunction
function MagiSwordUpdateOnHit takes unit u returns nothing
local integer i = 0
local item tempItem
if GetUnitStateSwap(UNIT_STATE_MANA, u) < 0.2 * MaxMana(u) then
call DisableTrigger( gg_trg_ItemUpdate )
loop
set tempItem = UnitItemInSlot(u, i)
if GetItemTypeId(tempItem) == MAGI_SWORD then
call RefreshItem(u, tempItem)
endif
set i = i + 1
exitwhen i > bj_MAX_INVENTORY
endloop
set tempItem = null
call EnableTrigger( gg_trg_ItemUpdate )
endif
endfunction
function item_mercurialScimitarOfInfiniteSharpnessReset takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer unitIndex = GetTimerData(t)
local unit u = udg_UDexUnits[unitIndex]
local integer i = 0
local item tempItem
call DisableTrigger( gg_trg_ItemUpdate )
loop
set tempItem = UnitItemInSlot(u, i)
if GetItemTypeId(tempItem) == MERCURIAL_SCIMITAR_OF_INFINITE_SHARPNESS then
call SetItemCharges(tempItem, 0)
call RefreshItem(u, tempItem)
endif
set i = i + 1
exitwhen i > bj_MAX_INVENTORY
endloop
call EnableTrigger( gg_trg_ItemUpdate )
set tempItem = null
call ReleaseTimer(t)
call RemoveSavedHandle(udg_item_hash, GetHandleId(u), StringHash("Mercurial Scimitar of Infinite Sharpness timer"))
call RemoveSavedInteger(udg_item_hash, GetHandleId(u), StringHash("Mercurial Scimitar of Infinite Sharpness attack counter"))
set t = null
set u = null
endfunction
scope DiabolicSpecialFireBolder
private struct FireBolder extends Missiles
method explode takes nothing returns nothing
call damageAoENoFFNoBuildingDamage(.source, .x, .y, .damage, 275, DAMAGE_TYPE_FIRE)
endmethod
method onHit takes unit u returns boolean
if BlzIsUnitSelectable(u) and not BlzIsUnitInvulnerable(u) and not IsUnitAlly(u, owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
call explode()
return true
endif
return false
endmethod
method onFinish takes nothing returns boolean
call explode()
return true
endmethod
static method accelerateBolder takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if allocated and speed < 400.0 then
set speed = speed + 7.0
else
call ReleaseTimer(t)
endif
set t = null
endmethod
public static method sendIt takes unit u, real angle returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real z = GetUnitFlyHeight(u) + 40.0
local real tx = x + 1500.0 * Cos(angle)
local real ty = y + 1500.0 * Sin(angle)
local thistype this = thistype.create(x, y, z, tx, ty, z)
local timer t = NewTimerEx(this)
set source = u
set owner = GetOwningPlayer(u)
set model = "vfx\\FelMeteor.mdx"
set speed = 40.0
set collision = 80
//https://www.wolframalpha.com/input/?i=x+%3D+1%2C10%2C20%2C30%2C40+for+%2833+%2B+4+*+%281+%2B+x+*+0.05%29+*+x%29
//x = 1,10,20,30,40 for (88 + 8 * (1 + x * 0.03) * x)
//{96.24, 192, 344, 544, 792}
set damage = (130.0 + 10.0 * (1.0 + udg_current_wave_nr * 0.025) * udg_current_wave_nr) * udg_difficulty_dmg_factor
call TimerStart(t, 0.04, true, function thistype.accelerateBolder)
call launch()
endmethod
endstruct
struct SummonBolder
static group summoners = CreateGroup()
static timer mainTimer = null
static integer currentSummonIndex
unit summoner
effect indicator
real x
real y
private static method FinishFireBolderSummoning takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local group g = CreateGroup()
local real tx
local real ty
local boolean hasFired = false
local unit u
call GroupEnumUnitsInRange(g, x, y, 950.0, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if UnitAlive(u) and not IsUnitAlly(u, GetOwningPlayer(summoner)) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
set tx = GetUnitX(u)
set ty = GetUnitY(u)
call FireBolder.sendIt(summoner, Atan2(ty - y, tx - x)) //angle towards target
set hasFired = true
set u = null
exitwhen true
endif
call GroupRemoveUnit(g,u)
endloop
if not hasFired then
//No target within radius, send it towards center because... what else?
call FireBolder.sendIt(summoner, Atan2(-y, -x)) //angle towards target
endif
call DestroyEffect(indicator)
call DestroyGroup(g)
call ReleaseTimer(t)
set indicator = null
set summoner = null
set g = null
set t = null
call deallocate()
endmethod
private static method StartFireBolder takes unit u returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
local real angle = GetRandomReal(0.0, 2.0 * bj_PI)
set .x = GetUnitX(u) + 50.0 * Cos(angle)
set .y = GetUnitY(u) + 50.0 * Sin(angle)
set .summoner = u
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl", x, y))
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Undead\\DarkRitual\\DarkRitualCaster.mdl", GetUnitX(u), GetUnitY(u)))
set .indicator = AddSpecialEffect("vfx\\Spell Marker TC.mdx", x, y)
call BlzSetSpecialEffectColorByPlayer(indicator, GetOwningPlayer(u))
call TimerStart(t, 2.0, false, function thistype.FinishFireBolderSummoning)
set t = null
endmethod
private static method CheckForSummoning takes nothing returns nothing
local unit u
local integer summonerSize
loop
set u = BlzGroupUnitAt(summoners, currentSummonIndex)
exitwhen u == null
if UnitAlive(u) then
call StartFireBolder(u)
set currentSummonIndex = currentSummonIndex + 1
set u = null
exitwhen true
else
call GroupRemoveUnit(summoners, u)
set currentSummonIndex = 0
endif
endloop
set summonerSize = BlzGroupGetSize(summoners)
if summonerSize > 0 then
call TimerStart(mainTimer, GetRandomReal(8.0, 13.0) / summonerSize, false, function thistype.CheckForSummoning)
if currentSummonIndex >= summonerSize then
set currentSummonIndex = 0
endif
else
call ReleaseTimer(mainTimer)
set mainTimer = null
endif
endmethod
public static method AddSummonner takes unit u returns nothing
call GroupAddUnit(summoners, u)
if mainTimer == null then
set currentSummonIndex = 0
set mainTimer = NewTimer()
//Initial "Cooldown"
call TimerStart(mainTimer, GetRandomReal(10.0, 16.0), false, function thistype.CheckForSummoning)
endif
endmethod
endstruct
function DiabolicSpecialFireBolderAdd takes unit u returns nothing
call SummonBolder.AddSummonner(u)
endfunction
endscope
scope DiabolicSpecialHoundMaster
globals
constant real CHAIN_START_DISTANCE = 425.0
constant real CHAIN_MAX_DISTANCE = 600.0
constant real CHAIN_BREAK_DISTANCE = 625.0
constant group diabolicSpecialHoundMastersLookingToChain = CreateGroup()
endglobals
public keyword DiabolicSpecialHoundMaster_LookForChainTargetsFor
private struct Chain
private static timer t = CreateTimer()
private static thistype array chains
static group chainedUnits = CreateGroup()
private unit master
private unit target
private lightning vfx
public static method IsUnitChained takes unit target returns boolean
return chains[GetUnitUserData(target)] != 0 and chains[GetUnitUserData(target)].target == target
endmethod
method destroy takes nothing returns nothing
//clean up everything
call DestroyLightning(vfx)
call GroupRemoveUnit(chainedUnits, target)
set chains[GetUnitUserData(target)] = 0
set master = null
set target = null
set vfx = null
call deallocate()
endmethod
method update takes nothing returns nothing
local real mx = GetUnitX(master)
local real my = GetUnitY(master)
local real mz = BlzGetUnitZ(master)
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local real tz = BlzGetUnitZ(target)
local real dx = tx - mx
local real dy = ty - my
local real distance = SquareRoot(dx * dx + dy * dy)
local real angle
//call BJDebugMsg("Updateing Chain to " + GetUnitName(target))
if UnitAlive(master) and UnitAlive(target) then
call DestroyLightning(vfx)
set .vfx = AddLightningEx("WHCA", false, mx, my, mz + 60.0, tx, ty, tz + 60.0)
call SetLightningColor(.vfx, 0.25, 0.25, 0.25, 1.0) //Gray, solid, chain
if distance > CHAIN_MAX_DISTANCE then
//call BJDebugMsg("OUTSIDE MAX RANGE, target=" + GetUnitName(target))
if distance > CHAIN_BREAK_DISTANCE and GetUnitTypeId(target) != HOUND_MASTER_WOLF_UNIT then
call DiabolicSpecialHoundMaster_LookForChainTargetsFor.evaluate(master)
call destroy()
else
set angle = Atan2(dy, dx)
call SetUnitX(target, mx + CHAIN_MAX_DISTANCE * Cos(angle))
call SetUnitY(target, my + CHAIN_MAX_DISTANCE * Sin(angle))
endif
//set udg_Knockback2DUnit = target
//set udg_Knockback2DAngle = bj_RADTODEG * Atan2(dy, dx)
//set udg_Knockback2DDistance = (distance - 500)
//set udg_Knockback2DTime = 0.10 + udg_Knockback2DDistance / 650.0
//set udg_Knockback2DCollision = 8.0
//set udg_Knockback2DFX = ""
//set udg_Knockback2DUnbiasedCollision = true
//call TriggerExecute(gg_trg_Knockback_2D)
endif
else
call destroy()
endif
endmethod
static method updateAll takes nothing returns nothing
local group tempGroup
local unit u
local integer numberOfChains = BlzGroupGetSize(chainedUnits)
//call BJDebugMsg("Chains Updating: Count=" + I2S(numberOfChains))
if numberOfChains == 0 then
//call BJDebugMsg("No chained units still exists, pausing...")
call PauseTimer(t)
return
endif
set tempGroup = CreateGroup()
call BlzGroupAddGroupFast(chainedUnits, tempGroup)
loop
set u = FirstOfGroup(tempGroup)
exitwhen u == null
call chains[GetUnitUserData(u)].update()
call GroupRemoveUnit(tempGroup, u)
endloop
call DestroyGroup(tempGroup)
set tempGroup = null
endmethod
public static method ChainUnit takes unit master, unit target returns nothing
local thistype this = thistype.allocate()
set .master = master
set .target = target
set .vfx = AddLightningEx("WHCA", true, GetUnitX(master), GetUnitY(master), BlzGetUnitZ(master) + 60., GetUnitX(target), GetUnitY(target), BlzGetUnitZ(target) + 60.)
call SetLightningColor(.vfx, 0.25, 0.25, 0.25, 1.0) //Gray, solid, chain
if BlzGroupGetSize(chainedUnits) == 0 then
call TimerStart(t, 1. / 45., true, function thistype.updateAll)
endif
call GroupAddUnit(chainedUnits, target)
set chains[GetUnitUserData(target)] = this
endmethod
endstruct
function DiabolicSpecialChainUnit takes unit master, unit target returns nothing
//call BJDebugMsg(GetUnitName(master) + " chaining " + GetUnitName(target))
call Chain.ChainUnit(master, target)
endfunction
function DiabolicSpecialsHoundMasterLookForTargets_SpecificUnit takes unit master returns nothing
local real x = GetUnitX(master)
local real y = GetUnitY(master)
local group tempGroup = CreateGroup()
local unit u
local player owner = GetOwningPlayer(master)
call GroupEnumUnitsInRange(tempGroup, x, y, CHAIN_START_DISTANCE, null)
loop
set u = FirstOfGroup(tempGroup)
exitwhen u == null
if IsUnitType(u, UNIT_TYPE_HERO) and not IsUnitAlly(u, owner) and not Chain.IsUnitChained(u) and UnitAlive(u) then
call DiabolicSpecialChainUnit(master, u)
call GroupRemoveUnit(diabolicSpecialHoundMastersLookingToChain, master)
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl", x, y))
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl", GetUnitX(u), GetUnitY(u)))
set u = null
exitwhen true
endif
call GroupRemoveUnit(tempGroup, u)
endloop
call DestroyGroup(tempGroup)
set owner = null
set tempGroup = null
set u = null
endfunction
function DiabolicSpecialsHoundMasterLookForTargets takes nothing returns nothing
local group tempGroup
local unit u
if BlzGroupGetSize(diabolicSpecialHoundMastersLookingToChain) == 0 then
call ReleaseTimer(GetExpiredTimer())
else
set tempGroup = CreateGroup()
call BlzGroupAddGroupFast(diabolicSpecialHoundMastersLookingToChain, tempGroup)
loop
set u = FirstOfGroup(tempGroup)
exitwhen u == null
if UnitAlive(u) then
call DiabolicSpecialsHoundMasterLookForTargets_SpecificUnit(u)
else
call GroupRemoveUnit(diabolicSpecialHoundMastersLookingToChain, u)
endif
call GroupRemoveUnit(tempGroup, u)
endloop
call DestroyGroup(tempGroup)
set tempGroup = null
endif
endfunction
//Needs to be called externally! Don't really know how to solve otherwise xD
//References triggers above it, so cannot move up to "onInit". Would have to create another struct or lib in order to "onInit" otherwise
function DiabolicSpecialHoundMaster_Init takes nothing returns nothing
//call TimerStart(NewTimer(), 1.37, true, function DiabolicSpecialsHoundMasterLookForTargets)
endfunction
public function DiabolicSpecialHoundMaster_LookForChainTargetsFor takes unit master returns nothing
if BlzGroupGetSize(diabolicSpecialHoundMastersLookingToChain) == 0 then
call TimerStart(NewTimer(), 0.67, true, function DiabolicSpecialsHoundMasterLookForTargets)
endif
call GroupAddUnit(diabolicSpecialHoundMastersLookingToChain, master)
endfunction
function DiabolicSpecialHoundMaster_CreateHound takes unit master returns nothing
local unit hound = CreateUnit(GetOwningPlayer(master), HOUND_MASTER_WOLF_UNIT, GetUnitX(master) + GetRandomReal(-100., 100.), GetUnitY(master) + GetRandomReal(-100., 100.), 0.)
call GroupAddUnit(udg_current_wave_unit_group, hound)
//Math: https://www.wolframalpha.com/input/?i=x+%3D+1%2C10%2C20%2C30%2C40+for+%28120+%2B+7.5+*+%281+%2B+x+*+0.04%29+*+x%29
call BlzSetUnitMaxHP(hound, R2I(120. + 10.0 * (1. + udg_current_wave_nr * 0.045) * udg_current_wave_nr))
call BlzSetUnitArmor(hound, 25. + 3.0 * udg_current_wave_nr)
call BlzSetUnitBaseDamage(hound, R2I(14.0 + 1.75 * (1.0 + udg_current_wave_nr * 0.04) * udg_current_wave_nr), 0)
call SetUnitState(hound, UNIT_STATE_LIFE, MaxHp(hound))
call DiabolicSpecialChainUnit(master, hound)
call IssuePointOrderLoc( hound, "attack", udg_center_player_spawn_point )
set hound = null
endfunction
endscope
scope DiabolicSpecialRocker
globals
constant integer ROCK_UNIT_ID = 'n01G'
group diabolic_special_rockers = CreateGroup()
timer diabolic_special_rockers_timer = null
integer diabolic_special_rockers_index = 0
endglobals
private struct BlockingRockProjectile extends Missiles
method onFinish takes nothing returns boolean
//Summon Rock
//Hp: https://www.wolframalpha.com/input?i=x+%3D+1%2C10%2C20%2C30%2C40+for+%28400+%2B+25+*+%281+%2B+x+*+0.02%29+*+x%29
local unit u = CreateUnit(owner, ROCK_UNIT_ID, x, y, GetRandomDirectionDeg())
call BlzSetUnitMaxHP(u, R2I(250 + 20 * (1 + udg_current_wave_nr * 0.02) * udg_current_wave_nr))
call SetUnitState(u, UNIT_STATE_LIFE, MaxHp(u))
call UnitApplyTimedLife(u, 'BTLF', 10.00) //Live for 10 seconds
set u = null
return true
endmethod
public static method sendRock takes unit u, real x, real y, real z, real tx, real ty returns nothing
local thistype this = thistype.create(x, y, z, tx, ty, 0)
local real distance = distanceBetweenXYXY(x, y, tx, ty)
set .source = u
set .owner = GetOwningPlayer(u)
set .model = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
set .speed = 180.0 + GetRandomReal(0.5 * distance, 0.75 * distance)
set .arc = 40
set .collision = 0
call launch()
endmethod
public static method sendIt takes unit u, unit target returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real z = GetUnitFlyHeight(u) + 40
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local real angleToTarget = Atan2(ty - y, tx - x)
local real distance = distanceBetweenXYXY(x, y, tx, ty)
local integer direction = 0 //1 = east, 2 = north, 4 = west, 8 = south, bitmask, I.E. 9 -> South East
local integer style = 0 //GetRandomInt(0, 1) //Maybe more styles in the future? Like an U
local real r1x = 0
local real r1y = 0
local real r2x = 0
local real r2y = 0
local real r3x = 0
local real r3y = 0
local real r4x = 0
local real r4y = 0
if distance + 68 < distanceBetweenXYXY(x, y, tx + 100, ty) then
set direction = BlzBitOr(direction, 1)
endif
if distance + 68 < distanceBetweenXYXY(x, y, tx, ty + 100) then
set direction = BlzBitOr(direction, 2)
endif
if distance + 68 < distanceBetweenXYXY(x, y, tx - 100, ty) then
set direction = BlzBitOr(direction, 4)
endif
if distance + 68 < distanceBetweenXYXY(x, y, tx, ty - 100) then
set direction = BlzBitOr(direction, 8)
endif
set tx = R2I((x + (distance + 240) * Cos(angleToTarget)) / 64) * 64.0
set ty = R2I((y + (distance + 240) * Sin(angleToTarget)) / 64) * 64.0
if style == 0 then //style 0 -> Line behind target
call sendRock(u, x, y, z, tx, ty)
if direction == 0 then
//unknown direction... Make an east-west line
set r1x = 128
set r2x = 256
set r3x = -128
set r4x = -256
else
if BlzBitAnd(direction, 1) > 0 then //Target is to east
//call BJDebugMsg("east")
set r1y = 128
set r2y = 256
set r3y = -128
set r4y = -256
elseif BlzBitAnd(direction, 4) > 0 then //Target is to west, flip what goes +/- to change direction of diagonal (if it's even a diagonal)
//call BJDebugMsg("west")
set r1y = -128
set r2y = -256
set r3y = 128
set r4y = 256
endif
if BlzBitAnd(direction, 2) > 0 then //Target is to North
//call BJDebugMsg("north")
set r1x = -128
set r2x = -256
set r3x = 128
set r4x = 256
elseif BlzBitAnd(direction, 8) > 0 then //Target is to sourth
//call BJDebugMsg("south")
set r1x = 128
set r2x = 256
set r3x = -128
set r4x = -256
endif
endif
call sendRock(u, x, y, z, tx + r1x, ty + r1y)
call sendRock(u, x, y, z, tx + r2x, ty + r2y)
call sendRock(u, x, y, z, tx + r3x, ty + r3y)
call sendRock(u, x, y, z, tx + r4x, ty + r4y)
endif
endmethod
endstruct
function DiabolicSpecialRocker_FindTarget takes unit u returns nothing
local unit target = null
local unit t
local boolean targetIsHero = false
local group targetsWithinRadius = CreateGroup()
call GroupEnumUnitsInRange(targetsWithinRadius, GetUnitX(u), GetUnitY(u), 1200.0, null)
loop
set t = FirstOfGroup(targetsWithinRadius)
exitwhen t == null or (targetIsHero and target != null)
if UnitAlive(t) and not IsUnitAlly(t, GetOwningPlayer(u)) and not IsUnitType(t, UNIT_TYPE_STRUCTURE) then
set targetIsHero = IsUnitType(t, UNIT_TYPE_HERO)
set target = t
endif
call GroupRemoveUnit(targetsWithinRadius,t)
endloop
call DestroyGroup (targetsWithinRadius)
set targetsWithinRadius = null
set t = null
set udg_temp_unit = target
set target = null
endfunction
function DiabolicSpecialRocker_Tick takes nothing returns nothing
local unit u
local integer rockerGroupSize
loop
set u = BlzGroupUnitAt(diabolic_special_rockers, diabolic_special_rockers_index)
exitwhen u == null
if UnitAlive(u) then
call DiabolicSpecialRocker_FindTarget(u)
set diabolic_special_rockers_index = diabolic_special_rockers_index + 1
if udg_temp_unit != null then
call BlockingRockProjectile.sendIt(u, udg_temp_unit)
set u = null
exitwhen true
endif
else
call GroupRemoveUnit(diabolic_special_rockers, u)
set diabolic_special_rockers_index = 0
endif
endloop
set rockerGroupSize = BlzGroupGetSize(diabolic_special_rockers)
if rockerGroupSize > 0 then
call TimerStart(diabolic_special_rockers_timer, GetRandomReal(10.0, 16.0) / rockerGroupSize, false, function DiabolicSpecialRocker_Tick)
if diabolic_special_rockers_index >= rockerGroupSize then
set diabolic_special_rockers_index = 0
endif
else
call ReleaseTimer(diabolic_special_rockers_timer)
set diabolic_special_rockers_timer = null
endif
endfunction
function DiabolicSpecialRockerAdd takes unit u returns nothing
call GroupAddUnit(diabolic_special_rockers, u)
if diabolic_special_rockers_timer == null then
set diabolic_special_rockers_timer = NewTimer()
set diabolic_special_rockers_index = 0 //Clear previous values!
//Initial "Cooldown"
call TimerStart(diabolic_special_rockers_timer, GetRandomReal(10.0, 16.0), false, function DiabolicSpecialRocker_Tick)
endif
endfunction
endscope
globals
constant integer STONE_SKIN_ID = 0
constant integer SIZE_MODIFIED_ID = 1
constant integer SIZE_MODIFIED_GIANT_ID = 1001
constant integer SIZE_MODIFIED_TINY_ID = 1002
constant integer LUCKY_ID = 2
constant integer AURA_ENHANCED_ID = 3
constant integer ENRAGED_ID = 4
constant integer LIGHTNING_ENHANCED_ID = 5
constant integer HOUND_MASTER_ID = 6
constant integer FIRE_BOLDER_ID = 7
constant integer ROCKER_ID = 8
//Units:
constant integer HOUND_MASTER_WOLF_UNIT = 'n01E'
//Abilities:
constant integer STONE_SKIN_ABILITY = 'A007'
constant integer LUCKY_ABILITY = 'A008' //Dummy vfx
constant integer LIGHTNING_ENHANCED_ABILITY = 'A013'
constant integer FIRE_BOLDER_ABILITY = 'A018'
constant integer ENRAGED_ABILITY = 'A014'
constant integer ROCKER_ABILITY = 'A01I'
//Auras:
constant integer AURA_DEVOTION_ABILITY = 'A00V'
constant integer AURA_VAMPYRIC_ABILITY = 'A00X'
constant integer AURA_COMMAND_ABILITY = 'A00Z'
constant integer AURA_ENDURANCE_ABILITY = 'A011'
constant integer AURA_THORNS_ABILITY = 'A012'
integer diabolic_last_bit_mask = 0
endglobals
function DiabolicSpecialShouldGeneratePrefix takes integer numberOfPrefixes, integer numberOfSuffixes returns boolean
if numberOfPrefixes == numberOfSuffixes then
return GetRandomInt(0,1) == 0
elseif numberOfPrefixes > numberOfSuffixes then
return false
endif
return true
endfunction
function DiabolicSpecialGenerateSuffix takes string currentSuffix, integer numberOfSuffixes, integer specialId returns string
local string resultPrefix
if numberOfSuffixes == 0 then
set resultPrefix = " of "
else
set resultPrefix = currentSuffix + " and "
endif
if specialId == STONE_SKIN_ID then
return resultPrefix + "Stone Skinned"
elseif specialId == SIZE_MODIFIED_GIANT_ID then
return resultPrefix + "Giantism"
elseif specialId == SIZE_MODIFIED_TINY_ID then
return resultPrefix + "Dinkiness"
elseif specialId == LUCKY_ID then
return resultPrefix + "the Four Clover"
elseif specialId == AURA_ENHANCED_ID then
return resultPrefix + "the Auras"
elseif specialId == ENRAGED_ID then
return resultPrefix + "Great Fury"
elseif specialId == LIGHTNING_ENHANCED_ID then
return resultPrefix + "Zappiness"
elseif specialId == HOUND_MASTER_ID then
return resultPrefix + "the Hound Master"
elseif specialId == FIRE_BOLDER_ID then
return resultPrefix + "Fel Rock"
elseif specialId == ROCKER_ID then
return resultPrefix + "Rock-Blockiness"
endif
return "Suffix fail for id: " + I2S(specialId)
endfunction
function DiabolicSpecialGeneratePrefix takes string currentPrefix, integer numberOfPrefixes, integer specialId returns string
if numberOfPrefixes > 0 then
set currentPrefix = currentPrefix + "and "
endif
if specialId == STONE_SKIN_ID then
return currentPrefix + "Stone Skinned "
elseif specialId == SIZE_MODIFIED_GIANT_ID then
return currentPrefix + "Giant "
elseif specialId == SIZE_MODIFIED_TINY_ID then
return currentPrefix + "Dinky Snappy "
elseif specialId == LUCKY_ID then
return currentPrefix + "Lucky "
elseif specialId == AURA_ENHANCED_ID then
return currentPrefix + "Aura Enhanced "
elseif specialId == ENRAGED_ID then
return currentPrefix + "Enraging "
elseif specialId == LIGHTNING_ENHANCED_ID then
return currentPrefix + "Shocking "
elseif specialId == HOUND_MASTER_ID then
return currentPrefix + "Hound Master "
elseif specialId == FIRE_BOLDER_ID then
return currentPrefix + "Fel Rocking "
elseif specialId == ROCKER_ID then
return currentPrefix + "Rock-Blocking "
endif
return "Prefix fail for id: " + I2S(specialId)
endfunction
function DiabolicSpecial takes unit u, integer minSpecials, integer maxSpecials returns nothing
local integer specialInt = 0
local integer i = 0
local integer random = 0
local integer addedSpecials = 0
local integer specialsToAdd = GetRandomInt(minSpecials, maxSpecials)
local integer numberOfPrefixes = 0
local integer numberOfSuffixes = 0
local string namePrefix = ""
local string nameSuffix = ""
local real temp
local unit tempUnit
local integer red = 200
local integer green = 200
local integer blue = 200
call BlzSetUnitMaxHP(u, R2I(1.80 * BlzGetUnitMaxHP(u)) + 25 * udg_current_wave_nr)
call BlzSetUnitArmor(u, 1.15 * BlzGetUnitArmor(u) + 1.0 * udg_current_wave_nr)
call BlzSetUnitBaseDamage(u, R2I(1.15 * BlzGetUnitBaseDamage(u, 0) + 0.35 * udg_current_wave_nr), 0)
call BlzSetUnitIntegerField(u, UNIT_IF_LEVEL, BlzGetUnitIntegerField(u, UNIT_IF_LEVEL) + 1)
//call BlzSetUnitIntegerField(u, UNIT_IF_GOLD_BOUNTY_AWARDED_BASE, BlzGetUnitIntegerField(u, UNIT_IF_GOLD_BOUNTY_AWARDED_BASE) + 5)
//UNIT_RF_SPEED
//call
loop
exitwhen i == 30 or addedSpecials == specialsToAdd
set random = GetRandomInt(0, 8)
if random == STONE_SKIN_ID and BlzBitAnd(specialInt, 1) == 0 then
//Stone Skinned
if BlzBitAnd(diabolic_last_bit_mask, 1) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -2) //unset 1-bit
else
set specialInt = BlzBitOr(specialInt, 1)
set addedSpecials = addedSpecials + 1
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
call UnitAddAbility(u, STONE_SKIN_ABILITY)
call AddUnitBonus(u, BONUS_ARMOR, 0.15 * BlzGetUnitArmor(u) + 2.5 * udg_current_wave_nr * (1 + 0.02 * udg_current_wave_nr))
set red = red + GetRandomInt(-96, -24)
set green = green + GetRandomInt(-96, -24)
set blue = blue + GetRandomInt(-96, -24)
endif
elseif random == SIZE_MODIFIED_ID and BlzBitAnd(specialInt, 2) == 0 then
//Giant / Small
if BlzBitAnd(diabolic_last_bit_mask, 2) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -3) //unset 2-bit
else
set specialInt = BlzBitOr(specialInt, 2)
set addedSpecials = addedSpecials + 1
set temp = BlzGetUnitRealField(u, UNIT_RF_SCALING_VALUE)
set random = GetRandomInt(SIZE_MODIFIED_GIANT_ID, SIZE_MODIFIED_TINY_ID)
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
if random == SIZE_MODIFIED_GIANT_ID then
//Giant and slow
call AddUnitBonus(u, BONUS_HEALTH, 0.5 * MaxHp(u) + 20 * udg_current_wave_nr * (1 + 0.03 * udg_current_wave_nr))
call AddUnitBonus(u, BONUS_MOVEMENT_SPEED, -0.20 * GetUnitMoveSpeed(u))
call SetUnitScale(u, temp * 1.4, temp * 1.4, temp * 1.4)
else
//Small and fast
call AddUnitBonus(u, BONUS_MOVEMENT_SPEED, 25 + 0.20 * GetUnitMoveSpeed(u))
call AddUnitBonus(u, BONUS_ATTACK_SPEED, 0.25)
call SetUnitScale(u, temp * 0.7, temp * 0.7, temp * 0.7)
endif
set red = red + GetRandomInt(-72, 72)
set green = green + GetRandomInt(-72, 72)
set blue = blue + GetRandomInt(-72, 72)
endif
elseif random == LUCKY_ID and BlzBitAnd(specialInt, 4) == 0 then
//Lucky
if BlzBitAnd(diabolic_last_bit_mask, 4) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -5)
else
set specialInt = BlzBitOr(specialInt, 4)
set addedSpecials = addedSpecials + 1
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
call UnitAddAbility(u, LUCKY_ABILITY)
call AddUnitBonus(u, BONUS_CRITICAL_CHANCE, 0.33)
//call BlzSetUnitIntegerField(u, UNIT_IF_GOLD_BOUNTY_AWARDED_BASE, BlzGetUnitIntegerField(u, UNIT_IF_GOLD_BOUNTY_AWARDED_BASE) + 5)
set red = red + GetRandomInt(24, 80)
set green = green + GetRandomInt(24, 80)
set blue = blue + GetRandomInt(-96, -32)
endif
elseif random == AURA_ENHANCED_ID and BlzBitAnd(specialInt, 8) == 0 then
//Aura Enhanced
if BlzBitAnd(diabolic_last_bit_mask, 8) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -9)
else
set specialInt = BlzBitOr(specialInt, 8)
set addedSpecials = addedSpecials + 1
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
set random = GetRandomInt(0, 4)
if random == 0 then
call UnitAddAbility(u, AURA_DEVOTION_ABILITY)
elseif random == 1 then
call UnitAddAbility(u, AURA_VAMPYRIC_ABILITY)
elseif random == 2 then
call UnitAddAbility(u, AURA_COMMAND_ABILITY)
elseif random == 3 then
call UnitAddAbility(u, AURA_ENDURANCE_ABILITY)
elseif random == 4 then
call UnitAddAbility(u, AURA_THORNS_ABILITY)
endif
set red = red + GetRandomInt(-72, 72)
set green = green + GetRandomInt(-72, 72)
set blue = blue + GetRandomInt(-72, 72)
endif
elseif random == ENRAGED_ID and BlzBitAnd(specialInt, 16) == 0 then
//Enraged
if BlzBitAnd(diabolic_last_bit_mask, 16) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -17) //unset 16-bit
else
set specialInt = BlzBitOr(specialInt, 16)
set addedSpecials = addedSpecials + 1
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
call UnitAddAbility(u, ENRAGED_ABILITY)
call AddUnitBonus(u, BONUS_MOVEMENT_SPEED, 15)
set red = red + GetRandomInt(32, 128)
set green = green + GetRandomInt(-128, -32)
set blue = blue + GetRandomInt(-128, -32)
endif
elseif random == LIGHTNING_ENHANCED_ID and BlzBitAnd(specialInt, 32) == 0 then
//Lightning Enhanced
if BlzBitAnd(diabolic_last_bit_mask, 32) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -33)
else
set specialInt = BlzBitOr(specialInt, 32)
set addedSpecials = addedSpecials + 1
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
call UnitAddAbility(u, LIGHTNING_ENHANCED_ABILITY)
set red = red + GetRandomInt(32, 128)
set green = green + GetRandomInt(32, 128)
set blue = blue + GetRandomInt(-128, -48)
endif
elseif random == HOUND_MASTER_ID and BlzBitAnd(specialInt, 64) == 0 then
//Hound Master
if BlzBitAnd(diabolic_last_bit_mask, 64) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -65)
else
set specialInt = BlzBitOr(specialInt, 64)
set addedSpecials = addedSpecials + 1
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
call DiabolicSpecialHoundMaster_DiabolicSpecialHoundMaster_LookForChainTargetsFor(u)
call DiabolicSpecialHoundMaster_CreateHound(u)
call DiabolicSpecialHoundMaster_CreateHound(u)
set red = red + GetRandomInt(-72, 72)
set green = green + GetRandomInt(-72, 72)
set blue = blue + GetRandomInt(-72, 72)
endif
elseif random == FIRE_BOLDER_ID and BlzBitAnd(specialInt, 128) == 0 then
//Fire Bolder
if BlzBitAnd(diabolic_last_bit_mask, 128) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -129)
else
set specialInt = BlzBitOr(specialInt, 128)
set addedSpecials = addedSpecials + 1
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
call UnitAddAbility(u, FIRE_BOLDER_ABILITY)
call DiabolicSpecialFireBolderAdd(u)
set red = red + GetRandomInt(32, 128)
set green = green + GetRandomInt(-64, 64)
set blue = blue + GetRandomInt(-128, -48)
endif
elseif random == ROCKER_ID and BlzBitAnd(specialInt, 256) == 0 then
//Rock Blocker
if BlzBitAnd(diabolic_last_bit_mask, 256) > 0 then
set diabolic_last_bit_mask = BlzBitAnd(diabolic_last_bit_mask, -257)
else
set specialInt = BlzBitOr(specialInt, 256)
set addedSpecials = addedSpecials + 1
if DiabolicSpecialShouldGeneratePrefix(numberOfPrefixes, numberOfSuffixes) then
set namePrefix = DiabolicSpecialGeneratePrefix(namePrefix, numberOfPrefixes, random)
set numberOfPrefixes = numberOfPrefixes + 1
else
set nameSuffix = DiabolicSpecialGenerateSuffix(nameSuffix, numberOfSuffixes, random)
set numberOfSuffixes = numberOfSuffixes + 1
endif
call UnitAddAbility(u, ROCKER_ABILITY)
call DiabolicSpecialRockerAdd(u)
set red = red + GetRandomInt(-96, -24)
set green = green + GetRandomInt(-96, -24)
set blue = blue + GetRandomInt(-96, -24)
endif
endif
set i = i + 1
endloop
call SetUnitState(u, UNIT_STATE_LIFE, MaxHp(u))
call BlzSetUnitName(u, namePrefix + GetUnitName(u) + nameSuffix)
set red = IMaxBJ(96, IMinBJ(255, red))
set green = IMaxBJ(96, IMinBJ(255, green))
set blue = IMaxBJ(96, IMinBJ(255, blue))
call SetUnitVertexColor(u, red, green, blue, 255)
set diabolic_last_bit_mask = specialInt
endfunction
function Trig_DiabolicEnraged_Conditions takes nothing returns boolean
return UnitHasBuffBJ(udg_DamageEventSource, 'B01G')
endfunction
function Trig_DiabolicEnraged_Actions takes nothing returns nothing
local real bonusRate = (0.41 + udg_current_wave_nr / 45.0) * udg_difficulty_dmg_factor
if GetUnitMoveSpeed(udg_DamageEventSource) < 433 then
call AddUnitBonus(udg_DamageEventSource, BONUS_MOVEMENT_SPEED, 17.0 * bonusRate)
else
call AddUnitBonus(udg_DamageEventSource, BONUS_DAMAGE, 2.5 * bonusRate)
endif
if GetUnitBonus(udg_DamageEventSource, BONUS_ATTACK_SPEED) < 2.5 then
call AddUnitBonus(udg_DamageEventSource, BONUS_ATTACK_SPEED, 0.13 * bonusRate)
else
call AddUnitBonus(udg_DamageEventSource, BONUS_DAMAGE, 2.5 * bonusRate)
endif
endfunction
//===========================================================================
function InitTrig_DiabolicEnraged takes nothing returns nothing
set gg_trg_DiabolicEnraged = CreateTrigger( )
call TriggerRegisterVariableEvent( gg_trg_DiabolicEnraged, "udg_AfterDamageEvent", EQUAL, 1.00 )
call TriggerAddCondition( gg_trg_DiabolicEnraged, Condition( function Trig_DiabolicEnraged_Conditions ) )
call TriggerAddAction( gg_trg_DiabolicEnraged, function Trig_DiabolicEnraged_Actions )
endfunction
scope DiabolicSpecialLightningEnhanced
private struct DiabolicLightning extends Missiles
sound sfx
real currentAngle
integer wobbleCounter
timer t
// static method applySpell takes unit src, unit tgt, real amt, damagetype dt returns Damage
method onHit takes unit u returns boolean
if u != source and BlzIsUnitSelectable(u) and not BlzIsUnitInvulnerable(u) and not IsUnitAlly(u, owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
call Damage.applySpell(source, u, damage, DAMAGE_TYPE_LIGHTNING)
return true
endif
return false
endmethod
method onPeriod takes nothing returns boolean
return false
endmethod
method onRemove takes nothing returns nothing
local boolean fade_out_allowed = GetSoundIsPlaying(sfx) // Using fade out on same frame as sound finishes playing seems to crash the game.
call StopSound(sfx, true, fade_out_allowed)
set sfx = null
call ReleaseTimer(t)
set t = null
endmethod
static method wobble takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set wobbleCounter = wobbleCounter + 1
if wobbleCounter < 15 then
set speed = RMaxBJ(125, RMinBJ(275, speed + GetRandomReal(-50, 50)))
set currentAngle = currentAngle + GetRandomReal(-50 * bj_DEGTORAD, 50 * bj_DEGTORAD)
call deflect(x + 500*Cos(currentAngle), y + 500 * Sin(currentAngle), 50)
call SetSoundPosition(sfx, x, y, 0 )
else
call terminate()
endif
endmethod
static method spawnLightning takes unit u, real angle returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real z = GetUnitFlyHeight(u) + 65.0
local real tx = x + 500.0 * Cos(angle)
local real ty = y + 500.0 * Sin(angle)
local thistype this = thistype.create(x, y, z, tx, ty, GetZHeight(tx, ty) + 50.0)
set source = u
set model = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
set speed = GetRandomInt(125, 275)
set owner = GetOwningPlayer(u)
set collision = 24
set sfx = CreateSound("lightningenhanced\\chargedbolt" + I2S(GetRandomInt(1, 3)) + ".wav", true, true, true, 10, 10, "")
call SetSoundDistances(sfx, 600.00, 10000.00)
call SetSoundDistanceCutoff(sfx, 3000.00)
call SetSoundPosition(sfx, x, y, 0 )
call SetSoundVolume(sfx, 127)
call StartSound(sfx)
//set arc = 10
//https://www.wolframalpha.com/input/?i=x+%3D+1%2C10%2C20%2C30%2C40+for+%2833+%2B+4+*+%281+%2B+x+*+0.05%29+*+x%29
//x = 1,10,20,30,40 for (33 + 5 * (1 + x * 0.03) * x)
//{38.15, 98, 193, 318, 473}
set damage = (30.0 + 5.5 * (1.0 + udg_current_wave_nr * 0.035) * udg_current_wave_nr) * udg_difficulty_dmg_factor
set t = NewTimerEx(this)
set wobbleCounter = 0
set currentAngle = angle
call TimerStart(t, 0.75, true, function thistype.wobble)
call launch()
endmethod
static method takeOffCooldown takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer unitIndex = GetTimerData(t)
call RemoveSavedBoolean(udg_wave_specific_hashtable, unitIndex, StringHash("Diabolic Lightning Cooldown"))
call ReleaseTimer(t)
set t = null
endmethod
static method checkShouldSpawnLightningForLightningEnhanced takes unit u returns nothing
local real a = GetRandomReal(0.0, 2.0 * bj_PI)
local integer unitIndex = GetUnitUserData(u)
if not LoadBoolean(udg_wave_specific_hashtable, unitIndex, StringHash("Diabolic Lightning Cooldown")) then
call SaveBoolean(udg_wave_specific_hashtable, unitIndex, StringHash("Diabolic Lightning Cooldown"), true)
call TimerStart(NewTimerEx(unitIndex), 0.13, false, function thistype.takeOffCooldown)
call DiabolicLightning.spawnLightning(u, a)
call DiabolicLightning.spawnLightning(u, a + bj_PI)
// call BJDebugMsg("Spawning lightning")
//else
// call BJDebugMsg("Spawning lightning on cooldown")
endif
endmethod
static method DiabolicLightning_OnDamageEvent takes nothing returns nothing
if GetUnitAbilityLevel(udg_DamageEventTarget, 'B01F') > 0 then
call checkShouldSpawnLightningForLightningEnhanced(udg_DamageEventTarget)
endif
endmethod
static method onInit takes nothing returns nothing
//RegisterDamageEngine takes code c, string eventName, real value
call RegisterDamageEngine(function thistype.DiabolicLightning_OnDamageEvent, "After", 1.0)
endmethod
endstruct
endscope
function Trig_CreateWave_Diabolic_CanSpawn takes integer spawn_i returns boolean
return LoadInteger(udg_timer_hashtable, StringHash("Diabolic Special"), spawn_i) < udg_wave_unit_diabolic_count[udg_current_wave_nr]
endfunction
function Trig_CreateWave_ApplyDifficultyModifiers takes unit u returns nothing
local real newMaxHp = BlzGetUnitMaxHP(u) * udg_difficulty_hp_factor
local real newDmg = BlzGetUnitBaseDamage(u, 0) * udg_difficulty_dmg_factor
call BlzSetUnitMaxHP(u, R2I(newMaxHp))
call BlzSetUnitBaseDamage( u, R2I(newDmg), 0 )
call SetUnitState(u, UNIT_STATE_LIFE, newMaxHp)
endfunction
//
// Will spawn a unit for each player * 2 and order it to attack.
//
function Trig_CreateWave_Spawn_unit takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer timer_handle = GetHandleId( t )
local integer unit_type = LoadInteger( udg_timer_hashtable, timer_handle, 0 )
local integer num_units_to_spawn = LoadInteger( udg_timer_hashtable, timer_handle, 1 )
local multiboarditem multiboard_num_units = MultiboardGetItem( udg_wave_multiboard, 1, 1 )
local integer diabolicsSpawned = 0
local unit spawned_unit = null
local integer curr_spawn_player_i = 0
local integer curr_spawn_i = 0
local integer max_spawn_i = ( ( CountPlayersInForceBJ(udg_current_human_players) * 2 ) - 1 )
// NORTH = Player(8), SOUTH = Player(9), WEST = Player(10), EAST = Player(11)
loop
exitwhen curr_spawn_i > max_spawn_i
set curr_spawn_player_i = R2I(curr_spawn_i / 2) + 8
set spawned_unit = CreateUnitAtLoc( Player(curr_spawn_player_i), unit_type, udg_wave_spawn_pos[curr_spawn_i], bj_UNIT_FACING )
call GroupAddUnit( udg_current_wave_unit_group, spawned_unit )
if Trig_CreateWave_Diabolic_CanSpawn(curr_spawn_player_i) and GetRandomReal(0.0, 1.0) < (1.0 / IMinBJ(6, num_units_to_spawn)) then
call SaveInteger(udg_timer_hashtable, StringHash("Diabolic Special"), curr_spawn_player_i, LoadInteger(udg_timer_hashtable, StringHash("Diabolic Special"), curr_spawn_player_i) + 1)
call DiabolicSpecial(spawned_unit, 1 + udg_current_wave_nr / 22, 1 + udg_current_wave_nr / 14)
endif
call Trig_CreateWave_ApplyDifficultyModifiers(spawned_unit)
call IssuePointOrderLoc( spawned_unit, "attack", udg_center_player_spawn_point )
set curr_spawn_i = curr_spawn_i + 1
endloop
call MultiboardSetItemValue( multiboard_num_units, I2S( CountUnitsInGroup( udg_current_wave_unit_group ) ) )
call MultiboardReleaseItem( multiboard_num_units )
set num_units_to_spawn = num_units_to_spawn - 1
if num_units_to_spawn > 0 then
call SaveInteger( udg_timer_hashtable, timer_handle, 1, num_units_to_spawn )
else
call PauseTimer( t )
call DestroyTimer( t )
call FlushChildHashtable( udg_timer_hashtable, timer_handle )
endif
set multiboard_num_units = null
set spawned_unit = null
set t = null
endfunction
//
// Main actions: Checks and spawns units.
//
function Trig_CreateWave_Actions takes nothing returns nothing
local timer t = null
local integer timer_handle = 0
call DisplayTextToForce( GetPlayersAll(), ( "|cffffcc00STARTING WAVE " + ( I2S(( udg_current_wave_nr + 1 )) + "|r" ) ) )
call MultiboardSetTitleText( udg_wave_multiboard, ( "Wave " + I2S(( udg_current_wave_nr + 1 )) ) )
call MultiboardSetItemValueBJ( udg_wave_multiboard, 2, 2, "TRIGSTR_501" )
//Reset so no Diabolic Specials has spawned this wave
call SaveInteger( udg_timer_hashtable, StringHash("Diabolic Special"), 8, 0)
call SaveInteger( udg_timer_hashtable, StringHash("Diabolic Special"), 9, 0)
call SaveInteger( udg_timer_hashtable, StringHash("Diabolic Special"), 10, 0)
call SaveInteger( udg_timer_hashtable, StringHash("Diabolic Special"), 11, 0)
// Spawn Unit Type Variation 1
if udg_wave_unit_amount_var1[udg_current_wave_nr] > 0 then
set t = CreateTimer()
set timer_handle = GetHandleId( t )
call SaveInteger( udg_timer_hashtable, timer_handle, 0, udg_wave_unit_type_var1[udg_current_wave_nr] )
call SaveInteger( udg_timer_hashtable, timer_handle, 1, udg_wave_unit_amount_var1[udg_current_wave_nr] )
call TimerStart( t, GetRandomReal(0.4, 0.6), true, function Trig_CreateWave_Spawn_unit )
endif
// Spawn Unit Type Variation 2
if udg_wave_unit_amount_var2[udg_current_wave_nr] > 0 then
set t = CreateTimer()
set timer_handle = GetHandleId( t )
call SaveInteger( udg_timer_hashtable, timer_handle, 0, udg_wave_unit_type_var2[udg_current_wave_nr] )
call SaveInteger( udg_timer_hashtable, timer_handle, 1, udg_wave_unit_amount_var2[udg_current_wave_nr] )
call TimerStart( t, GetRandomReal(0.4, 0.6), true, function Trig_CreateWave_Spawn_unit )
endif
// Spawn Unit Type Variation 3
if udg_wave_unit_amount_var3[udg_current_wave_nr] > 0 then
set t = CreateTimer()
set timer_handle = GetHandleId( t )
call SaveInteger( udg_timer_hashtable, timer_handle, 0, udg_wave_unit_type_var3[udg_current_wave_nr] )
call SaveInteger( udg_timer_hashtable, timer_handle, 1, udg_wave_unit_amount_var3[udg_current_wave_nr] )
call TimerStart( t, GetRandomReal(0.4, 0.6), true, function Trig_CreateWave_Spawn_unit )
endif
call BossCheck()
set t = null
endfunction
//===========================================================================
function InitTrig_CreateWave takes nothing returns nothing
set gg_trg_CreateWave = CreateTrigger( )
call TriggerAddAction( gg_trg_CreateWave, function Trig_CreateWave_Actions )
endfunction
//
// Check wave and run appropiate boss spawn trigger.
//
function BossCheck takes nothing returns nothing
local integer current_wave = (udg_current_wave_nr + 1)
if ( current_wave == 10 ) then
call TriggerExecuteWait(gg_trg_SpawnBoss1)
elseif ( current_wave == 20 ) then
call TriggerExecuteWait(gg_trg_SpawnBoss2)
elseif ( current_wave == 30 ) then
call TriggerExecuteWait(gg_trg_SpawnBoss3)
elseif ( current_wave == 40 ) then
call TriggerExecuteWait(gg_trg_SpawnBoss4)
endif
endfunction
//
// Learns skills for the boss.
//
function Trig_SpawnBoss1_Learn_Skills takes unit boss returns nothing
local integer curr_i = 1
local integer end_i = GetHeroSkillPoints(boss)
loop
exitwhen curr_i > end_i
call SelectHeroSkill( boss, 'AEsh' ) // Shadow Strike
call SelectHeroSkill( boss, 'AUav' ) // Vampiric Aura
call SelectHeroSkill( boss, 'AUcs' ) // Carrion Swarm
set curr_i = curr_i + 1
endloop
endfunction
//
// Spawns the boss and orders it to attack.
//
function Trig_SpawnBoss1_Spawn_Boss takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer number_human_players = CountPlayersInForceBJ( udg_current_human_players )
local integer max_spawn_index = (number_human_players * 2) - 1
local integer rand_int = GetRandomInt( 0, max_spawn_index )
local integer boss_level = 10
local unit boss_unit = CreateUnitAtLoc( Player(8), 'U000', udg_wave_spawn_pos[rand_int], bj_UNIT_FACING )
local multiboarditem unit_left_board_item = null
local string nr_units_string = null
call SetHeroLevel( boss_unit, boss_level, false )
call SuspendHeroXP( boss_unit, true )
call GroupAddUnit( udg_current_wave_unit_group, boss_unit )
set nr_units_string = I2S( CountUnitsInGroup( udg_current_wave_unit_group ) )
call IssuePointOrderLoc( boss_unit, "attack", udg_center_player_spawn_point )
call Trig_SpawnBoss1_Learn_Skills( boss_unit )
set unit_left_board_item = MultiboardGetItem(udg_wave_multiboard, 1, 1)
call MultiboardSetItemValue( unit_left_board_item, nr_units_string)
call MultiboardReleaseItem( unit_left_board_item )
set unit_left_board_item = null
set udg_wave_specific_unit = boss_unit
set boss_unit = null
call SetMusicVolume(100)
call PlayMusic( gg_snd_Castlevania2_Night )
call MultiboardDisplay(udg_wave_multiboard, true)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
call Trig_Boss1CustomSetup_Actions()
endfunction
///
/// Fades in screen, will also trigger next step of intro.
///
function Trig_SpawnBoss1_Intro_FadeIn takes nothing returns nothing
local timer t = GetExpiredTimer()
call CinematicFadeBJ(bj_CINEFADETYPE_FADEIN, 1.50, "ReplaceableTextures\\CameraMasks\\Black_mask.blp", 0, 0, 0, 0)
call PauseTimer(t)
// Start next step of intro sequence
call TimerStart(t, 1.50, false, function Trig_SpawnBoss1_Spawn_Boss)
set t = null
endfunction
///
/// Set time of day to night, will also trigger next step of intro.
///
function Trig_SpawnBoss1_Intro_Set_Night takes nothing returns nothing
local timer t = GetExpiredTimer()
call SetTimeOfDay( 24.00 )
call PauseTimer(t)
// Start next step of intro sequence
call TimerStart(t, 0.65, false, function Trig_SpawnBoss1_Intro_FadeIn)
set t = null
endfunction
///
/// Fades out screen, will also trigger next step of intro.
///
function Trig_SpawnBoss1_Intro_FadeOut takes nothing returns nothing
local timer t = GetExpiredTimer()
local multiboarditem middle_board_item = MultiboardGetItem(udg_cinematic_multiboard, 2, 0)
call MultiboardSetItemValue(middle_board_item, "")
call MultiboardDisplay(udg_cinematic_multiboard, false)
call CinematicFadeBJ(bj_CINEFADETYPE_FADEOUT, 1.50, "ReplaceableTextures\\CameraMasks\\Black_mask.blp", 0, 0, 0, 0)
call StopMusic(true)
call PauseTimer(t)
// Start next step of intro sequence
call TimerStart(t, 1.50, false, function Trig_SpawnBoss1_Intro_Set_Night)
call MultiboardReleaseItem(middle_board_item)
set middle_board_item = null
set t = null
endfunction
//
// Output intro text using leaderboard title in a manner similar to Catlevania II.
//
function Trig_SpawnBoss1_Output_Intro_Text takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer timer_handle = GetHandleId(t)
local integer curr_i = LoadInteger(udg_timer_hashtable, timer_handle, 0)
local integer max_i = LoadInteger(udg_timer_hashtable, timer_handle, 1)
local string intro_text = LoadStr(udg_timer_hashtable, timer_handle, 2)
local string curr_text = ""
local string curr_char = SubString(intro_text, curr_i-1, curr_i)
local multiboarditem middle_board_item = MultiboardGetItem(udg_cinematic_multiboard, 2, 0)
if curr_i <= max_i then
if curr_i > 1 and (curr_char == "\n" or curr_char == ".") then
call StopSound( gg_snd_castlevania_text_blip, false, false )
elseif not GetSoundIsPlaying( gg_snd_castlevania_text_blip ) then
call StartSound( gg_snd_castlevania_text_blip )
endif
set curr_text = SubString(intro_text, 0, curr_i)
call MultiboardSetItemValue(middle_board_item, curr_text)
set curr_i = curr_i + 1
call SaveInteger(udg_timer_hashtable, timer_handle, 0, curr_i)
else
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_timer_hashtable, timer_handle)
// Start next step of intro sequence
set t = CreateTimer()
call TimerStart(t, 1.00, false, function Trig_SpawnBoss1_Intro_FadeOut)
endif
call MultiboardReleaseItem(middle_board_item)
set middle_board_item = null
set t = null
endfunction
//
// Setup and starts the intro output timer.
// Intro will consist of 3 steps: Output text, cinematic camera effects and spawn boss unit.
//
function Trig_SpawnBoss1_Start_Intro takes nothing returns nothing
local string intro_text = "\nWHAT A\nHORRIBLE\nNIGHT TO\nHAVE A\nCURSE."
local integer intro_text_length = StringLength(intro_text)
local timer output_intro_timer = CreateTimer()
local integer timer_handle = GetHandleId(output_intro_timer)
call SaveInteger(udg_timer_hashtable, timer_handle, 0, 0)
call SaveInteger(udg_timer_hashtable, timer_handle, 1, intro_text_length)
call SaveStr(udg_timer_hashtable, timer_handle, 2, intro_text)
call TimerStart(output_intro_timer, 0.1, true, function Trig_SpawnBoss1_Output_Intro_Text)
set output_intro_timer = null
endfunction
//
// Main actions: display text, change time of day and spawn boss.
//
function Trig_SpawnBoss1_Actions takes nothing returns nothing
call MultiboardDisplay(udg_wave_multiboard, false)
call MultiboardDisplay(udg_cinematic_multiboard, true)
call MultiboardMinimize(udg_cinematic_multiboard, false)
// Setup cinematic multiboard
call MultiboardSetColumnCount(udg_cinematic_multiboard, 1)
call MultiboardSetRowCount(udg_cinematic_multiboard, 6)
call MultiboardSetItemsWidth(udg_cinematic_multiboard, 0.06)
call MultiboardSetItemsStyle(udg_cinematic_multiboard, true, false)
call MultiboardSetTitleText(udg_cinematic_multiboard, "Boss Approaching")
// Start the intro
call SetMusicVolume(85)
call Trig_SpawnBoss1_Start_Intro()
endfunction
//===========================================================================
function InitTrig_SpawnBoss1 takes nothing returns nothing
set gg_trg_SpawnBoss1 = CreateTrigger()
call TriggerAddAction( gg_trg_SpawnBoss1, function Trig_SpawnBoss1_Actions )
endfunction
//
// Learns skills for the boss.
//
function Trig_SpawnBoss2_Learn_Skills takes unit boss returns nothing
local integer curr_i = 1
local integer end_i = GetHeroSkillPoints(boss)
loop
exitwhen curr_i > end_i
call SelectHeroSkill( boss, 'ANso' ) // Soul Burn
call SelectHeroSkill( boss, 'AUav' ) // Vampiric Aura
call SelectHeroSkill( boss, 'AUcs' ) // Carrion Swarm
call SelectHeroSkill( boss, 'AUim' ) // Impale
set curr_i = curr_i + 1
endloop
endfunction
//
// Spawns the boss and orders it to attack.
//
function Trig_SpawnBoss2_Spawn_Boss takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer number_human_players = CountPlayersInForceBJ( udg_current_human_players )
local integer max_spawn_index = (number_human_players * 2) - 1
local integer rand_int = GetRandomInt( 0, max_spawn_index )
local integer boss_level = 20
local unit boss_unit = CreateUnitAtLoc( Player(8), 'U005', udg_wave_spawn_pos[rand_int], bj_UNIT_FACING )
local multiboarditem unit_left_board_item = MultiboardGetItem( udg_wave_multiboard, 1, 1 )
local multiboarditem middle_board_item = MultiboardGetItem( udg_cinematic_multiboard, 2, 0 )
local string nr_units_string = null
call MultiboardSetItemValue( middle_board_item, "" )
call MultiboardReleaseItem( middle_board_item )
set middle_board_item = null
call SetHeroLevel( boss_unit, boss_level, false )
call SuspendHeroXP( boss_unit, true )
call GroupAddUnit( udg_current_wave_unit_group, boss_unit )
set nr_units_string = I2S( CountUnitsInGroup( udg_current_wave_unit_group ) )
call IssuePointOrderLoc( boss_unit, "attack", udg_center_player_spawn_point )
call Trig_SpawnBoss2_Learn_Skills( boss_unit )
call MultiboardSetItemValue( unit_left_board_item, nr_units_string)
call MultiboardReleaseItem( unit_left_board_item )
set unit_left_board_item = null
set udg_wave_specific_unit = boss_unit
set boss_unit = null
call SetMusicVolume(100)
call PlayMusic( gg_snd_Castlevania1_Final_Fight_2 )
call MultiboardDisplay(udg_wave_multiboard, true)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
call Trig_Boss2CustomSetup_Actions()
endfunction
//
// Output intro text using leaderboard title in a manner similar to Catlevania II.
//
function Trig_SpawnBoss2_Output_Intro_Text takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer timer_handle = GetHandleId(t)
local integer curr_i = LoadInteger(udg_timer_hashtable, timer_handle, 0)
local integer max_i = LoadInteger(udg_timer_hashtable, timer_handle, 1)
local string intro_text = LoadStr(udg_timer_hashtable, timer_handle, 2)
local string curr_text = ""
local string curr_char = SubString(intro_text, curr_i-1, curr_i)
local multiboarditem middle_board_item = MultiboardGetItem(udg_cinematic_multiboard, 2, 0)
if curr_i <= max_i then
if curr_i > 1 and (curr_char == "\n" or curr_char == ".") then
call StopSound( gg_snd_castlevania_text_blip, false, false )
elseif not GetSoundIsPlaying( gg_snd_castlevania_text_blip ) then
call StartSound( gg_snd_castlevania_text_blip )
endif
set curr_text = SubString(intro_text, 0, curr_i)
call MultiboardSetItemValue(middle_board_item, curr_text)
set curr_i = curr_i + 1
call SaveInteger(udg_timer_hashtable, timer_handle, 0, curr_i)
else
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_timer_hashtable, timer_handle)
// Start next step of intro sequence
set t = CreateTimer()
call TimerStart(t, 1.00, false, function Trig_SpawnBoss2_Spawn_Boss)
endif
call MultiboardReleaseItem(middle_board_item)
set middle_board_item = null
set t = null
endfunction
//
// Setup and starts the intro output timer.
// Intro will consist of 2 steps: Output text and spawn boss unit.
//
function Trig_SpawnBoss2_Start_Intro takes nothing returns nothing
local string intro_text = "\nWHAT A\nHORRIBLE\nNIGHT TO\nHAVE A\nCURSE\nAGAIN."
local integer intro_text_length = StringLength(intro_text)
local timer output_intro_timer = CreateTimer()
local integer timer_handle = GetHandleId(output_intro_timer)
call SaveInteger(udg_timer_hashtable, timer_handle, 0, 0)
call SaveInteger(udg_timer_hashtable, timer_handle, 1, intro_text_length)
call SaveStr(udg_timer_hashtable, timer_handle, 2, intro_text)
call TimerStart(output_intro_timer, 0.1, true, function Trig_SpawnBoss2_Output_Intro_Text)
set output_intro_timer = null
endfunction
//
// Main actions: display text, change time of day and spawn boss.
//
function Trig_SpawnBoss2_Actions takes nothing returns nothing
local multiboarditem middle_board_item = MultiboardGetItem(udg_cinematic_multiboard, 2, 0)
call MultiboardDisplay(udg_wave_multiboard, false)
call MultiboardDisplay(udg_cinematic_multiboard, true)
call MultiboardMinimize(udg_cinematic_multiboard, false)
// Setup cinematic multiboard
call MultiboardSetColumnCount(udg_cinematic_multiboard, 1)
call MultiboardSetRowCount(udg_cinematic_multiboard, 6)
call MultiboardSetItemsWidth(udg_cinematic_multiboard, 0.06)
call MultiboardSetItemsStyle(udg_cinematic_multiboard, true, false)
call MultiboardSetTitleText(udg_cinematic_multiboard, "Boss Approaching")
// Start the intro
call SetMusicVolume(85)
call Trig_SpawnBoss2_Start_Intro()
endfunction
//===========================================================================
function InitTrig_SpawnBoss2 takes nothing returns nothing
set gg_trg_SpawnBoss2 = CreateTrigger()
call TriggerAddAction( gg_trg_SpawnBoss2, function Trig_SpawnBoss2_Actions )
endfunction
//
// Learns skills for the boss.
//
function Trig_SpawnBoss3_Learn_Skills takes unit boss returns nothing
local integer curr_i = 1
local integer end_i = GetHeroSkillPoints(boss)
loop
exitwhen curr_i > end_i
call SelectHeroSkill( boss, 'ANsh' ) // Shockwave
set curr_i = curr_i + 1
endloop
endfunction
//
// Spawns the boss and orders it to attack.
//
function Trig_SpawnBoss3_Spawn_Boss takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer number_human_players = CountPlayersInForceBJ( udg_current_human_players )
local integer max_spawn_index = (number_human_players * 2) - 1
local integer rand_int = GetRandomInt( 0, max_spawn_index )
local integer boss_level = 30
local unit boss_unit = CreateUnitAtLoc( Player(8), 'U007', udg_wave_spawn_pos[rand_int], bj_UNIT_FACING )
local multiboarditem unit_left_board_item = MultiboardGetItem( udg_wave_multiboard, 1, 1 )
local multiboarditem middle_board_item = MultiboardGetItem( udg_cinematic_multiboard, 2, 0 )
local string nr_units_string = null
call MultiboardSetItemValue( middle_board_item, "" )
call MultiboardReleaseItem( middle_board_item )
set middle_board_item = null
call SetHeroLevel( boss_unit, boss_level, false )
call SuspendHeroXP( boss_unit, true )
call GroupAddUnit( udg_current_wave_unit_group, boss_unit )
set nr_units_string = I2S( CountUnitsInGroup( udg_current_wave_unit_group ) )
call IssuePointOrderLoc( boss_unit, "attack", udg_center_player_spawn_point )
call Trig_SpawnBoss3_Learn_Skills( boss_unit )
call MultiboardSetItemValue( unit_left_board_item, nr_units_string)
call MultiboardReleaseItem( unit_left_board_item )
set unit_left_board_item = null
set udg_wave_specific_unit = boss_unit
set boss_unit = null
call SetMusicVolume(100)
call PlayMusic( gg_snd_Warcraft2_OrcWin )
call MultiboardDisplay(udg_wave_multiboard, true)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
call Trig_Boss3CustomSetup_Actions()
endfunction
//
// Output intro text using leaderboard title in a manner similar to Catlevania II.
//
function Trig_SpawnBoss3_Output_Intro_Text takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer timer_handle = GetHandleId(t)
local integer curr_i = LoadInteger(udg_timer_hashtable, timer_handle, 0)
local integer max_i = LoadInteger(udg_timer_hashtable, timer_handle, 1)
local string intro_text = LoadStr(udg_timer_hashtable, timer_handle, 2)
local string curr_text = ""
local string curr_char = SubString(intro_text, curr_i-1, curr_i)
local multiboarditem middle_board_item = MultiboardGetItem(udg_cinematic_multiboard, 2, 0)
if curr_i <= max_i then
if curr_i > 1 and (curr_char == "\n" or curr_char == ".") then
call StopSound( gg_snd_castlevania_text_blip, false, false )
elseif not GetSoundIsPlaying( gg_snd_castlevania_text_blip ) then
call StartSound( gg_snd_castlevania_text_blip )
endif
set curr_text = SubString(intro_text, 0, curr_i)
call MultiboardSetItemValue(middle_board_item, curr_text)
set curr_i = curr_i + 1
call SaveInteger(udg_timer_hashtable, timer_handle, 0, curr_i)
else
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_timer_hashtable, timer_handle)
// Start next step of intro sequence
set t = CreateTimer()
call TimerStart(t, 1.00, false, function Trig_SpawnBoss3_Spawn_Boss)
endif
call MultiboardReleaseItem(middle_board_item)
set middle_board_item = null
set t = null
endfunction
//
// Setup and starts the intro output timer.
// Intro will consist of 2 steps: Output text and spawn boss unit.
//
function Trig_SpawnBoss3_Start_Intro takes nothing returns nothing
local string intro_text = "\nPREPARE \nTO FACE \nTHE TIDES\nOF\nDARKNESS."
local integer intro_text_length = StringLength(intro_text)
local timer output_intro_timer = CreateTimer()
local integer timer_handle = GetHandleId(output_intro_timer)
call SaveInteger(udg_timer_hashtable, timer_handle, 0, 0)
call SaveInteger(udg_timer_hashtable, timer_handle, 1, intro_text_length)
call SaveStr(udg_timer_hashtable, timer_handle, 2, intro_text)
call TimerStart(output_intro_timer, 0.1, true, function Trig_SpawnBoss3_Output_Intro_Text)
set output_intro_timer = null
endfunction
//
// Main actions: display text, change time of day and spawn boss.
//
function Trig_SpawnBoss3_Actions takes nothing returns nothing
local multiboarditem middle_board_item = MultiboardGetItem(udg_cinematic_multiboard, 2, 0)
call MultiboardDisplay(udg_wave_multiboard, false)
call MultiboardDisplay(udg_cinematic_multiboard, true)
call MultiboardMinimize(udg_cinematic_multiboard, false)
// Setup cinematic multiboard
call MultiboardSetColumnCount(udg_cinematic_multiboard, 1)
call MultiboardSetRowCount(udg_cinematic_multiboard, 6)
call MultiboardSetItemsWidth(udg_cinematic_multiboard, 0.06)
call MultiboardSetItemsStyle(udg_cinematic_multiboard, true, false)
call MultiboardSetTitleText(udg_cinematic_multiboard, "Boss Approaching")
// Start the intro
call SetMusicVolume(85)
call Trig_SpawnBoss3_Start_Intro()
endfunction
//===========================================================================
function InitTrig_SpawnBoss3 takes nothing returns nothing
set gg_trg_SpawnBoss3 = CreateTrigger()
call TriggerAddAction( gg_trg_SpawnBoss3, function Trig_SpawnBoss3_Actions )
endfunction
//
// Spawns the boss and orders it to attack.
//
function Trig_SpawnBoss4_Spawn_Boss takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer number_human_players = CountPlayersInForceBJ( udg_current_human_players )
local integer max_spawn_index = (number_human_players * 2) - 1
local integer rand_int = GetRandomInt( 0, max_spawn_index )
local integer boss_level = 30
local location boss_spawn_point = GetRectCenter(gg_rct_MedivhAltar)
local location heroes_telepoirt_point = GetRectCenter(gg_rct_MedivhArenaEnterence)
local unit boss_unit = CreateUnitAtLoc( Player(8), 'U00B', boss_spawn_point, bj_UNIT_FACING )
local multiboarditem unit_left_board_item = MultiboardGetItem( udg_wave_multiboard, 1, 1 )
local multiboarditem middle_board_item = MultiboardGetItem( udg_cinematic_multiboard, 2, 0 )
local string nr_units_string = null
call MultiboardSetItemValue( middle_board_item, "" )
call MultiboardReleaseItem( middle_board_item )
set middle_board_item = null
call SetHeroLevel( boss_unit, boss_level, false )
call SuspendHeroXP( boss_unit, true )
call GroupAddUnit( udg_current_wave_unit_group, boss_unit )
set nr_units_string = I2S( CountUnitsInGroup( udg_current_wave_unit_group ) )
//call IssuePointOrderLoc( boss_unit, "attack", heroes_telepoirt_point )
call MultiboardSetItemValue( unit_left_board_item, nr_units_string)
call MultiboardReleaseItem( unit_left_board_item )
set unit_left_board_item = null
set udg_wave_specific_unit = boss_unit
set boss_unit = null
set boss_spawn_point = null
set heroes_telepoirt_point = null
call SetMusicVolume(100)
call PlayMusic( gg_snd_Warcraft2_OrcBrefing )
call Trig_Boss4CustomSetup_Actions()
call MultiboardDisplay(udg_wave_multiboard, true)
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
//
// Output intro text using leaderboard title in a manner similar to Catlevania II.
//
function Trig_SpawnBoss4_Output_Intro_Text takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer timer_handle = GetHandleId(t)
local integer curr_i = LoadInteger(udg_timer_hashtable, timer_handle, 0)
local integer max_i = LoadInteger(udg_timer_hashtable, timer_handle, 1)
local string intro_text = LoadStr(udg_timer_hashtable, timer_handle, 2)
local string curr_text = ""
local string curr_char = SubString(intro_text, curr_i-1, curr_i)
local multiboarditem middle_board_item = MultiboardGetItem(udg_cinematic_multiboard, 2, 0)
if curr_i <= max_i then
if curr_i > 1 and (curr_char == "\n" or curr_char == ".") then
call StopSound( gg_snd_castlevania_text_blip, false, false )
elseif not GetSoundIsPlaying( gg_snd_castlevania_text_blip ) then
call StartSound( gg_snd_castlevania_text_blip )
endif
set curr_text = SubString(intro_text, 0, curr_i)
call MultiboardSetItemValue(middle_board_item, curr_text)
set curr_i = curr_i + 1
call SaveInteger(udg_timer_hashtable, timer_handle, 0, curr_i)
else
call PauseTimer(t)
call DestroyTimer(t)
call FlushChildHashtable(udg_timer_hashtable, timer_handle)
// Start next step of intro sequence
set t = CreateTimer()
call TimerStart(t, 1.00, false, function Trig_SpawnBoss4_Spawn_Boss)
endif
call MultiboardReleaseItem(middle_board_item)
set middle_board_item = null
set t = null
endfunction
//
// Setup and starts the intro output timer.
// Intro will consist of 2 steps: Output text and spawn boss unit.
//
function Trig_SpawnBoss4_Start_Intro takes nothing returns nothing
local string intro_text = "\nTIME\n TO \nDIE."
local integer intro_text_length = StringLength(intro_text)
local timer output_intro_timer = CreateTimer()
local integer timer_handle = GetHandleId(output_intro_timer)
call SaveInteger(udg_timer_hashtable, timer_handle, 0, 0)
call SaveInteger(udg_timer_hashtable, timer_handle, 1, intro_text_length)
call SaveStr(udg_timer_hashtable, timer_handle, 2, intro_text)
call TimerStart(output_intro_timer, 0.1, true, function Trig_SpawnBoss4_Output_Intro_Text)
set output_intro_timer = null
endfunction
//
// Main actions: display text, change time of day and spawn boss.
//
function Trig_SpawnBoss4_Actions takes nothing returns nothing
local multiboarditem middle_board_item = MultiboardGetItem(udg_cinematic_multiboard, 2, 0)
call MultiboardDisplay(udg_wave_multiboard, false)
call MultiboardDisplay(udg_cinematic_multiboard, true)
call MultiboardMinimize(udg_cinematic_multiboard, false)
// Setup cinematic multiboard
call MultiboardSetColumnCount(udg_cinematic_multiboard, 1)
call MultiboardSetRowCount(udg_cinematic_multiboard, 6)
call MultiboardSetItemsWidth(udg_cinematic_multiboard, 0.06)
call MultiboardSetItemsStyle(udg_cinematic_multiboard, true, false)
call MultiboardSetTitleText(udg_cinematic_multiboard, "Boss Approaching")
// Start the intro
call CinematicFadeBJ( bj_CINEFADETYPE_FADEOUTIN, 6.0, "ReplaceableTextures\\CameraMasks\\Black_mask.blp", 0, 0, 0, 0 )
call SetMusicVolume(85)
call Trig_SpawnBoss4_Start_Intro()
endfunction
//===========================================================================
function InitTrig_SpawnBoss4 takes nothing returns nothing
set gg_trg_SpawnBoss4 = CreateTrigger()
call TriggerAddAction( gg_trg_SpawnBoss4, function Trig_SpawnBoss4_Actions )
endfunction
function OutroUtils_RepairRoad takes nothing returns nothing
//Cover Grass
call SetTerrainType(-1152, -3072, 'Yhdg', 0, 5, 1)
call SetTerrainType(0, -3072, 'Yhdg', 0, 5, 1)
call SetTerrainType(1152, -3072, 'Yhdg', 0, 5, 1)
//Road
call SetTerrainType(0, -3456, 'Yblm', 0, 3, 1)
call SetTerrainType(0, -2816, 'Yblm', 0, 3, 1)
endfunction
function OutroUtils_CreateKingUnits takes nothing returns nothing
local real kingX = 0
local real kingY = -1280
local integer i = 0
local unit footman
set udg_wave_specific_unit = CreateUnit(Player(6), 'h013', kingX, kingY, 90)
call SetUnitAnimation(udg_wave_specific_unit, "Stand Talk")
loop
exitwhen i == 9
set i = i + 1
set footman = CreateUnit(Player(6), 'h015', kingX - 192, kingY - 256 * i, 0)
call SetUnitAnimation(footman, "Stand Victory")
set footman = CreateUnit(Player(6), 'h015', kingX + 192, kingY - 256 * i, 180)
call SetUnitAnimation(footman, "Stand Victory")
endloop
set footman = null
endfunction
function OutroUtils_PositionUnits takes nothing returns nothing
local integer heroesCount = CountUnitsInGroup(udg_heroes_unit_group)
local unit hero = BlzGroupUnitAt(udg_heroes_unit_group, 0)
call SetUnitX(hero, 128)
call SetUnitY(hero, -832)
call SetUnitFacing(hero, 250)
if(heroesCount > 1) then
set hero = BlzGroupUnitAt(udg_heroes_unit_group, 1)
call SetUnitX(hero, -128)
call SetUnitY(hero, -832)
call SetUnitFacing(hero, 285)
if(heroesCount > 2) then
set hero = BlzGroupUnitAt(udg_heroes_unit_group, 2)
call SetUnitX(hero, 320)
call SetUnitY(hero, -896)
call SetUnitFacing(hero, 225)
if(heroesCount > 3) then
set hero = BlzGroupUnitAt(udg_heroes_unit_group, 3)
call SetUnitX(hero, -320)
call SetUnitY(hero, -896)
call SetUnitFacing(hero, 315)
endif
endif
endif
set hero = null
call OutroUtils_CreateKingUnits()
endfunction
//
// Checks whether all enemies have been destroyed.
//
function Trig_WaveCompleted_Conditions takes nothing returns boolean
return udg_wave_completed == false and IsUnitInGroup(GetTriggerUnit(), udg_current_wave_unit_group) and IsUnitGroupDeadBJ(udg_current_wave_unit_group) == true
endfunction
//
// Checks if unit is a hero.
//
function Trig_WaveCompleted_IsHero takes nothing returns boolean
return IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO)
endfunction
//
// Changes the owner of the ressurection stone back to its Player owner.
//
function Trig_WaveCompleted_MakeRessurectionStonePlayer takes nothing returns nothing
local player current_player = GetEnumPlayer()
if current_player == Player(0) then
call SetUnitOwner( gg_unit_n00H_0020, Player(0), true )
elseif current_player == Player(1) then
call SetUnitOwner( gg_unit_n00H_0021, Player(1), true )
elseif current_player == Player(2) then
call SetUnitOwner( gg_unit_n00H_0022, Player(2), true )
elseif current_player== Player(3) then
call SetUnitOwner( gg_unit_n00H_0023, Player(3), true )
endif
set current_player = null
endfunction
//
// Moves unit to the marketplace entrance and remove all negative buffs.
//
function Trig_WaveCompleted_TeleportUnit takes nothing returns nothing
local location marketplace_point = GetRectCenter( gg_rct_MarketplaceEntrance )
local unit enum_unit = GetEnumUnit()
local effect teleport_fx = null
call SetUnitPositionLoc( enum_unit, marketplace_point )
set teleport_fx = AddSpecialEffectTarget( "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl", enum_unit, "origin")
call UnitRemoveBuffs( enum_unit, false, true )
call DestroyEffect( teleport_fx )
call RemoveLocation( marketplace_point )
set enum_unit = null
set marketplace_point = null
set teleport_fx = null
endfunction
//
// Moves all units owned by player to the marketplace entrance.
//
function Trig_WaveCompleted_TeleportPlayerUnits takes nothing returns nothing
local player curr_player = GetEnumPlayer()
local location marketplace_point = GetRectCenter( gg_rct_MarketplaceEntrance )
local boolexpr is_hero_expr = Condition (function Trig_WaveCompleted_IsHero )
local group hero_units = GetUnitsOfPlayerMatching( curr_player, is_hero_expr )
call ForGroup( hero_units, function Trig_WaveCompleted_TeleportUnit )
call PanCameraToTimedLocForPlayer( curr_player, marketplace_point, 0.50 )
call DestroyGroup( hero_units )
call DestroyBoolExpr( is_hero_expr )
call RemoveLocation( marketplace_point )
set hero_units = null
set is_hero_expr = null
set marketplace_point = null
set curr_player = null
endfunction
//
// Teleport players to the shop area.
//
function Trig_WaveCompleted_TeleportToShop takes nothing returns nothing
local timer t = GetExpiredTimer()
set udg_wave_completed = false
call ForForce( udg_current_human_players, function Trig_WaveCompleted_TeleportPlayerUnits )
call StopMusic( true )
call TriggerExecute( gg_trg_MarketplaceEnter )
call ReleaseTimer( t )
set t = null
endfunction
function Trig_WaveCompleted_StartOutro takes nothing returns nothing
local timer t = GetExpiredTimer()
call ReleaseTimer( t )
call TriggerExecute( gg_trg_Outro )
set udg_wave_completed = false
set t = null
endfunction
//
// Creates the next wave.
//
function Trig_WaveCompleted_CreateNextWave takes nothing returns nothing
local timer t = GetExpiredTimer()
set udg_current_wave_nr = ( udg_current_wave_nr + 1 )
set udg_wave_current = I2R(udg_current_wave_nr)
set udg_wave_completed = false
call TriggerExecute( gg_trg_CreateWave )
call ReleaseTimer( t )
set t = null
endfunction
function Trig_WaveCompleted_ResurrectDestructable takes nothing returns nothing
call DestructableRestoreLife( GetEnumDestructable(), GetDestructableMaxLife(GetEnumDestructable()), true )
endfunction
function Trig_WaveCompleted_Actions takes nothing returns nothing
local timer t = NewTimer()
local integer next_wave = udg_current_wave_nr + 1
local integer goldReward = udg_wave_gold_reward[udg_current_wave_nr]
local boolean isShoppingWave = next_wave - (next_wave / 5) * 5 == 0
local boolean isBossWave = next_wave - (next_wave / 10) * 10 == 0
set udg_wave_completed = true
if udg_current_human_players_count == 1 and isBossWave then
set goldReward = goldReward + 500 * (next_wave / 10)
endif
call GiveGoldToPlayers(goldReward)
call DisplayTextToForce( GetPlayersAll(), ( "|cffffcc00WAVE " + ( I2S(( udg_current_wave_nr + 1 )) + " COMPLETED. REWARD: "+I2S(goldReward)+"!|r" ) ) )
// For each fifth wave, allow shop time for players
if isShoppingWave then
if next_wave == 40 then
call TimerStart( t, 5.00, false, function Trig_WaveCompleted_StartOutro )
else
call TimerStart( t, 5.00, false, function Trig_WaveCompleted_TeleportToShop )
call ForForce( udg_current_human_players, function Trig_WaveCompleted_MakeRessurectionStonePlayer )
call EnumDestructablesInRectAll( gg_rct_MainArena, function Trig_WaveCompleted_ResurrectDestructable )
endif
elseif next_wave < 40 then
call TimerStart( t, 5.00, false, function Trig_WaveCompleted_CreateNextWave )
endif
set t = null
endfunction
//===========================================================================
function InitTrig_WaveCompleted takes nothing returns nothing
set gg_trg_WaveCompleted = CreateTrigger( )
call TriggerRegisterPlayerUnitEventSimple( gg_trg_WaveCompleted, Player(8), EVENT_PLAYER_UNIT_DEATH )
call TriggerRegisterPlayerUnitEventSimple( gg_trg_WaveCompleted, Player(9), EVENT_PLAYER_UNIT_DEATH )
call TriggerRegisterPlayerUnitEventSimple( gg_trg_WaveCompleted, Player(10), EVENT_PLAYER_UNIT_DEATH )
call TriggerRegisterPlayerUnitEventSimple( gg_trg_WaveCompleted, Player(11), EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition( gg_trg_WaveCompleted, Condition( function Trig_WaveCompleted_Conditions ) )
call TriggerAddAction( gg_trg_WaveCompleted, function Trig_WaveCompleted_Actions )
endfunction
//
// Changes the owner of the Player's ressurection stone to Neutral Passive.
//
function Trig_MarketplaceExit_MakeRessurectionStonePassive takes nothing returns nothing
local player current_player = GetEnumPlayer()
if current_player == Player(0) then
call SetUnitOwner( gg_unit_n00H_0020, Player(PLAYER_NEUTRAL_PASSIVE), false )
elseif current_player == Player(1) then
call SetUnitOwner( gg_unit_n00H_0021, Player(PLAYER_NEUTRAL_PASSIVE), false )
elseif current_player == Player(2) then
call SetUnitOwner( gg_unit_n00H_0022, Player(PLAYER_NEUTRAL_PASSIVE), false )
elseif current_player == Player(3) then
call SetUnitOwner( gg_unit_n00H_0023, Player(PLAYER_NEUTRAL_PASSIVE), false )
endif
set current_player = null
endfunction
//
// Pans camera to player spawn for player.
//
function Trig_MarketplaceExit_PanCamera takes nothing returns nothing
call PanCameraToTimedLocForPlayer( GetEnumPlayer(), udg_center_player_spawn_point, 0.50 )
endfunction
//
// Trigger conditionns. All player heroes must be in teleport zone before teleportation occurs.
//
function Trig_MarketplaceExit_Conditions takes nothing returns boolean
local boolean result = true
local unit curr_unit = null
local region exit_region = CreateRegion()
local group tempGroup = CreateGroup()
call BlzGroupAddGroupFast(udg_heroes_unit_group, tempGroup)
call RegionAddRect( exit_region, gg_rct_MarketplaceExit )
set curr_unit = FirstOfGroup(tempGroup)
loop
exitwhen curr_unit == null
call GroupRemoveUnit(tempGroup, curr_unit)
if UnitAlive(curr_unit) and not IsUnitInRegion( exit_region, curr_unit ) then
call DisplayTextToForce( GetPlayersAll(), "|cffff0000YOU MUST GATHER YOUR PARTY BEFORE VENTURING FORTH|r")
if not GetSoundIsPlaying( gg_snd_baldurs_gate_gather_party ) then
call StartSound(gg_snd_baldurs_gate_gather_party)
endif
set result = false
set curr_unit = null
else
set curr_unit = FirstOfGroup(tempGroup)
endif
endloop
call DestroyGroup( tempGroup )
call RemoveRegion( exit_region )
set tempGroup = null
set exit_region = null
return result
endfunction
function Trig_MarketplaceExit_Victory takes nothing returns nothing
local player current_player = GetEnumPlayer()
call CustomVictoryBJ( current_player, true, true )
set current_player = null
endfunction
//
// Teleports unit to player spawn (the Arena)
//
function Trig_MarketplaceExit_TeleportUnit takes unit u returns nothing
call SetUnitPositionLoc( u, udg_center_player_spawn_point )
call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl", u, "origin") )
call SetUnitColor( u, GetPlayerColor(GetOwningPlayer(u)) )
call DestroyEffect( udg_battle_arena_flag[GetConvertedPlayerId(GetOwningPlayer(u))] )
endfunction
//
// Teleport all hero units to the player spawn area for all human players
// + start the next wave.
//
function Trig_MarketplaceExit_Actions takes nothing returns nothing
local timer t = NewTimer()
//Teleport units
local group tempGroup = CreateGroup()
local unit u
set udg_current_arena = gg_rct_MainAreaIncludeSpawners
call BlzGroupAddGroupFast(udg_heroes_unit_group, tempGroup)
call BlzGroupAddGroupFast(udg_al_leadership_units, tempGroup)
loop
set u = FirstOfGroup(tempGroup)
exitwhen u == null
if UnitAlive(u) then
call Trig_MarketplaceExit_TeleportUnit(u)
endif
call GroupRemoveUnit(tempGroup, u)
endloop
call DestroyGroup(tempGroup)
set tempGroup = null
call ForForce( udg_current_human_players, function Trig_MarketplaceExit_MakeRessurectionStonePassive )
call ForForce( udg_current_human_players, function Trig_MarketplaceExit_PanCamera )
call Trig_MarketplaceExitGui_Actions()
call TimerStart( t, 5.00, false, function Trig_WaveCompleted_CreateNextWave )
// Need to change music sometimes - Yes, this is a hax.
if udg_current_wave_nr < 5 then
call StopMusic(true)
call SetMusicVolume(100)
call PlayMusicBJ(gg_snd_Castlevania3_Stage1)
elseif udg_current_wave_nr < 15 then
call StopMusic(true)
call SetMusicVolume(100)
call PlayMusicBJ(gg_snd_Castlevania2_Stage_Theme)
call SetTimeOfDay( 24.00 )
elseif udg_current_wave_nr < 25 then
call StopMusic(true)
call SetMusicVolume(100)
call PlayMusicBJ(gg_snd_Warcraft2_Human02)
//Weather
call TriggerExecuteWait(gg_trg_StartSnow)
elseif udg_current_wave_nr < 35 then
call StopMusic(true)
call SetMusicVolume(100)
call PlayMusicBJ(gg_snd_Warcraft1_Orc01)
//Weather
call TriggerExecuteWait(gg_trg_StartRain)
else
call ForForce( udg_current_human_players, function Trig_MarketplaceExit_Victory )
call ReleaseTimer(t)
endif
set t = null
endfunction
//===========================================================================
function InitTrig_MarketplaceExit takes nothing returns nothing
set gg_trg_MarketplaceExit = CreateTrigger( )
call TriggerRegisterEnterRectSimple( gg_trg_MarketplaceExit, gg_rct_MarketplaceExit )
call TriggerAddCondition( gg_trg_MarketplaceExit, Condition( function Trig_MarketplaceExit_Conditions ) )
call TriggerAddAction( gg_trg_MarketplaceExit, function Trig_MarketplaceExit_Actions )
endfunction
scope SkeletonMageProjectile
private struct SkeletonMageProjectileWithIndicator extends Missiles
effect indicator
boolean hasRedirected
method onFinish takes nothing returns boolean
local real distance
if hasRedirected then
call damageAoENoFF(source, x, y, damage, 56., DAMAGE_TYPE_DEATH)
call DestroyEffect(indicator)
set indicator = null
return true
else
set hasRedirected = true
call deflect(BlzGetLocalSpecialEffectX(indicator), BlzGetLocalSpecialEffectY(indicator), BlzGetLocalSpecialEffectZ(indicator))
set distance = distanceBetweenXYXY(x, y, BlzGetLocalSpecialEffectX(indicator), BlzGetLocalSpecialEffectY(indicator))
set speed = distance / 0.45
return false
endif
endmethod
static method fireNearTarget takes unit c, unit target, real dmg returns nothing
local real z = GetUnitFlyHeight(c) + 64.
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real utx = GetUnitX(target)
local real uty = GetUnitY(target)
local real angle = Atan2(uty - y, utx - x)
local real tx = x + 121. * Cos(angle)
local real ty = y + 121. * Sin(angle)
local real rAngle = angle + GetRandomReal(-0.36 * bj_PI, 0.36 * bj_PI)
local real rDist = GetRandomReal(-16., 96.)
local thistype this = thistype.create(x + 21. * Cos(angle), y + 21. * Sin(angle), z, tx, ty, GetZHeight(tx, ty) + 340.)
set source = c
set model = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
set speed = 230.
set collision = 0.
set damage = dmg //GetTotalDamage(c)
set owner = GetOwningPlayer(c)
set tx = utx + rDist * Cos(rAngle)
set ty = uty + rDist * Sin(rAngle)
set indicator = AddSpecialEffect("vfx\\Circle_200.mdx", tx, ty)
call BlzSetSpecialEffectScale(indicator, 0.5 * 0.56)
call BlzSetSpecialEffectZ(indicator, GetZHeight(tx, ty) + 10.)
call BlzSetSpecialEffectColor(indicator, 100, 200, 100)
call BlzSetSpecialEffectAlpha(indicator, 230)
set hasRedirected = false
call launch()
endmethod
endstruct
function fireSkeletalMageProjectileTimerExpired takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer i = GetTimerData(t)
local Table timerSubtable = timerTable[i]
local unit attacker = timerSubtable.unit[1]
if UnitAlive(attacker) and not IsUnitStunned(attacker) then
call SkeletonMageProjectileWithIndicator.fireNearTarget(attacker, timerSubtable.unit[2], timerSubtable.real[3])
endif
call timerSubtable.destroy()
call timerTable.remove(i)
call ReleaseTimer(t)
set t = null
endfunction
function fireSkeletalMageProjectile takes unit c, unit target, real dmg returns nothing
local timer t = NewTimer()
local integer i = GetHandleId(t)
call SetTimerData(t, i)
call timerTable.link(i).unit.save(1, c).unit.save(2, target).real.save(3, dmg)
call TimerStart(t, 0.38, false, function fireSkeletalMageProjectileTimerExpired)
set t = null
endfunction
endscope
scope Dracula
private struct Skull extends Missiles
method onHit takes unit u returns boolean
if u != source and BlzIsUnitSelectable(u) and IsUnitEnemy(u, owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DEATH, null)
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl", x, y))
return true
endif
return false
endmethod
static method fireNewMissile takes unit c, real sx, real sy, real tx, real ty, real curveAmount, real speedAmount, real dmg returns nothing
local thistype this = thistype.create(sx, sy, 60.0, tx, ty, 60.0)
set source = c
set model = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl"
set speed = speedAmount
set arc = 1.0
set collision = 26.0
set damage = dmg
set owner = GetOwningPlayer(c)
set curve = curveAmount
//set target = GetSpellTargetUnit()
call launch()
endmethod
endstruct
function ghostTargetedSkull takes unit c, unit target, real distance, real dmg returns nothing
local real curve = GetRandomReal(-1.0, 1.0)
local real speed = GetRandomReal(300.0, 350.0)
local real sx = GetUnitX(c)
local real sy = GetUnitY(c)
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local real angle = Atan2(ty - sy, tx - sx)
set tx = sx + distance * Cos(angle)
set ty = sy + distance * Sin(angle)
call Skull.fireNewMissile(c, sx, sy, tx, ty, curve, speed, dmg)
endfunction
function draculaTargetedSkull takes real angle, real dmgPercent returns nothing
local real curve = GetRandomReal(-1.0, 1.0)
local real speed = GetRandomReal(700.0, 800.0)
local real sx = GetUnitX(udg_wave_specific_unit)
local real sy = GetUnitY(udg_wave_specific_unit)
local real tx = sx + 2000.0 * Cos(angle)
local real ty = sy + 2000.0 * Sin(angle)
local real dmg = dmgPercent * GetTotalDamage(udg_wave_specific_unit)
call Skull.fireNewMissile(udg_wave_specific_unit, sx, sy, tx, ty, curve, speed, dmg)
endfunction
function draculaRandomSkull takes nothing returns nothing
local real angle = GetRandomDirectionDeg()
local real speed = GetRandomReal(200.0, 300.0)
local real curve = GetRandomReal(-30.0, 30.0)
local real startAngle = GetRandomReal(-30.0, 30.0)
local real endAngle = GetRandomReal(-30.0, 30.0)
local real x = 3500.0 * Cos(bj_PI / 180.0 * (angle + startAngle))
local real y = 3500.0 * Sin(bj_PI / 180.0 * (angle + startAngle))
local real tx = 3500.0 * Cos(bj_PI + bj_PI / 180.0 * (angle + endAngle))
local real ty = 3500.0 * Sin(bj_PI + bj_PI / 180.0 * (angle + endAngle))
call Skull.fireNewMissile(udg_wave_specific_unit, x, y, tx, ty, curve, speed, 0.35 * GetTotalDamage(udg_wave_specific_unit))
endfunction
endscope
function Dracula1FlyingSkullNoInRangeTeleportFinish takes nothing returns nothing
call DestroyEffect(LoadEffectHandle(udg_wave_specific_hashtable, 1, StringHash("tp_boss")))
call DestroyEffect(LoadEffectHandle(udg_wave_specific_hashtable, 1, StringHash("tp_target")))
call SetUnitX(udg_wave_specific_unit, udg_ws_reals1[11])
call SetUnitY(udg_wave_specific_unit, udg_ws_reals1[12])
//call BlzPauseUnitEx(udg_wave_specific_unit, true)
call TriggerExecute( gg_trg_Dracula1RetriggerActiveAbility )
call SetUnitTimeScale(udg_wave_specific_unit, 1.0)
call SetUnitAnimation(udg_wave_specific_unit, "stand")
call IssuePointOrder( udg_wave_specific_unit, "attack", 0.0, 0.0 )
endfunction
function Dracula1TeleportToFurtherestUnit takes nothing returns nothing
local real bossx = GetUnitX(udg_wave_specific_unit)
local real bossy = GetUnitY(udg_wave_specific_unit)
call NPCGetHeroFurthestAway(udg_wave_specific_unit)
//Abilities\\Spells\\Demon\\DarkConversion\\ZombifyTarget.mdl
set udg_ws_reals1[11] = GetUnitX(udg_temp_unit) //+ GetRandomReal(64.0, 128.0) * Cos()
set udg_ws_reals1[12] = GetUnitY(udg_temp_unit)
set udg_ws_ints1[2] = 9
call SaveEffectHandle(udg_wave_specific_hashtable, 1, StringHash("tp_boss"), AddSpecialEffect("Abilities\\Spells\\Demon\\DarkConversion\\ZombifyTarget.mdl", bossx, bossy))
call SaveEffectHandle(udg_wave_specific_hashtable, 1, StringHash("tp_target"), AddSpecialEffect("Abilities\\Spells\\Demon\\DarkConversion\\ZombifyTarget.mdl", GetUnitX(udg_temp_unit), GetUnitY(udg_temp_unit)))
//call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Banish\\BanishTarget.mdl", GetUnitX(udg_wave_specific_unit), GetUnitY(udg_wave_specific_unit)))
//call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Banish\\BanishTarget.mdl", GetUnitX(udg_temp_unit), GetUnitY(udg_temp_unit)))
//call BlzPauseUnitEx(udg_wave_specific_unit, false)
call IssuePointOrder(udg_wave_specific_unit, "farsight", udg_ws_reals1[11], udg_ws_reals1[12])
call SetUnitTimeScale(udg_wave_specific_unit, 0.75)
call SetUnitAnimationByIndex(udg_wave_specific_unit, 4) // Play Spell (standing, 1.0 seconds)
call TimerStart(NewTimer(), 1.30, false, function Dracula1FlyingSkullNoInRangeTeleportFinish)
//Abilities\Spells\Human\Banish\BanishTarget.mdl
//Teleport to that point with this vfx. "Spell" takes 1.0 seconds
//call BJDebugMsg("TP")
endfunction
//===========================================================================
function Dracula1FlyingSkull_LandVfxCreate takes nothing returns nothing
local real x = GetUnitX(udg_wave_specific_unit)
local real y = GetUnitY(udg_wave_specific_unit)
local effect e = AddSpecialEffect("Abilities\\Spells\\Other\\Drain\\DrainTarget.mdl", x, y)
call BlzSetSpecialEffectScale(e, 3.0)
call DestroyEffectLater(e, 0.9)
set e = null
endfunction
function Dracula1FlyingSkull_Landed takes nothing returns nothing
local real x = GetUnitX(udg_wave_specific_unit)
local real y = GetUnitY(udg_wave_specific_unit)
local effect e = AddSpecialEffect("Abilities\\Dracula1\\Reapers Claws Gold.mdx", x, y)
call ReleaseTimer(GetExpiredTimer())
call DummyCastNoTarget(x, y, udg_wave_specific_unit, 'A07M', "thunderclap")
call BlzSetSpecialEffectScale(e, 1.5)
call DestroyEffect(e)
set e = null
call IssuePointOrder(udg_wave_specific_unit, "attack", 0.0, 0.0)
call TriggerExecute( gg_trg_Dracula1RetriggerActiveAbility )
endfunction
//Land (End flying)
function Dracula1FlyingSkull_StartLanding takes nothing returns nothing
local real bossx = GetUnitX(udg_wave_specific_unit)
local real bossy = GetUnitY(udg_wave_specific_unit)
local unit u = GetVisibleEnemiesClosestToCoords(udg_wave_specific_unit, bossx, bossy, 1000.0, 100.0)
local real angleToUnit = Atan2(bossy - GetUnitY(u), bossx - GetUnitX(u))
local effect landingVfx = AddSpecialEffect("vfx\\Circle_200.mdx", bossx, bossy)
call BlzSetSpecialEffectScale(landingVfx, 2.0)
call BlzSetSpecialEffectColor(landingVfx, 100, 200, 100)
call DestroyEffectLater(landingVfx, 0.75)
set landingVfx = null
set u = null
//call BJDebugMsg("Time = " + R2S(udg_ws_reals1[99]))
call SetUnitFacing(udg_wave_specific_unit, angleToUnit * bj_RADTODEG)
call IssueImmediateOrder( udg_wave_specific_unit, "unbearform" )
set udg_ws_ints1[1] = 63
call TimerStart(NewTimer(), 0.5, false, function Dracula1FlyingSkull_LandVfxCreate)
call TimerStart(NewTimer(), 0.75, false, function Dracula1FlyingSkull_Landed)
endfunction
function Dracula1FlyingSkull_3_Tick takes nothing returns nothing
local timer t = GetExpiredTimer()
local real angle = udg_ws_reals1[13] + udg_ws_reals1[14]
local real distance = udg_ws_reals1[15]
local real x = udg_ws_reals1[11] + distance * Cos(angle)
local real y = udg_ws_reals1[12] + distance * Sin(angle)
set udg_ws_reals1[14] = udg_ws_reals1[14] + udg_ws_reals1[16] * 1.0 * bj_DEGTORAD
//set udg_ws_ints1[16] = udg_ws_ints1[16] + 1
set udg_ws_reals1[15] = udg_ws_reals1[15] - 7.5
set udg_ws_reals1[99] = udg_ws_reals1[99] + 0.02
call SetUnitX(udg_wave_specific_unit, x)
call SetUnitY(udg_wave_specific_unit, y)
if (distance < 20.0) then
call ReleaseTimer(t)
call Dracula1FlyingSkull_StartLanding()
endif
set t = null
//call BJDebugMsg("Stage 3")
endfunction
function Dracula1FlyingSkull_2_FireSkull takes nothing returns nothing
local real tx
local real ty
local real ux = GetUnitX(udg_wave_specific_unit)
local real uy = GetUnitY(udg_wave_specific_unit)
local real angle
local unit u = GetVisibleEnemiesClosestToCoords(udg_wave_specific_unit, udg_ws_reals1[11], udg_ws_reals1[12], udg_ws_reals1[15], 100.0)
if u != null then
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set u = null
else
set u = GetVisibleEnemiesClosestToCoords(udg_wave_specific_unit, GetUnitX(udg_wave_specific_unit), GetUnitY(udg_wave_specific_unit), 1000.0, 100.0)
if u != null then
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set u = null
else
set tx = udg_ws_reals1[11]
set ty = udg_ws_reals1[12]
endif
endif
set angle = Atan2(ty - uy, tx - ux)
call SetUnitFacing(udg_wave_specific_unit, angle * bj_RADTODEG)
call draculaTargetedSkull(angle, 0.35)
endfunction
function Dracula1FlyingSkull_2_Tick takes nothing returns nothing
local timer t = GetExpiredTimer()
local real angle = udg_ws_reals1[13] + udg_ws_reals1[14]
local real distance = udg_ws_reals1[15]
local real x = udg_ws_reals1[11] + distance * Cos(angle)
local real y = udg_ws_reals1[12] + distance * Sin(angle)
set udg_ws_reals1[14] = udg_ws_reals1[14] + udg_ws_reals1[16] * 1.20 * bj_DEGTORAD
set udg_ws_ints1[16] = udg_ws_ints1[16] + 1
set udg_ws_reals1[99] = udg_ws_reals1[99] + 0.02
call SetUnitX(udg_wave_specific_unit, x)
call SetUnitY(udg_wave_specific_unit, y)
if Mod(udg_ws_ints1[16], 12) == 0 then
call Dracula1FlyingSkull_2_FireSkull()
endif
if RAbsBJ(udg_ws_reals1[14]) > 110.0 * bj_DEGTORAD then //Angle traveled
call ReleaseTimer(t)
call TimerStart(NewTimer(), 0.02, true, function Dracula1FlyingSkull_3_Tick)
endif
set t = null
//call BJDebugMsg("Stage 2")
endfunction
function Dracula1FlyingSkull_1_Tick takes nothing returns nothing
local timer t = GetExpiredTimer()
local real angle = udg_ws_reals1[13] + udg_ws_reals1[14]
local real distance = udg_ws_reals1[15]
local real x = udg_ws_reals1[11] + distance * Cos(angle)
local real y = udg_ws_reals1[12] + distance * Sin(angle)
set udg_ws_reals1[14] = udg_ws_reals1[14] + udg_ws_reals1[16] * 1.0 * bj_DEGTORAD //1.0 degree per tick
set udg_ws_ints1[16] = udg_ws_ints1[16] + 1
set udg_ws_reals1[99] = udg_ws_reals1[99] + 0.02
if udg_ws_reals1[15] < 490.0 then
set udg_ws_reals1[15] = udg_ws_reals1[15] + 6.0
elseif udg_ws_reals1[15] > 510.0 then
set udg_ws_reals1[15] = udg_ws_reals1[15] - 6.0
endif
call SetUnitX(udg_wave_specific_unit, x)
call SetUnitY(udg_wave_specific_unit, y)
if RAbsBJ(udg_ws_reals1[14]) > 20.0 * bj_DEGTORAD then //Angle traveled
call ReleaseTimer(t)
call TimerStart(NewTimer(), 0.02, true, function Dracula1FlyingSkull_2_Tick)
endif
set t = null
//call BJDebugMsg("Stage 1")
endfunction
//First 30 degrees are to adjust distance
function Dracula1FlyingSkull_1_Start takes nothing returns nothing
local real bossx = GetUnitX(udg_wave_specific_unit)
local real bossy = GetUnitY(udg_wave_specific_unit)
local real tx = udg_ws_reals1[11]
local real ty = udg_ws_reals1[12]
call ReleaseTimer(GetExpiredTimer())
set udg_ws_ints1[1] = 62
set udg_ws_reals1[99] = 0.0
//set udg_ws_reals1[11] - Center Point X
//set udg_ws_reals1[12] - Center Point Y
set udg_ws_reals1[13] = Atan2(bossy - ty, bossx - tx) //Base angle
set udg_ws_reals1[14] = 0.0 //Angle traveled
set udg_ws_reals1[15] = distanceBetweenXYXY(bossx, bossy, tx, ty)
set udg_ws_reals1[16] = GetRandomInt(0, 1) * 2.0 - 1.0 //-1 or 1
call IssueImmediateOrder( udg_wave_specific_unit, "windwalk" )
//if IssueImmediateOrder( udg_wave_specific_unit, "windwalk" ) then
//call BJDebugMsg(" -- > Casted spell!")
//endif
//call SetUnitAnimationByIndex(udg_wave_specific_unit, 18) // Play Flying Spell
call TimerStart(NewTimer(), 0.02, true, function Dracula1FlyingSkull_1_Tick)
endfunction
//Jump up (Start flying)
function Dracula1FlyingSkull_JumpUp takes nothing returns nothing
call IssueImmediateOrder( udg_wave_specific_unit, "bearform" )
set udg_ws_ints1[2] = 3 //Current Action Id
set udg_ws_ints1[1] = 61 //Current Sub-action
set udg_ws_ints1[16] = 0 //ticks (used for determine when to send skulls)
call TimerStart(NewTimer(), 1.00, false, function Dracula1FlyingSkull_1_Start)
endfunction
function Dracula1FlyingSkullStart takes nothing returns nothing
if not UnitAlive(udg_wave_specific_unit) then
return
endif
call GetTargetClosestToDistancePerferHeroes(udg_wave_specific_unit, 1000., 500., 150.)
if udg_temp_unit == null then
call Dracula1TeleportToFurtherestUnit()
return
else
set udg_ws_reals1[11] = GetUnitX(udg_temp_unit)
set udg_ws_reals1[12] = GetUnitY(udg_temp_unit)
call Dracula1FlyingSkull_JumpUp()
endif
endfunction
scope Dracula2Spells
globals
real dracula2NecroStormDamage
endglobals
//NecroStorm - Abilities\Dracula2\Necro Storm.mdx
private struct NecroStorm
effect vfx
integer ticks
real speed
real angle
real x
real y
method isWithinBounds takes nothing returns boolean
return -3110.0 < x and x < 3110.0 and -3650.0 < y and y < 2610.0
endmethod
static method tick takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set x = x + speed * Cos(angle)
set y = y + speed * Sin(angle)
set ticks = ticks + 1
if ticks < 300 or isWithinBounds() then //start checking bounds after 6 seconds
call BlzSetSpecialEffectX(vfx, x)
call BlzSetSpecialEffectY(vfx, y)
call BlzSetSpecialEffectZ(vfx, GetZHeight(x, y))
//50 ticks per second. 50 * 15 = 750 damage per second. Don't stand in it!
call damageAoENoFFNoBuildingDamage(udg_wave_specific_unit, x, y, dracula2NecroStormDamage, 190.0, DAMAGE_TYPE_DEATH)
else
call DestroyEffect(vfx)
set vfx = null
call ReleaseTimer(t)
call .deallocate()
endif
set t = null
endmethod
static method startBeam takes real x, real y, real angle, real speed returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
set .x = x
set .y = y
set .angle = angle
set .speed = speed * 0.02 //Parameter is in "units per second", convert to "units per tick"
set .ticks = 0
set .vfx = AddSpecialEffect( "Abilities\\Dracula2\\Necro Storm.mdx", x, y)
call TimerStart(t, .02, true, function thistype.tick)
set t = null
endmethod
endstruct
function randomNecroStorm takes nothing returns nothing
local real angle = GetRandomReal(0.0, 2.0 * bj_PI)
local real startX = GetRandomReal(-1250.0, 1250.0) + 2750.0 * Cos(angle + bj_PI)
local real startY = GetRandomReal(-1750.0, 750.0) + 2750.0 * Sin(angle + bj_PI)
call NecroStorm.startBeam(startX, startY, angle, GetRandomReal(150.0, 325.0))
endfunction
// NETHER BLAST
private struct NeththerBlast
real x
real y
static method dealDamage takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call damageAoENoFFNoBuildingDamage(udg_wave_specific_unit, x, y, 150, 225, DAMAGE_TYPE_DEATH)
call ReleaseTimer(t)
set t = null
call .deallocate()
endmethod
static method startBlast takes real x, real y returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
set .x = x
set .y = y
call DestroyEffect(AddSpecialEffect( "Abilities\\Dracula2\\Nether Blast IV.mdx", x, y))
call TimerStart(t, 1.0, false, function thistype.dealDamage)
set t = null
endmethod
endstruct
private struct NetherBlastProjectiles extends Missiles
effect indicator
method onFinish takes nothing returns boolean
call damageAoENoFFNoBuildingDamage(udg_wave_specific_unit, x, y, damage, 225.0, DAMAGE_TYPE_DEATH)
call NeththerBlast.startBlast(x, y)
call DestroyEffect(indicator)
set indicator = null
return true
endmethod
static method fireNewMissile takes real sx, real sy, real tx, real ty returns nothing
local thistype this = thistype.create(sx, sy, 100, tx, ty, 10)
set source = udg_wave_specific_unit
set model = "Abilities\\Dracula2\\UndeadMissleGreen.mdx"
set speed = 300 + GetRandomReal(0, 200)
set arc = 50
set collision = 0
set damage = 125.
set owner = GetOwningPlayer(udg_wave_specific_unit)
set indicator = AddSpecialEffect("Abilities\\Weapons\\BansheeMissile\\BansheeMissile.mdl", tx, ty)
call BlzSetSpecialEffectPosition(indicator, tx, ty, GetZHeight(tx, ty) + 12.)
//set curve = curveAmount
//set isBossSkull = bossSkull
//set target = GetSpellTargetUnit()
call launch()
endmethod
endstruct
function dracula2NetherBlastProjectilesNearTarget takes real sx, real sy, unit target returns nothing
local real angle = GetRandomReal(0, 2*bj_PI)
local real distance = GetRandomReal(0, 250)
local real tx = GetUnitX(target) + distance * Cos(angle)
local real ty = GetUnitY(target) + distance * Sin(angle)
call NetherBlastProjectiles.fireNewMissile(sx, sy, tx, ty)
endfunction
//sx, sy is source x/y
//tx, ty is target x/y
function dracula2NetherBlastProjectiles takes real sx, real sy, real tx, real ty returns nothing
local integer i = 0
local integer randomHeroIndex = GetRandomInt(0, CountUnitsInGroup(udg_heroes_unit_group) - 1)
local real angle = GetRandomReal(0, 2*bj_PI)
call NetherBlastProjectiles.fireNewMissile(sx, sy, tx, ty)
call NetherBlastProjectiles.fireNewMissile(sx, sy, tx + 270 * Cos(angle), ty + 270 * Sin(angle))
set angle = GetRandomReal(0, 2*bj_PI)
call NetherBlastProjectiles.fireNewMissile(sx, sy, tx + 400 * Cos(angle), ty + 400 * Sin(angle))
loop
exitwhen i == CountUnitsInGroup(udg_heroes_unit_group)
set i = i + 1
call dracula2NetherBlastProjectilesNearTarget(sx, sy, BlzGroupUnitAt(udg_heroes_unit_group, randomHeroIndex))
endloop
endfunction
//------------ SPINNY THINGIE -----------------
private struct NetherBlastSpinnyThingies
real distance
real direction
real angle
effect vfx
static method tick takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local real x
local real y
local real z
set distance = distance + GetRandomReal(2.0, 3.0)
//set angle = angle + direction * .104 // //roughly PI/30, I.E. 60 ticks for 1 revolusion
set angle = angle + direction * 100. / (2 * bj_PI * distance) // //roughly PI/30, I.E. 60 ticks for 1 revolusion
set x = GetUnitX(udg_wave_specific_unit) + distance * Cos(angle)
set y = GetUnitY(udg_wave_specific_unit) + distance * Sin(angle)
set z = GetZHeight(x, y) + 64
call BlzSetSpecialEffectPosition(vfx, x, y, z)
call damageAoENoFFNoBuildingDamage(udg_wave_specific_unit, x, y, 35, 80, DAMAGE_TYPE_DEATH)
if distance > 900 then
call damageAoENoFFNoBuildingDamage(udg_wave_specific_unit, x, y, 200, 200, DAMAGE_TYPE_DEATH)
call ReleaseTimer(t)
call DestroyEffect(vfx)
set vfx = null
call .deallocate()
endif
set t = null
endmethod
static method fireNewMissile takes nothing returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
set vfx = AddSpecialEffect("Abilities\\Dracula2\\UndeadMissleGreen.mdx", GetUnitX(udg_wave_specific_unit), GetUnitY(udg_wave_specific_unit))
set direction = GetRandomInt(0, 1) * 2 - 1
set angle = GetRandomReal(0, 2 * bj_PI)
set distance = 32
call TimerStart(t, 1./60., true, function thistype.tick)
set t = null
endmethod
endstruct
function dracula2FireSpinnyProjectilesFromTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer ticks = GetTimerData(t)
call NetherBlastSpinnyThingies.fireNewMissile()
if ticks > 3 then
call ReleaseTimer(t)
else
call SetTimerData(t, ticks + 1)
endif
set t = null
endfunction
function dracula2FireSpinnyProjectiles takes nothing returns nothing
local timer t = NewTimer()
call NetherBlastSpinnyThingies.fireNewMissile()
call TimerStart(t, 0.7, true, function dracula2FireSpinnyProjectilesFromTimer)
set t = null
endfunction
function dracula2PeriodNecroStorm takes nothing returns nothing
local timer t = GetExpiredTimer()
if UnitAlive(udg_wave_specific_unit) then
if GetRandomReal(0.0, 1.0) > 0.70 then
call randomNecroStorm()
endif
else
call ReleaseTimer(t)
endif
set t = null
endfunction
function dracula2StartPeriodicNecroticStorms takes nothing returns nothing
local timer t = NewTimer()
local real numberOfHeroes = I2R(CountUnitsInGroup(udg_heroes_unit_group))
call TimerStart(t, 4.0 + 2.5 / numberOfHeroes, true, function dracula2PeriodNecroStorm)
set t = null
endfunction
function dracula2StartDirectionalNecroStormBeams takes real angle returns nothing
//Source x/y, angle, function, tick-rate, movespeed-per-tick, duration
//NEW ONE, Soul Beam is to be removed!
local real sourceX = GetUnitX(udg_wave_specific_unit) - 2500.0 * Cos(angle)
local real sourceY = GetUnitY(udg_wave_specific_unit) - 2500.0 * Sin(angle)
local integer i = -6
local real offsetAngle = angle + bj_PI / 2. //90 degrees
local real offsetX
local real offsetY
loop
exitwhen i > 6
set i = i + 1
set offsetX = sourceX + i * 700.0 * Cos(offsetAngle)
set offsetY = sourceY + i * 700.0 * Sin(offsetAngle)
call NecroStorm.startBeam(offsetX, offsetY, angle, 225.0)
endloop
set sourceX = GetUnitX(udg_wave_specific_unit) + 2500.0 * Cos(angle)
set sourceY = GetUnitY(udg_wave_specific_unit) + 2500.0 * Sin(angle)
set i = -6
loop
exitwhen i > 5
set i = i + 1
set offsetX = sourceX + (350.0 + i * 700.0) * Cos(offsetAngle)
set offsetY = sourceY + (350.0 + i * 700.0) * Sin(offsetAngle)
call NecroStorm.startBeam(offsetX, offsetY, angle + bj_PI, 225.0)
endloop
endfunction
private struct DarknessRising
effect vfx
real x
real y
integer tick
integer projectileSpawnTick
static method update takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call damageAoENoFFNoBuildingDamage(udg_wave_specific_unit, x, y, 30.0, 225, DAMAGE_TYPE_DEATH)
if tick > 40 then
call DestroyEffect(vfx)
call ReleaseTimer(t)
set vfx = null
elseif tick == projectileSpawnTick then
set projectileSpawnTick = projectileSpawnTick + GetRandomInt(5, 8)
call dracula2NetherBlastProjectilesNearTarget(x, y, GroupPickRandomUnit(udg_heroes_unit_group))
endif
set tick = tick + 1
set t = null
endmethod
static method setup takes real x, real y returns nothing
local thistype this = thistype.allocate()
local timer t = NewTimerEx(this)
set .x = x
set .y = y
set .tick = 0
set .projectileSpawnTick = 6
set vfx = AddSpecialEffect( "Abilities\\Dracula2\\DarknessRising.mdx", x, y)
call TimerStart(t, 0.15, true, function thistype.update)
set t = null
endmethod
endstruct
function dracula2DarknessRising takes nothing returns nothing
local real angle = GetRandomReal(0, 2*bj_PI)
local real distance = GetRandomReal(200, 550)
local real tx = GetUnitX(udg_wave_specific_unit) + distance * Cos(angle)
local real ty = GetUnitY(udg_wave_specific_unit) + distance * Sin(angle)
call DarknessRising.setup(tx, ty)
set angle = GetRandomReal(0, 2*bj_PI)
set distance = GetRandomReal(600, 1000)
set tx = GetUnitX(udg_wave_specific_unit) + distance * Cos(angle)
set ty = GetUnitY(udg_wave_specific_unit) + distance * Sin(angle)
call DarknessRising.setup(tx, ty)
endfunction
endscope
function Dracula2TeleportAwayOriginPoint takes real x, real y returns nothing
local group targetsWithinRadius = CreateGroup()
local unit u
local real unitCollisonSize
local real distanceToCenter
local real ux
local real uy
local real dx
local real dy
call DestroyEffect(AddSpecialEffect( "Abilities\\Dracula2\\Call of Dread Green.mdx", x, y))
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 300, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if not (u == udg_wave_specific_unit) then
set unitCollisonSize = BlzGetUnitCollisionSize(u)
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set dx = x - ux
set dy = y - uy
set distanceToCenter = SquareRoot(dx * dx + dy * dy)
if distanceToCenter < 250.0 + unitCollisonSize/2.0 then
call UnitDamageTarget(udg_wave_specific_unit, u, 100, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
set udg_Knockback2DUnit = u
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(dy, dx)
set udg_Knockback2DDistance = 250 + unitCollisonSize/2.0 - distanceToCenter
set udg_Knockback2DTime = 0.5
set udg_Knockback2DCollision = 8.0
call TriggerExecute(gg_trg_Knockback_2D)
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup(targetsWithinRadius)
set targetsWithinRadius = null
endfunction
function Dracula2TeleportAway takes nothing returns nothing
local real x = GetUnitX(udg_wave_specific_unit)
local real y = GetUnitY(udg_wave_specific_unit)
local real newX //= GetRandomReal(GetRectMinX(gg_rct_MainArena), GetRectMaxX(gg_rct_MainArena))
local real newY //= GetRandomReal(GetRectMinY(gg_rct_MainArena), GetRectMaxY(gg_rct_MainArena))
call Dracula2TeleportAwayOriginPoint(x, y) // Handles origin-point vfx, damage, and stuff
// Place item and check its actual location, this way we don't place the character in a place where an item cannot be placed...
loop
set newX = GetRandomReal(GetRectMinX(gg_rct_MainArena), GetRectMaxX(gg_rct_MainArena))
set newY = GetRandomReal(GetRectMinY(gg_rct_MainArena), GetRectMaxY(gg_rct_MainArena))
call SetItemPosition(udg_CP_Item, newX, newY)
set newX = GetItemX(udg_CP_Item)
set newY = GetItemY(udg_CP_Item)
exitwhen distanceBetweenXYXY(x, y, newX, newY) > 1550 // Teleport at least this far away!
endloop
call SetItemPosition(udg_CP_Item, 5000, 0) //Outside arena test area, to prevent it to show
call DestroyEffect(AddSpecialEffect( "Abilities\\Dracula2\\Call of Dread Green.mdx", newX, newY))
call SetUnitX(udg_wave_specific_unit, newX)
call SetUnitY(udg_wave_specific_unit, newY)
call dracula2NetherBlastProjectiles(newX, newY, x, y)
endfunction
function Dracula2MainAITakeNewAction takes nothing returns nothing
local integer aiAction = GetRandomInt(0, 5)
if aiAction <= 1 then
call Dracula2TeleportAway()
elseif aiAction <= 2 then
call dracula2FireSpinnyProjectiles()
elseif aiAction <= 3 then
call dracula2DarknessRising()
elseif aiAction <= 5 then
call dracula2StartDirectionalNecroStormBeams(GetRandomReal(0, bj_PI * 2.))
endif
call StartTimerBJ( udg_wave_specific_timer2, false, GetRandomReal(16.00, 22.00) )
//call BJDebugMsg("aiAction=" + I2S(aiAction))
endfunction
scope ChogallBloodlust initializer init
globals
constant integer CHOGALL_BLOODLUST_VFX_ABILITY = 'A01Z'
private code bloodlustCast
endglobals
private function End takes nothing returns nothing
call TimerStart(GetExpiredTimer(), 35.0, false, bloodlustCast)
call UnitRemoveAbility(udg_wave_specific_unit, CHOGALL_BLOODLUST_VFX_ABILITY)
set udg_wave_specific_bool1 = false
endfunction
private function Start takes nothing returns nothing
local real duration = 9.0 + udg_wave_specific_int1
call AddUnitBonusTimed(udg_wave_specific_unit, BonusMovementSpeed.typeid, 90 + 20 * udg_wave_specific_int1, duration)
call AddUnitBonusTimed(udg_wave_specific_unit, BonusAttackSpeed.typeid, 0.35 + 0.05 * udg_wave_specific_int1, duration)
call UnitAddAbility(udg_wave_specific_unit, CHOGALL_BLOODLUST_VFX_ABILITY)
call IssuePointOrder(udg_wave_specific_unit, "attack", 0.0, 0.0)
call TimerStart(GetExpiredTimer(), duration, false, function End)
endfunction
public function ChogallBloodlustCast takes nothing returns nothing
set udg_wave_specific_int1 = udg_wave_specific_int1 + 1
set udg_wave_specific_bool1 = true
call IssueImmediateOrder( udg_wave_specific_unit, "channel" )
call TimerStart(udg_wave_specific_timer, 1.5, false, function Start)
endfunction
private function init takes nothing returns nothing
set bloodlustCast = function ChogallBloodlustCast
endfunction
endscope
struct ChogallOnHit extends OnHit
static method timeout takes nothing returns nothing
local integer unitIndex = GetTimerData(GetExpiredTimer())
local integer hitCounter = udg_ws_ints1[unitIndex]
if hitCounter == 2 then
set udg_ws_ints1[unitIndex] = hitCounter - 1
call BlzSpecialEffectClearSubAnimations(udg_ws_vfxs1[unitIndex])
call BlzPlaySpecialEffect(udg_ws_vfxs1[unitIndex], ANIM_TYPE_STAND)
call TimerStart(udg_ws_timers1[unitIndex], 1.5, false, function thistype.timeout)
elseif hitCounter == 3 then
set udg_ws_ints1[unitIndex] = hitCounter - 1
call BlzSpecialEffectRemoveSubAnimation(udg_ws_vfxs1[unitIndex], SUBANIM_TYPE_THREE)
call BlzSpecialEffectAddSubAnimation(udg_ws_vfxs1[unitIndex], SUBANIM_TYPE_TWO)
call BlzPlaySpecialEffect(udg_ws_vfxs1[unitIndex], ANIM_TYPE_STAND)
call TimerStart(udg_ws_timers1[unitIndex], 1.5, false, function thistype.timeout)
else
//hitCounter == 1 or something strange occurred. Clear.
call DestroyEffect(udg_ws_vfxs1[unitIndex])
call ReleaseTimer(udg_ws_timers1[unitIndex])
set udg_ws_timers1[unitIndex] = null
set udg_ws_vfxs1[unitIndex] = null
set udg_ws_ints1[unitIndex] = 0
endif
endmethod
method onHit takes unit source, integer chogallHits returns real
local integer unitIndex = GetUnitUserData(Damage.target)
local integer hitCounter = udg_ws_ints1[unitIndex] + 1
if udg_ws_timers1[unitIndex] == null then
set udg_ws_timers1[unitIndex] = NewTimerEx(unitIndex)
endif
call TimerStart(udg_ws_timers1[unitIndex], 6.5, false, function thistype.timeout)
if hitCounter == 1 then
set udg_ws_vfxs1[unitIndex] = AddSpecialEffectTarget("Units\\Chogall\\LightningShieldTargetSteps.mdx", Damage.target, "origin")
elseif hitCounter == 2 then
call BlzSpecialEffectAddSubAnimation(udg_ws_vfxs1[unitIndex], SUBANIM_TYPE_TWO)
call BlzPlaySpecialEffect(udg_ws_vfxs1[unitIndex], ANIM_TYPE_STAND)
elseif hitCounter == 3 then
call BlzSpecialEffectRemoveSubAnimation(udg_ws_vfxs1[unitIndex], SUBANIM_TYPE_TWO)
call BlzSpecialEffectAddSubAnimation(udg_ws_vfxs1[unitIndex], SUBANIM_TYPE_THREE)
call BlzPlaySpecialEffect(udg_ws_vfxs1[unitIndex], ANIM_TYPE_STAND)
else
call DestroyEffect(udg_ws_vfxs1[unitIndex])
call ReleaseTimer(udg_ws_timers1[unitIndex])
set udg_ws_timers1[unitIndex] = null
set hitCounter = 0
set udg_ws_vfxs1[unitIndex] = AddSpecialEffectTarget("Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl", Damage.target, "origin")
call BlzPlaySpecialEffect( udg_ws_vfxs1[unitIndex], ANIM_TYPE_BIRTH )
call DestroyEffectLater(udg_ws_vfxs1[unitIndex], 0.75)
set udg_ws_vfxs1[unitIndex] = null
call StunTarget(udg_wave_specific_unit, Damage.target, 1.0, 1.0)
call Damage.applySpell(udg_wave_specific_unit, Damage.target, 0.16 * GetUnitState(Damage.target, UNIT_STATE_LIFE) * udg_difficulty_dmg_factor, DAMAGE_TYPE_LIGHTNING)
endif
set udg_ws_ints1[unitIndex] = hitCounter
return 0.0
endmethod
endstruct
scope Medivh
private struct FireBolt extends Missiles
method onFinish takes nothing returns boolean
return true
endmethod
method onTerrain takes nothing returns boolean
return true
endmethod
method onBoundaries takes nothing returns boolean
return true
endmethod
method onHit takes unit u returns boolean
if u != source and BlzIsUnitSelectable(u) then
call Damage.applySpell(source, u, damage, DAMAGE_TYPE_FIRE)
return true
endif
return false
endmethod
static method fireNewMissile takes real sx, real sy, real tx, real ty returns nothing
local thistype this = thistype.create(sx, sy, 70, tx, ty, 30)
set source = udg_wave_specific_unit
set model = "Units\\Medivh\\Abilities\\FireArrow.mdl"
set speed = 900
set arc = 2
set collision = 22
set damage = 0.25 * GetTotalDamage(udg_wave_specific_unit)
set owner = GetOwningPlayer(udg_wave_specific_unit)
call launch()
endmethod
endstruct
function Medivh__triDirectionalProjectile takes unit target returns nothing
local real usx = GetUnitX(udg_wave_specific_unit)
local real usy = GetUnitY(udg_wave_specific_unit)
local real utx = GetUnitX(target)
local real uty = GetUnitY(target)
local real baseAngle = Atan2(uty - usy, utx - usx)
local real angle = baseAngle + bj_PI / 180.0 * GetRandomReal(-2, 2)
local real tx = usx + 1750 * Cos(angle)
local real ty = usy + 1750 * Sin(angle)
local real sx = usx + 50 * Cos(angle)
local real sy = usy + 50 * Sin(angle)
call SetUnitFacing(udg_wave_specific_unit, baseAngle * 180.0 / bj_PI)
call FireBolt.fireNewMissile(sx, sy, tx, ty)
set angle = baseAngle + bj_PI / 180.0 * (30 + GetRandomReal(-2, 2))
set sx = usx + 50 * Cos(angle)
set sy = usy + 50 * Sin(angle)
set tx = sx + 1750 * Cos(angle)
set ty = sy + 1750 * Sin(angle)
call FireBolt.fireNewMissile(sx, sy, tx, ty)
set angle = baseAngle - bj_PI / 180.0 * (30 + GetRandomReal(-2, 2))
set sx = usx + 50 * Cos(angle)
set sy = usy + 50 * Sin(angle)
set tx = sx + 1750 * Cos(angle)
set ty = sy + 1750 * Sin(angle)
call FireBolt.fireNewMissile(sx, sy, tx, ty)
endfunction
function Medivh__triCircularProjectile takes real circleAngleDeg returns nothing
local real usx = GetUnitX(udg_wave_specific_unit)
local real usy = GetUnitY(udg_wave_specific_unit)
local real angle = bj_PI / 180.0 * (circleAngleDeg + GetRandomReal(-2, 2))
local real tx = usx + 1750 * Cos(angle)
local real ty = usy + 1750 * Sin(angle)
local real sx = usx + 50 * Cos(angle)
local real sy = usy + 50 * Sin(angle)
call SetUnitFacing(udg_wave_specific_unit, circleAngleDeg)
call FireBolt.fireNewMissile(sx, sy, tx, ty)
set angle = bj_PI / 180.0 * (circleAngleDeg + GetRandomReal(-2, 2) + 120)
set sx = usx + 50 * Cos(angle)
set sy = usy + 50 * Sin(angle)
set tx = sx + 1750 * Cos(angle)
set ty = sy + 1750 * Sin(angle)
call FireBolt.fireNewMissile(sx, sy, tx, ty)
set angle = bj_PI / 180.0 * (circleAngleDeg + GetRandomReal(-2, 2) + 240)
set sx = usx + 50 * Cos(angle)
set sy = usy + 50 * Sin(angle)
set tx = sx + 1750 * Cos(angle)
set ty = sy + 1750 * Sin(angle)
call FireBolt.fireNewMissile(sx, sy, tx, ty)
endfunction
endscope
scope MedivhFireArc
private struct FireArc extends Missiles
method onFinish takes nothing returns boolean
local unit u
local group targetsWithinRadius = CreateGroup()
call GroupEnumUnitsInRange(targetsWithinRadius, .x, .y, 200, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if u != source then
if IsUnitInRangeXY(u, x, y, 60) then
call Damage.applySpell(source, u, damage, DAMAGE_TYPE_FIRE)
elseif IsUnitInRangeXY(u, x, y, 120) then
call Damage.applySpell(source, u, 0.4 * damage, DAMAGE_TYPE_FIRE)
elseif IsUnitInRangeXY(u, x, y, 180) then
call Damage.applySpell(source, u, 0.1 * damage, DAMAGE_TYPE_FIRE)
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set targetsWithinRadius = null
return true
endmethod
//Fires a bolt from udg_temp_point to udg_temp_move_point1 - Was previously pure gui, spawning units, etc.
public static method sendIt takes nothing returns nothing
local real x = GetLocationX(udg_temp_point)
local real y = GetLocationY(udg_temp_point)
local real tx = GetLocationX(udg_temp_move_point1)
local real ty = GetLocationY(udg_temp_move_point1)
local thistype this = thistype.create(x, y, 40, tx, ty, 40)
call RemoveLocation(udg_temp_point)
call RemoveLocation(udg_temp_move_point1)
set source = udg_wave_specific_unit
set owner = GetOwningPlayer(udg_wave_specific_unit)
set model = "Units\\Medivh\\Abilities\\Fireball Minor.mdl"
set arc = 42
set speed = 580
set collision = 0
set damage = GetTotalDamage(udg_wave_specific_unit) * 0.5
call launch()
endmethod
endstruct
function MedivhFireArc takes nothing returns nothing
call FireArc.sendIt()
endfunction
endscope
scope MedivhCastleFire
globals
constant integer MEDIVH_CASTLE_FIRES_PER_ROW = 28
constant integer MEDIVH_CASTLE_FIRES_ROWS = 11
constant string MEDIVH_CASTLE_FIRE_SMALL = "Environment\\SmallBuildingFire\\SmallBuildingFire2.mdl" //1.2 scaling
constant string MEDIVH_CASTLE_FIRE_MEDIUM = "Environment\\LargeBuildingFire\\LargeBuildingFire2.mdl" //1.5 for medium, 1.8 for big
constant real MEDIVH_CASTLE_X = -9350.0
constant real MEDIVH_CASTLE_Y = -5500.0
effect array medivh_castle_fire
integer array medivh_castle_fire_status
integer medivh_castle_fire_main_status
integer medivh_castle_fire_row
boolean medivh_castle_fire_force_next_move_teleport
endglobals
function MedivhCastleFireCleanupEffectsRow takes integer row returns nothing
local integer col = 0
loop
call DestroyEffect(medivh_castle_fire[row * MEDIVH_CASTLE_FIRES_ROWS + col])
set col = col + 1
exitwhen col == MEDIVH_CASTLE_FIRES_PER_ROW
endloop
endfunction
function MedivhCastleFireCleanupEffects takes nothing returns nothing
local integer row = 0
loop
call MedivhCastleFireCleanupEffectsRow(row)
set row = row + 1
exitwhen row == MEDIVH_CASTLE_FIRES_ROWS
endloop
endfunction
function MedivhCastleFireDamage takes nothing returns nothing
local group g
local unit u
local real damage = udg_wave_specific_real1 * 0.1 * medivh_castle_fire_main_status * medivh_castle_fire_main_status
if UnitAlive(udg_wave_specific_unit) then
set g = GetUnitsInRectAll(gg_rct_MedivhCastlePart)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if u != udg_wave_specific_unit then
call Damage.applySpell(udg_wave_specific_unit, u, damage, DAMAGE_TYPE_FIRE)
endif
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
set g = null
else
call ReleaseTimer(GetExpiredTimer())
call MedivhCastleFireCleanupEffects()
endif
endfunction
function MedivhCastleFireMakeAllBig takes nothing returns nothing
local integer i = 0
set medivh_castle_fire_main_status = 3
//call BJDebugMsg("CastleFire Finished!")
loop
call BlzSetSpecialEffectScale(medivh_castle_fire[i], 1.8)
set medivh_castle_fire_status[i] = 3
set i = i + 1
exitwhen i == MEDIVH_CASTLE_FIRES_ROWS * MEDIVH_CASTLE_FIRES_PER_ROW
endloop
endfunction
function MedivhCastleFireUpgradeFlames takes nothing returns nothing
local real x
local real y
local integer i = 0
local integer row
local integer col
local integer resultingIndex
local integer totalChanges = 0
local timer t = GetExpiredTimer()
loop
set row = GetRandomInt(IMaxBJ(0, medivh_castle_fire_row - 1), IMinBJ(medivh_castle_fire_row + 1, MEDIVH_CASTLE_FIRES_ROWS - 1))
set col = GetRandomInt(0, MEDIVH_CASTLE_FIRES_PER_ROW - 1)
set resultingIndex = row * MEDIVH_CASTLE_FIRES_PER_ROW + col
if medivh_castle_fire_status[row * MEDIVH_CASTLE_FIRES_ROWS + col] == 1 then
set x = MEDIVH_CASTLE_X + col * 100 + GetRandomReal(-40, 40)
set y = MEDIVH_CASTLE_Y + row * 100 + GetRandomReal(-40, 40)
call DestroyEffect(medivh_castle_fire[resultingIndex])
set medivh_castle_fire[resultingIndex] = AddSpecialEffect(MEDIVH_CASTLE_FIRE_MEDIUM, x, y)
call BlzSetSpecialEffectScale(medivh_castle_fire[resultingIndex], 1.5)
set medivh_castle_fire_status[resultingIndex] = 2
//call BJDebugMsg("!!! Upg i=" + I2S(resultingIndex) + ", status=" + I2S(medivh_castle_fire_status[resultingIndex]))
if medivh_castle_fire_main_status == 1 then
set totalChanges = totalChanges + 1
else
set totalChanges = totalChanges + 10
endif
elseif medivh_castle_fire_main_status == 2 and medivh_castle_fire_status[row * MEDIVH_CASTLE_FIRES_ROWS + col] == 2 then
call BlzSetSpecialEffectScale(medivh_castle_fire[resultingIndex], 1.8)
set medivh_castle_fire_status[resultingIndex] = 3
set totalChanges = totalChanges + 1
endif
set i = i + 1
exitwhen i == 15
endloop
//call BJDebugMsg("Upgrading flames, mainStatus=" + I2S(medivh_castle_fire_main_status) + ", mainRow=" + I2S(medivh_castle_fire_row) + ", changes=" + I2S(totalChanges))
if totalChanges < 8 then
if medivh_castle_fire_row < MEDIVH_CASTLE_FIRES_ROWS - 1 then
set medivh_castle_fire_row = medivh_castle_fire_row + 1 //Extend rows
call TimerStart(t, 0.1, false, function MedivhCastleFireUpgradeFlames)
elseif medivh_castle_fire_main_status == 1 then
//Delay before going from "medium" -> "big"
call TimerStart(t, 4.0, false, function MedivhCastleFireUpgradeFlames)
set medivh_castle_fire_main_status = 2
set medivh_castle_fire_row = 0
else
//Finalize, we are "big" now!
call MedivhCastleFireMakeAllBig()
call ReleaseTimer(t)
endif
else
call TimerStart(t, 0.04, false, function MedivhCastleFireUpgradeFlames)
endif
set t = null
endfunction
function MedivhCastleFireInitRow takes nothing returns nothing
local timer t = GetExpiredTimer()
local real x = MEDIVH_CASTLE_X
local real yBase = MEDIVH_CASTLE_Y + medivh_castle_fire_row * 100.0
local real y
local integer i = 0
local integer resultingIndex
loop
set resultingIndex = medivh_castle_fire_row * MEDIVH_CASTLE_FIRES_PER_ROW + i
set x = MEDIVH_CASTLE_X + i * 100 + GetRandomReal(-40, 40)
set y = yBase + GetRandomReal(-40, 40)
set medivh_castle_fire[resultingIndex] = AddSpecialEffect(MEDIVH_CASTLE_FIRE_SMALL, x, y)
call BlzSetSpecialEffectScale(medivh_castle_fire[resultingIndex], 1.2)
set medivh_castle_fire_status[resultingIndex] = 1
set i = i + 1
exitwhen i == MEDIVH_CASTLE_FIRES_PER_ROW
endloop
set medivh_castle_fire_row = medivh_castle_fire_row + 1
if medivh_castle_fire_row < MEDIVH_CASTLE_FIRES_ROWS - 1 then
call TimerStart(t, 0.09, false, function MedivhCastleFireInitRow)
else
set medivh_castle_fire_main_status = 1
set medivh_castle_fire_row = 0
call TimerStart(NewTimer(), 0.5, true, function MedivhCastleFireDamage)
call TimerStart(t, 4.5, false, function MedivhCastleFireUpgradeFlames)
endif
set t = null
endfunction
function MedivhCastleFireStart takes nothing returns nothing
set medivh_castle_fire_main_status = 0
set medivh_castle_fire_row = 0
set medivh_castle_fire_force_next_move_teleport = true
call TimerStart(NewTimer(), 0.00, false, function MedivhCastleFireInitRow)
endfunction
endscope
//Damage once, knock away in tangent of whirlwind rotation
scope DeathknightWhirlwind
globals
private constant real WHRILDWIND_RADIUS = 166.
private constant integer WHIRLWIND_BUFF = 'B00V'
private constant integer WHIRLWIND_ABILITY = 'S003'
endglobals
function WhirlwindRemove takes unit target, unit source returns nothing
call UnitRemoveAbility(target, WHIRLWIND_ABILITY)
call UnitRemoveAbility(target, WHIRLWIND_BUFF)
endfunction
private struct Whirlwind
//sound sfx
effect vfx
real currentAngle
real x
real y
real damage
integer tickCount
unit source
timer t
// static method applySpell takes unit src, unit tgt, real amt, damagetype dt returns Damage
method cleanup takes nothing returns nothing
//local boolean fade_out_allowed = GetSoundIsPlaying(sfx) // Using fade out on same frame as sound finishes playing seems to crash the game.
//call StopSound(sfx, true, fade_out_allowed)
//set sfx = null
call DestroyEffect(vfx)
set vfx = null
call ReleaseTimer(t)
set t = null
set source = null
endmethod
//source, x, y, damage, radius, damage-type
/*
set udg_Knockback2DUnit = u
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(dy, dx)
set udg_Knockback2DDistance = (250 - distanceToCenter) * 0.66 + 75
set udg_Knockback2DTime = 0.15 + udg_Knockback2DDistance / 650.0
set udg_Knockback2DCollision = 8.0
call TriggerExecute(gg_trg_Knockback_2D)
*/
method dealDamage takes nothing returns nothing
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
local real dx
local real dy
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, WHRILDWIND_RADIUS + 64., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if GetUnitAbilityLevel(u, WHIRLWIND_BUFF) == 0 and UnitAlive(u) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitInRangeXY(u, x, y, WHRILDWIND_RADIUS) then
//Slow
call UnitAddAbility(u, WHIRLWIND_ABILITY)
call DelayedUnitAction.setup(u, source, UnitAction.WhirlwindRemove, 1.20)
//knockback into the whirlwind
set dx = x - GetUnitX(u)
set dy = y - GetUnitY(u)
set udg_Knockback2DUnit = u
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(dy, dx)
set udg_Knockback2DDistance = 425.
set udg_Knockback2DTime = 0.60
set udg_Knockback2DCollision = 8.0
call TriggerExecute(gg_trg_Knockback_2D)
//Damage
if IsUnitAlly(u, owner) then
if GetUnitState(u, UNIT_STATE_LIFE) < 200.0 then
call Damage.applySpell(source, u, damage * 0.20, DAMAGE_TYPE_DEATH)
else
call Damage.applySpell(source, u, damage * 0.40, DAMAGE_TYPE_DEATH)
endif
else
call Damage.applySpell(source, u, damage, DAMAGE_TYPE_DEATH)
endif
endif
call GroupRemoveUnit(targetsWithinRadius, u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endmethod
method findNewTarget takes nothing returns nothing
local group targetsWithinRadius = CreateGroup()
local unit u
local unit closestHero = null
local real closestDistance = 9999.
local real distanceForUnit
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 1000., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and IsUnitType(u, UNIT_TYPE_HERO) then
if closestHero == null then
set closestHero = u
set closestDistance = distanceBetweenXYXY(x, y, GetUnitX(u), GetUnitY(u))
elseif distanceBetweenXYXY(x, y, GetUnitX(u), GetUnitY(u)) < closestDistance then
set closestHero = u
set closestDistance = distanceBetweenXYXY(x, y, GetUnitX(u), GetUnitY(u))
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set targetsWithinRadius = null
if closestHero != null then
set currentAngle = Atan2(GetUnitY(closestHero) - y, GetUnitX(closestHero) - x)
set closestHero = null
endif
endmethod
method move takes nothing returns nothing
if (tickCount / 160) * 160 == tickCount then
call findNewTarget()
endif
set .x = .x + 9.5 * Cos(currentAngle)
set .y = .y + 9.5 * Sin(currentAngle)
call BlzSetSpecialEffectPosition(.vfx, .x, .y, 16.)
//call SetSoundPosition(sfx, x, y, 0 )
endmethod
static method tick takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if UnitAlive(source) then
set tickCount = tickCount + 1
call dealDamage()
call move()
else
call cleanup()
endif
endmethod
static method spawnWhirlwind takes unit source, unit firstTarget returns nothing
local real sx = GetUnitX(source)
local real sy = GetUnitY(source)
local real tx = GetUnitX(firstTarget)
local real ty = GetUnitY(firstTarget)
local thistype this = thistype.allocate()
set .source = source
set t = NewTimerEx(this)
set damage = GetTotalDamage(source) * 2.75
set tickCount = 0
set currentAngle = Atan2(ty - sy, tx - sx)
set x = tx - 725. * Cos(currentAngle)
set y = ty - 725. * Sin(currentAngle)
set vfx = AddSpecialEffect("Abilities\\Spells\\Other\\Tornado\\TornadoElemental.mdl", x, y)
call TimerStart(t, 0.04, true, function thistype.tick)
endmethod
endstruct
function castWhirlwind takes unit source, unit target returns nothing
call Whirlwind.spawnWhirlwind(source, target)
endfunction
endscope
//Slash
function ALSlash_Heal_BaseAmount takes integer levelOfSlash returns integer
return 16 + 4 * levelOfSlash
endfunction
function ALSlash_Heal_PercentMissingHP takes integer levelOfSlash returns real
return 0.017 + 0.003 * levelOfSlash
endfunction
function ALSlash_Heal_ScalingPercent takes integer levelOfSlash returns real
return 0.10 + 0.02 * levelOfSlash
endfunction
function ALSlash_Heal_HealAmount takes nothing returns real
local integer levelOfSlash = GetUnitAbilityLevel(udg_al_hero, 'A05I')
return ALSlash_Heal_BaseAmount(levelOfSlash) + ALSlash_Heal_ScalingPercent(levelOfSlash) * GetSpellPower(udg_al_hero)
endfunction
//---------- Charge -----------
globals
constant integer AL_CHARGE_ABILITY = 'A051'
constant real AL_CHARGE_DURATION_SPELLPOWER = 75.0
endglobals
function ALChargeUtils_BuffDuration_Base takes nothing returns real
return 2.5 + 0.5 * GetUnitAbilityLevel(udg_al_hero, AL_CHARGE_ABILITY)
endfunction
function ALChargeUtils_BuffDuration_Scaling takes nothing returns real
return GetSpellPower(udg_al_hero) / AL_CHARGE_DURATION_SPELLPOWER
endfunction
function ALChargeUtils_BuffDuration takes nothing returns real
return ALChargeUtils_BuffDuration_Base() + ALChargeUtils_BuffDuration_Scaling()
endfunction
function ALChargeUtils_StunTime takes nothing returns real
return 1.25 + 0.25 * GetUnitAbilityLevel(udg_al_hero, AL_CHARGE_ABILITY)
endfunction
//---------- Offensive Stance -----------
function ALOffensive_Crit_Base takes integer levelOfAbility returns real
return 0.04 + 0.01 * levelOfAbility
endfunction
function ALOffensive_Crit_ScalingStrength_PerX takes integer levelOfAbility returns integer
return 8
endfunction
function ALOffensive_Crit_ScalingStr_Percent takes integer levelOfAbility returns real
return 0.01 / ALOffensive_Crit_ScalingStrength_PerX(levelOfAbility)
endfunction
function ALOffensive_Crit_ScalingSpellPower_PerX takes integer levelOfAbility returns integer
return 26 - 2 * levelOfAbility
endfunction
function ALOffensive_Crit_ScalingSpellPower takes integer levelOfAbility returns real
return 0.01 / ALOffensive_Crit_ScalingSpellPower_PerX(levelOfAbility)
endfunction
function ALOffensive_Damage_Per_Agility takes integer levelOfAbility returns real
return 0.5 + 0.15 * levelOfAbility
endfunction
function ALOffensive_Damage_base takes integer levelOfAbility returns real
return 16.0 + 4.0 * levelOfAbility
endfunction
function Trig_ALChangeStanceCast_Conditions takes nothing returns boolean
if ( GetTriggerUnit() == udg_al_hero ) then
set bj_forLoopAIndex = 0
set bj_forLoopAIndexEnd = 2
loop
exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
// Second Condition = Sanity check, since orders are repeated when transforming into a unit (i.e. ignore those duplicate orders).
if ( GetIssuedOrderId() == udg_al_stance_order_ids[bj_forLoopAIndex]) and bj_forLoopAIndex != udg_al_stance_index then
return true
endif
set bj_forLoopAIndex = bj_forLoopAIndex + 1
endloop
endif
return false
endfunction
//
// Changes Lothar's stance.
//
function Trig_ALChangeStanceCast_Change_Stances takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer timer_handle = GetHandleId(t)
local player current_player = LoadPlayerHandle(udg_timer_hashtable, timer_handle, 0)
local integer stance_index = LoadInteger(udg_timer_hashtable, timer_handle, 1)
local unit current_unit = LoadUnitHandle(udg_timer_hashtable, timer_handle, 2)
local integer stance_ability_id = udg_al_stance_change[stance_index]
// SWITCH TO STANCE
call SetPlayerAbilityAvailableBJ(true, stance_ability_id, current_player)
call IssueImmediateOrderBJ(current_unit, "bearform" )
call SetPlayerAbilityAvailableBJ(false, stance_ability_id, current_player)
set udg_al_stance_index = stance_index
call EnableTrigger(gg_trg_ALChangeStanceCast) // Whole stance change is now done - thus safe to trigger new stance changes.
call ForceUICancelBJ(current_player) // Closes the Change Stance - Spellbook menu
call ReleaseTimer(t)
call FlushChildHashtable(udg_timer_hashtable, timer_handle)
set t = null
set current_player = null
set current_unit = null
endfunction
//
// Will change stance for Lothar by first switching back to the default (offensive)
// and then to selected stance (if needed).
//
function Trig_ALChangeStanceCast_Actions takes nothing returns nothing
local unit current_unit = GetTriggerUnit()
local player current_player = GetTriggerPlayer()
local integer new_stance_index = 0
local timer change_stance_timer = NewTimer()
local integer timer_handle = GetHandleId(change_stance_timer)
local integer reset_ability_id = 0
local integer order_id = GetIssuedOrderId()
call IssueImmediateOrder (current_unit, "stop")
call FireHeroAbilityEvents(current_unit, AE_AL_STANCE_CHANGED)
// SWITCH BACK TO DEFAULT Lothar (Offensive)
if ( udg_al_stance_index > 0 ) then
set reset_ability_id = udg_al_stance_change[udg_al_stance_index]
call SetPlayerAbilityAvailableBJ(true, reset_ability_id, current_player)
call IssueImmediateOrderBJ(current_unit, "unbearform" )
call SetPlayerAbilityAvailableBJ(false, reset_ability_id, current_player)
endif
call SetPlayerAbilityAvailableBJ(true, udg_al_stance_used_abilities[udg_al_stance_index], current_player)
if ( order_id == udg_al_stance_order_ids[0] ) then
set new_stance_index = 0 // offensive
elseif ( order_id == udg_al_stance_order_ids[1] ) then
set new_stance_index = 1 // defensive
else
set new_stance_index = 2 // tactical
endif
call SetPlayerAbilityAvailableBJ(false, udg_al_stance_used_abilities[new_stance_index], current_player)
set udg_temp_string = "currentStance=" + I2S(udg_al_stance_index) + " nextStance=" + I2S(new_stance_index) + " orderid="+ I2S(order_id)
call DebugPrint(udg_temp_string, "ALDebug", null, true)
// CHANGE STANCE IF NEEDED (WE NEED TO WAIT A SMALL AMOUNT OF TIME IN ORDER FOR THE TRANSFORM TO WORK)
if ( new_stance_index > 0 ) then
call SavePlayerHandle(udg_timer_hashtable, timer_handle, 0, current_player)
call SaveInteger(udg_timer_hashtable, timer_handle, 1, new_stance_index)
call SaveUnitHandle(udg_timer_hashtable, timer_handle, 2, current_unit)
call TimerStart(change_stance_timer, 0.05, false, function Trig_ALChangeStanceCast_Change_Stances)
call DisableTrigger(gg_trg_ALChangeStanceCast) // The same order is issued to the Default stance Lothar (probably because transform happens during the same frame as order is issued),
// so disable trigger until we are done with the whole stance change.
else
set udg_al_stance_index = new_stance_index
call ReleaseTimer(change_stance_timer)
call ForceUICancelBJ(current_player) // Closes the Change Stance - Spellbook menu
endif
set current_unit = null
set current_player = null
set change_stance_timer = null
endfunction
//===========================================================================
function InitTrig_ALChangeStanceCast takes nothing returns nothing
set gg_trg_ALChangeStanceCast = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_ALChangeStanceCast, EVENT_PLAYER_UNIT_ISSUED_ORDER )
call TriggerAddCondition( gg_trg_ALChangeStanceCast, Condition( function Trig_ALChangeStanceCast_Conditions ) )
call TriggerAddAction( gg_trg_ALChangeStanceCast, function Trig_ALChangeStanceCast_Actions )
endfunction
globals
constant integer ALLEADERSHIP_ABILITY = 'A06O'
constant integer ALLEADERSHIP_CAPTAIN_UNIT_ID = 'h01A'
integer al_leadership_units_allowed = 1 //base is 1 (for captain)
real al_leadership_captain_bonus = 1.40
real al_leadership_cooldown_base = 26.0
real al_leadership_cooldown_captain = 20.0
real al_leadership_scaling_hp_bonus = 0.0
real al_leadership_scaling_dmg_bonus = 0.0
real al_leadership_flat_hp_bonus = 0.0
real al_leadership_flat_dmg_bonus = 0.0
endglobals
//return true if a captain is in unit group, false otherwise.
function ALLeadership_HasCaptain takes nothing returns boolean
local group g= CreateGroup()
local unit picked
local boolean hasCaptain = false
call GroupAddGroup( udg_al_leadership_units , g)
loop
set picked=FirstOfGroup(g)
exitwhen picked==null
set hasCaptain=( GetUnitTypeId(picked) == ALLEADERSHIP_CAPTAIN_UNIT_ID ) //h01A is captain unit-id
exitwhen hasCaptain==true
call GroupRemoveUnit(g,picked)
endloop
call DestroyGroup(g)
set g=null
set picked=null
return hasCaptain
endfunction
//returns total number of units (including the captain) that are allowed to for the given level of the ability
function ALLeadership_GetUnitsAllowed takes nothing returns integer
local integer levelOfLeadership = GetUnitAbilityLevel(udg_al_hero, ALCHANGE_STANCE_ABILITY)
return (levelOfLeadership + 1) / 2 + al_leadership_units_allowed
endfunction
//Returns true if more units can be spawned
function ALLeadership_CanSpawnMoreUnits takes nothing returns boolean
return not(ALLeadership_HasCaptain()) or CountUnitsInGroup(udg_al_leadership_units) < ALLeadership_GetUnitsAllowed()
endfunction
//Returns true if the next spawned unit should be a captain, false otherwise
function ALLeadership_ShouldSpawnCaptain takes nothing returns boolean
return not(ALLeadership_HasCaptain()) and CountUnitsInGroup(udg_al_leadership_units) >= ALLeadership_GetUnitsAllowed() - 1
endfunction
//CalculateAbilityCooldown takes unit u, integer ability-id, integer level, real cooldown
function ALLeadership_UpdateCooldown takes nothing returns nothing
if ALLeadership_HasCaptain() then
call CalculateAbilityCooldown(udg_al_hero, ALLEADERSHIP_ABILITY, GetUnitAbilityLevel(udg_al_hero, ALCHANGE_STANCE_ABILITY), al_leadership_cooldown_captain)
else
call CalculateAbilityCooldown(udg_al_hero, ALLEADERSHIP_ABILITY, GetUnitAbilityLevel(udg_al_hero, ALCHANGE_STANCE_ABILITY), al_leadership_cooldown_base)
endif
endfunction
function ALLeadership_Scaling_SpellPower_PerX takes integer levelOfLeadership returns real
return 26.0 - 2.0 * levelOfLeadership
endfunction
function ALLeadership_Scaling_SpellPower takes integer levelOfLeadership returns real
return 0.01 / ALLeadership_Scaling_SpellPower_PerX(levelOfLeadership)
endfunction
function ALLeadership_Scaling_Hp takes integer levelOfLeadership returns real
return 0.13 + 0.02 * levelOfLeadership + al_leadership_scaling_hp_bonus
endfunction
function ALLeadership_Scaling_Damage takes integer levelOfLeadership returns real
return 0.09 + 0.02 * levelOfLeadership + al_leadership_scaling_dmg_bonus
endfunction
function ALLeadership_Scaling_Armor takes integer levelOfLeadership returns real
return 0.15 + 0.02 * levelOfLeadership
endfunction
function ALCavalrySupportCreateVfxFromReferencePoint takes integer index, location referencePoint, real offsetAngle returns nothing
local location walkablePoint = Location(GetLocationX(referencePoint), GetLocationY(referencePoint))
local integer i = 1
local effect tempEffect
loop
exitwhen i > 9
set udg_CP_Point = PolarProjectionBJ(referencePoint, ( -10.00 * I2R(i) ), ( udg_al_cavalry_direction + offsetAngle ))
call TriggerExecute( gg_trg_Check_Walkability )
if ( udg_CP_PointIsWalkable == true ) then
call RemoveLocation(walkablePoint)
set walkablePoint = udg_CP_Point
else
call RemoveLocation(udg_CP_Point)
set udg_CP_Point = PolarProjectionBJ(referencePoint, ( -10.00 * I2R(i) ), ( udg_al_cavalry_direction + 2 * offsetAngle ))
call TriggerExecute( gg_trg_Check_Walkability )
if ( udg_CP_PointIsWalkable == true ) then
call RemoveLocation(walkablePoint)
set walkablePoint = udg_CP_Point
else
call RemoveLocation(udg_CP_Point)
set udg_CP_Point = PolarProjectionBJ(referencePoint, ( -10.00 * I2R(i) ), ( udg_al_cavalry_direction + 3 * offsetAngle ))
call TriggerExecute( gg_trg_Check_Walkability )
if ( udg_CP_PointIsWalkable == true ) then
call RemoveLocation(walkablePoint)
set walkablePoint = udg_CP_Point
else
call RemoveLocation(udg_CP_Point)
exitwhen true
endif
endif
endif
set i = i + 1
endloop
set udg_al_cavalry_points[index] = walkablePoint
set tempEffect = AddSpecialEffectLoc( "Units\\Lothar\\Footman\\WC2_Footman.mdx", walkablePoint )
call BlzSpecialEffectAddSubAnimation( tempEffect, SUBANIM_TYPE_DEFEND )
call BlzPlaySpecialEffect( tempEffect, ANIM_TYPE_WALK )
call BlzSetSpecialEffectOrientation( tempEffect, Deg2Rad(udg_al_cavalry_direction), 0.00, 0.0 )
call BlzSetSpecialEffectColorByPlayer( tempEffect, GetOwningPlayer(GetTriggerUnit()) )
call BlzSetSpecialEffectAlpha( tempEffect, 0 )
call BlzSetSpecialEffectScale( tempEffect, 1.05 )
set udg_al_cavalry_knights_fx[index] = tempEffect
set walkablePoint = null
set referencePoint = null
set tempEffect = null
endfunction
function ALUltiSlowMovement takes integer abilityLevel returns real
return 0.45 + 0.15 * abilityLevel
endfunction
function ALUltiSlowAttack takes integer abilityLevel returns real
return 0.20 + 0.20 * abilityLevel
endfunction
function ALUltiSlowDurBase takes integer abilityLevel returns real
return 5.0
endfunction
function ALUltiSlowDurHeroBase takes integer abilityLevel returns real
return 2.5
endfunction
function ALUltiSlowDur takes integer abilityLevel returns real
if udg_al_stance_index == 1 then
return 2.0 * ALUltiSlowDurBase(abilityLevel)
else
return ALUltiSlowDurBase(abilityLevel)
endif
endfunction
function ALUltiSlowDurHero takes integer abilityLevel returns real
if udg_al_stance_index == 1 then
return 2.0 * ALUltiSlowDurHeroBase(abilityLevel)
else
return ALUltiSlowDurHeroBase(abilityLevel)
endif
endfunction
function DVForceLightningBaseHealAmount takes integer level returns real
return 30.0 + 10.0 * level
endfunction
function SaChargeBeamText takes nothing returns string
local integer levelOfAbility = GetUnitAbilityLevelSwapped('A06P', udg_samus_hero)
local string chargeIndicator = "...................."
local string indicatorText = "|c003C14DC" + SubString(chargeIndicator, 0, udg_sa_charge_beam_tick + 1) + "|r"
local string requiredIndicator = "|c00FF4840....."
if udg_sa_charge_beam_tick < 5 then
return indicatorText + SubString(requiredIndicator,0, 15 - udg_sa_charge_beam_tick)+ "|r" + SubString(chargeIndicator,0, 5+2*levelOfAbility)
else
return indicatorText + SubString(chargeIndicator,0, 10+2*levelOfAbility - udg_sa_charge_beam_tick)
endif
endfunction
function SAChargeBeam_GetTotalRange takes nothing returns real
return 700.0 + 50.0 * udg_sa_charge_beam_tick
endfunction
function SAChargeBeam_GetTotalAoE takes nothing returns real
return 100.0 + 10.0 * udg_sa_charge_beam_tick
endfunction
function SAChargeBeam_Pause takes nothing returns nothing
call ReleaseTimer(GetExpiredTimer())
call PauseUnit(udg_samus_hero, true)
endfunction
function SAChargeBeam_Unpause takes nothing returns nothing
call ReleaseTimer(GetExpiredTimer())
call PauseUnit(udg_samus_hero, false)
endfunction
scope Samus
globals
boolean sa_missile_friendly_fire = true
real sa_missile_explosion_radius = 150.
real sa_missile_empower_distance = 900.
real sa_missile_empowerd_crit_damage = 0.5 //50% of crit damage multiplier, default to 50% of 50% -> +25% damage
endglobals
function SAMissileDamage takes nothing returns real
call Trig_SAMissileSetDmgCalculations_Actions()
return udg_temp_real6
endfunction
function SAMissileEmpowerPercent takes nothing returns real
return GetCriticalHitDamageInclAgi(udg_samus_hero) * sa_missile_empowerd_crit_damage
endfunction
private struct SAMissile extends Missiles
boolean isEmpowered
method onPeriod takes nothing returns boolean
if isEmpowered == false and travel > sa_missile_empower_distance then
set isEmpowered = true
//Note: attached effect is cleaned up properly when this missile is destroyed
call attach("Vfx\\Firebolt Minor.mdx", 0, 0, 0, 1.4)
call FireHeroAbilityEvents(udg_samus_hero, AE_SA_MISSILE_EMPOWER)
endif
return false
endmethod
method explode takes nothing returns nothing
local real damageModifier = 1.0
if isEmpowered then
set damageModifier = 1.0 + SAMissileEmpowerPercent()
endif
if sa_missile_friendly_fire then
call damageAoENoFriendlyBuildingDamage(source, x, y, damageModifier * damage, sa_missile_explosion_radius, DAMAGE_TYPE_FIRE)
else
call damageAoENoFF(source, x, y, damageModifier * damage, sa_missile_explosion_radius, DAMAGE_TYPE_FIRE)
endif
endmethod
method onFinish takes nothing returns boolean
call explode()
return true
endmethod
method onHit takes unit u returns boolean
if sa_missile_friendly_fire then
if u != source and BlzIsUnitSelectable(u) and not BlzIsUnitInvulnerable(u) and not (IsUnitAlly(u, owner) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
call explode()
return true
endif
else
if u != source and BlzIsUnitSelectable(u) and not BlzIsUnitInvulnerable(u) and not IsUnitAlly(u, owner) then
call explode()
return true
endif
endif
return false
endmethod
static method fireMissile takes unit c, real x, real y, real angle returns nothing
local real z = GetUnitFlyHeight(c) + 70.0
local real tx = x + 2250. * Cos(angle)
local real ty = y + 2250. * Sin(angle)
local thistype this = thistype.create(x, y, z, tx, ty, GetZHeight(tx, ty) + 50.)
call FireHeroAbilityEvents(udg_samus_hero, AE_SA_MISSILE)
set source = c
set model = "Units\\Samus\\Abilities\\DO_RocketMissile.mdl"
set speed = 1100.
set collision = 32.
set damage = SAMissileDamage()
set owner = GetOwningPlayer(c)
set isEmpowered = false
call launch()
set c = null
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real angle = Atan2(GetSpellTargetY() - y, GetSpellTargetX() - x)
call fireMissile(GetTriggerUnit(), x, y, angle)
set c = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A05T', function thistype.onCast)
endmethod
endstruct
function SamusFireMissile takes unit caster, real angle returns nothing
call SAMissile.fireMissile(caster, GetUnitX(caster), GetUnitY(caster), angle)
endfunction
endscope
function SABombExplode takes real x, real y, real angle_unused returns nothing
set udg_x = x
set udg_y = y
call TriggerExecute(gg_trg_SABombExplodeFinal)
endfunction
struct MasterChiefBullets extends Missiles
boolean friendlyFire
method onFinish takes nothing returns boolean
return true
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
*/
method onHit takes unit hit returns boolean
if BlzIsUnitSelectable(hit) and UnitAlive(hit) and (friendlyFire or not IsUnitAlly(hit, owner)) then
//set udg_NextDamageType = udg_DamageTypeCode
//call UnitDamageTarget(source, hit, damage, true, true, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null)
call Damage.applyAttack(source, hit, damage, true, ATTACK_TYPE_HERO, WEAPON_TYPE_WHOKNOWS)
return true
endif
return false
endmethod
static method fireBullet takes unit mc, unit target, real baseDamage, real spread, real distance, real speed, boolean friendlyFire returns nothing
local unit c = mc
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real z = 105
local real angle = Atan2(GetUnitY(target) - y, GetUnitX(target) - x) + GetRandomReal(-spread, spread)
local real tx = x + distance * Cos(angle)
local real ty = y + distance * Sin(angle)
local thistype this = thistype.create(x + 48 * Cos(angle), y + 48 * Sin(angle), z, tx, ty, GetZHeight(tx, ty) + 64)
set .source = c
set .model = "Units\\MasterChief\\Bullet.mdx"
set .speed = speed
set .arc = 0.5
set .collision = 40
set .damage = baseDamage
set .owner = GetOwningPlayer(c)
set .friendlyFire = friendlyFire
call launch()
endmethod
endstruct
scope MCWeapons
globals
constant string MC_SWITCH_TO_ORDER = "bearform"
constant string MC_SWITCH_FROM_ORDER = "unbearform"
constant integer MC_ROULETTE_ABILITY = 'A00K'
constant integer MC_SWAP_ABILITY = 'A016'
constant integer MC_FRESH_WEAPON_ABILITY = 'A02M' //Talent
constant integer MC_FRESH_WEAPON_BUFF = 'B01O' //Talent
endglobals
private keyword verifySwitchOrTryAgain
private struct SwitchWeapon
static unit caster
static integer newWeaponIndex
static integer unitTypeIdBefore
static timer freshWeaponTimer
static method WeaponIcon takes integer weaponIndex returns string
if weaponIndex == -1 then
return "ReplaceableTextures\\CommandButtons\\BTNMagnum.blp"
elseif weaponIndex == 0 then
return "ReplaceableTextures\\CommandButtons\\BTNsmg.blp"
elseif weaponIndex == 1 then
return "ReplaceableTextures\\CommandButtons\\BTNAssualtrifle.blp"
elseif weaponIndex == 2 then
return "ReplaceableTextures\\CommandButtons\\BTNShotgun.blp"
elseif weaponIndex == 3 then
return "ReplaceableTextures\\CommandButtons\\BTNSniper.blp"
elseif weaponIndex == 4 then
return "ReplaceableTextures\\CommandButtons\\BTNRocketlauncer.blp"
endif
return "Invalid WeaponName - Index: " + I2S(weaponIndex)
endmethod
static method WeaponName takes integer weaponIndex returns string
if weaponIndex == -1 then
return "Magnum"
elseif weaponIndex == 0 then
return "SMG"
elseif weaponIndex == 1 then
return "Assault Rifle"
elseif weaponIndex == 2 then
return "Shotgun"
elseif weaponIndex == 3 then
return "Sniper"
elseif weaponIndex == 4 then
return "Rocket Launcher"
endif
return "None"
endmethod
static method WeaponDescription takes integer weaponIndex returns string
if weaponIndex == -1 then
return "A basic attack. Nothing special.|nMedium/long range, moderate attack speed."
elseif weaponIndex == 0 then
return "Extreme attack speed, but attacks fires an inacurate bullet that deals |c00FF780040% of regular damage|r.|nBullets are fired in a 45 degrees arc towards the target, hitting the first enemy it comes into contact with."
elseif weaponIndex == 1 then
return "High base attack speed, but slightly inaccurate.|nAttack fires a bullet dealing |c00FF780090% of regular damage|r, going in a 10 degree arc towards the target, hitting first enemy it comes into contact with."
elseif weaponIndex == 2 then
return "Low base attack speed, moderate range, fires 7 bullets in a 60 degree arc towards the target.|nEach bullet deals |c00FF780036% of regular damage|r for a total of |c00FF7800252% of regular damage|r if all bullets hit."
elseif weaponIndex == 3 then
return "Low base attack speed, extreme range.|nThe attack hit and pierce the target and enemies in a short line behind it.|nOutside 500 range: |c00FF7800+40% damage|r|nOutside 1000 range: |c00FF7800+100% damage|r"
elseif weaponIndex == 4 then
return "Low base attack speed, big splash damage area, deals |c00FF7800+50% damage|r but has 150 minimum attack range!|n|n|c00888888Note that many on-attack-hit items only effects the primary target."
endif
return "Invalid WeaponDescription - Index: " + I2S(weaponIndex)
endmethod
static method UpdateIconAndDescription takes nothing returns nothing
local string abilityText = "|cffffcc00Active|r: Use to swap to your secondary weapon.|n|n|cffffcc00Current Weapon|r: " + WeaponName(udg_mc_current_weapon_index) + "|n" + WeaponDescription(udg_mc_current_weapon_index)
set abilityText = abilityText + "|n|n|cffffcc00Secondary Weapon|r: " + WeaponName(udg_mc_backup_weapon_index)
call BlzSetAbilityExtendedTooltip( MC_SWAP_ABILITY, abilityText, 0 )
call BlzSetAbilityIcon(MC_SWAP_ABILITY, WeaponIcon(udg_mc_current_weapon_index))
endmethod
static method RemoveFreshWeapon takes nothing returns nothing
call ReleaseTimer(freshWeaponTimer)
call UnitRemoveAbility(udg_mc_hero, MC_FRESH_WEAPON_ABILITY)
call UnitRemoveAbility(udg_mc_hero, MC_FRESH_WEAPON_BUFF)
set freshWeaponTimer = null
endmethod
static method ClearSwitchFreshWeaponCooldown takes nothing returns nothing
call ReleaseTimer(GetExpiredTimer())
set udg_mc_fresh_weapon_talent = BlzBitAnd(udg_mc_fresh_weapon_talent, -5) //Clear 3rd bit
endmethod
// function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
static method FreshWeaponDelayed takes nothing returns nothing
call UnitAddAbility(udg_mc_hero, MC_FRESH_WEAPON_ABILITY)
if BlzBitAnd(udg_mc_fresh_weapon_talent, 2) > 0 then
call LinkBonusToBuff(udg_mc_hero, BONUS_CRITICAL_CHANCE, 0.14, MC_FRESH_WEAPON_BUFF)
call TimerStart(freshWeaponTimer, 14., false, function thistype.RemoveFreshWeapon)
set udg_mc_fresh_weapon_talent = BlzBitAnd(udg_mc_fresh_weapon_talent, -3) //Clear 2nd bit
else
call LinkBonusToBuff(udg_mc_hero, BONUS_CRITICAL_CHANCE, 0.07, MC_FRESH_WEAPON_BUFF)
call TimerStart(freshWeaponTimer, 14., false, function thistype.RemoveFreshWeapon)
call TimerStart(NewTimer(), 50., false, function thistype.ClearSwitchFreshWeaponCooldown)
endif
endmethod
/**
Weapon Bit Arrays are:
43210(-1) <- Weapon Index
111111 <- Weapon bit-map
|||||V
||||VPistol
|||VSMG
||VAssault Rifle
|VShotgun
VSniper
Rocket Launcher
**/
static method IndexToBit takes integer weaponIndex returns integer
local integer i = -1
local integer bit = 1
loop
if i == weaponIndex then
return bit
endif
set i = i + 1
set bit = bit * 2
exitwhen i > 4
endloop
return -1 //This should never happen!
endmethod
static method MakeWeaponsUnAvailable takes integer weaponIndex returns nothing
local integer weaponBit = IndexToBit(weaponIndex)
local integer weaponBitNotSet = BlzBitXor(-1, weaponBit) //-1 is "all bits is set"
set udg_mc_available_weapon_bit_array = BlzBitAnd(udg_mc_available_weapon_bit_array, weaponBitNotSet)
endmethod
static method RefreshAvailableWeapons takes nothing returns nothing
local integer max_weapon_index = GetUnitAbilityLevel(udg_mc_hero, MC_ROULETTE_ABILITY) - 1
local integer i = -1
local integer bit = 1
set udg_mc_available_weapon_bit_array = 0
loop
set udg_mc_available_weapon_bit_array = BlzBitOr(udg_mc_available_weapon_bit_array, bit)
exitwhen i == max_weapon_index
set i = i + 1
set bit = bit * 2
endloop
call MakeWeaponsUnAvailable(udg_mc_current_weapon_index)
call MakeWeaponsUnAvailable(udg_mc_backup_weapon_index)
endmethod
static method isWeaponsAvailable takes integer weaponIndex returns boolean
return BlzBitAnd(udg_mc_available_weapon_bit_array, IndexToBit(weaponIndex)) > 0
endmethod
static method changeToBase takes unit u, integer oldWeaponIndex returns nothing
local integer weapon_ability_id = udg_mc_weapons_abilities[oldWeaponIndex]
call SetPlayerAbilityAvailable(GetOwningPlayer(u), weapon_ability_id, true)
call IssueImmediateOrder(u, MC_SWITCH_FROM_ORDER )
call SetPlayerAbilityAvailable(GetOwningPlayer(u), weapon_ability_id, false)
endmethod
static method changeToWeapon takes unit u, integer newWeapon returns nothing
local integer weapon_ability_id = udg_mc_weapons_abilities[newWeapon]
call SetPlayerAbilityAvailable(GetOwningPlayer(u), weapon_ability_id, true)
call IssueImmediateOrder(u, MC_SWITCH_TO_ORDER )
call SetPlayerAbilityAvailable(GetOwningPlayer(u), weapon_ability_id, false)
endmethod
static method changeWeaponAfterDelay takes nothing returns nothing
local timer t = GetExpiredTimer()
//local thistype this = GetTimerData(t)
set unitTypeIdBefore = GetUnitTypeId(.caster)
call changeToWeapon(.caster, .newWeaponIndex)
//Note, at this line of code, "GetUnitTypeId(.caster) == unitTypeIdBefore" is true, it takes one tick to update
call TimerStart(t, 0.02, false, function thistype.verifySwitchOrTryAgain)
set t = null
endmethod
static method verifySwitchOrTryAgain takes nothing returns nothing
local timer t = GetExpiredTimer()
//call BJDebugMsg("Verify - Before: " + I2S(unitTypeIdBefore) + ", Now: " + I2S(GetUnitTypeId(.caster)))
if .unitTypeIdBefore == GetUnitTypeId(.caster) then
call TimerStart(t, 0.02, false, function thistype.changeWeaponAfterDelay)
else
call ReleaseTimer(t)
endif
set t = null
endmethod
static method changeWeaponDelayed takes unit u, integer newWeaponIndex returns nothing
//local thistype this = thistype.allocate()
local timer t = NewTimer()
set .caster = u
set .newWeaponIndex = newWeaponIndex
call TimerStart(t, 0.02, false, function thistype.changeWeaponAfterDelay)
set t = null
endmethod
static method changeWeapon takes unit u, integer newWeaponIndex returns nothing
//call BJDebugMsg("UnitType Before: " + I2S(GetUnitTypeId(u)))
if udg_mc_current_weapon_index > -1 then
call changeToBase(u, udg_mc_current_weapon_index)
endif
//call BJDebugMsg("Changing Weapon from: " + I2S(udg_mc_current_weapon_index) + " to " + I2S(newWeaponIndex))
if newWeaponIndex > -1 then
call changeWeaponDelayed(u, newWeaponIndex)
else
set udg_mc_current_weapon_index = newWeaponIndex
endif
//call BJDebugMsg("UnitType After: " + I2S(GetUnitTypeId(u)))
endmethod
static method castRoulette takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, MC_ROULETTE_ABILITY)
local integer newWeaponIndex
if udg_mc_fresh_weapon_talent > 0 then
call UnitRemoveAbility(udg_mc_hero, MC_FRESH_WEAPON_ABILITY) //Remove current bonus, if any
call UnitRemoveAbility(udg_mc_hero, MC_FRESH_WEAPON_BUFF) //Remove current bonus, if any
set udg_mc_fresh_weapon_talent = BlzBitOr(udg_mc_fresh_weapon_talent, 2) //Set 2nd bit, indicating newly rouletted weapon
if freshWeaponTimer != null then
call PauseTimer(freshWeaponTimer)
else
set freshWeaponTimer = NewTimer()
endif
call TimerStart(freshWeaponTimer, 0.0625, false, function thistype.FreshWeaponDelayed)
endif
if udg_mc_available_weapon_bit_array == 0 then
call RefreshAvailableWeapons()
endif
loop
set newWeaponIndex = ( GetRandomInt(0, level) - 1 )
exitwhen newWeaponIndex != udg_mc_current_weapon_index and isWeaponsAvailable(newWeaponIndex)
endloop
call MakeWeaponsUnAvailable(newWeaponIndex)
call changeWeapon(u, newWeaponIndex)
set udg_mc_current_weapon_index = newWeaponIndex
call UpdateIconAndDescription()
set u = null
endmethod
static method castSwitch takes nothing returns nothing
local integer oldPrimary = udg_mc_current_weapon_index
if udg_mc_fresh_weapon_talent > 0 then
call UnitRemoveAbility(udg_mc_hero, MC_FRESH_WEAPON_ABILITY) //Remove current bonus, if any
call UnitRemoveAbility(udg_mc_hero, MC_FRESH_WEAPON_BUFF) //Remove current bonus, if any
if BlzBitAnd(udg_mc_fresh_weapon_talent, 4) == 0 then
set udg_mc_fresh_weapon_talent = BlzBitOr(udg_mc_fresh_weapon_talent, 4) //Set 3nd bit, indicating Switch is on cooldown
if freshWeaponTimer != null then
call PauseTimer(freshWeaponTimer)
else
set freshWeaponTimer = NewTimer()
endif
call TimerStart(freshWeaponTimer, 0.0625, false, function thistype.FreshWeaponDelayed)
endif
endif
call changeWeapon(GetTriggerUnit(), udg_mc_backup_weapon_index)
set udg_mc_current_weapon_index = udg_mc_backup_weapon_index
set udg_mc_backup_weapon_index = oldPrimary
call UpdateIconAndDescription()
endmethod
static method castCheckAbility takes nothing returns nothing
local integer abilityCast = GetSpellAbilityId()
if abilityCast == MC_ROULETTE_ABILITY then
call castRoulette()
elseif abilityCast == MC_SWAP_ABILITY then
call castSwitch()
endif
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_SPELL_FINISH, function SwitchWeapon.castCheckAbility, Player(0))
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_SPELL_FINISH, function SwitchWeapon.castCheckAbility, Player(1))
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_SPELL_FINISH, function SwitchWeapon.castCheckAbility, Player(2))
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_SPELL_FINISH, function SwitchWeapon.castCheckAbility, Player(3))
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_SPELL_FINISH, function SwitchWeapon.castCheckAbility, Player(4))
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_SPELL_FINISH, function SwitchWeapon.castCheckAbility, Player(6))
set udg_mc_current_weapon_index = -1
set udg_mc_backup_weapon_index = -2
set freshWeaponTimer = null
call UpdateIconAndDescription()
endmethod
endstruct
function MCForceNextRoulette takes integer weaponIndex returns nothing
set udg_mc_available_weapon_bit_array = SwitchWeapon.IndexToBit(weaponIndex)
endfunction
function MCUpdateIconAndDescription takes nothing returns nothing
call SwitchWeapon.UpdateIconAndDescription()
endfunction
endscope
scope MCPlasmaGrenade
globals
boolean MCPlasmaGrenadeNoFriendlyFireTalent = false
endglobals
function distanceBetween takes real x1, real y1, real x2, real y2 returns real
local real dx = x1 - x2
local real dy = y1 - y2
return SquareRoot((dx) * (dx) + (dy) * (dy))
endfunction
private struct PlasmaGrenade extends Missiles
boolean isAttached = false
boolean isExploded = false
effect attachedEffect
method onFinish takes nothing returns boolean
local real distanceFromStartToImpact = distanceBetween(origin.x, origin.y, impact.x, impact.y)
local real vx
local real vy
if not isAttached and distanceFromStartToImpact > 350 then
//call BJDebugMsg("onFinish - Bounce - speed: " + I2S(R2I(speed)))
set vx = impact.x + ((impact.x - origin.x) / distanceFromStartToImpact) * 0.35 * distanceFromStartToImpact
set vy = impact.y + ((impact.y - origin.y) / distanceFromStartToImpact) * 0.35 * distanceFromStartToImpact
//call BJDebugMsg("x=" + I2S(R2I(x)) + ", y=" + I2S(R2I(y)) + " - NEW COORDS: x=" + I2S(R2I(vx)) + ", y=" + I2S(R2I(vy)))
set speed = speed * 0.4
//call BJDebugMsg("onFinish - arc before: " + R2S(arc))
set arc = Rad2Deg(arc * 0.5)
//call BJDebugMsg("onFinish - arc after: " + R2S(arc))
call deflect(vx, vy, 10.)
else
//call BJDebugMsg("onFinish - Stay - speed: " + I2S(R2I(speed)))
set speed = 0
endif
return isExploded //model should either bounce or stay still "onFinish"
endmethod
method onHit takes unit hit returns boolean
if not isAttached and hit != .source and UnitAlive(hit) and not IsUnitType(hit, UNIT_TYPE_STRUCTURE) then
//call BJDebugMsg("onHit")
set target = hit
//call BJDebugMsg(GetUnitName(target))
set isAttached = true
set attachedEffect = AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareMissile.mdl", hit, "overhead")
set model = "" //Intentionally none for this missile! It should be invisible from now on!
//Need to keep this instance around to communicate to other struct info about target!
endif
return false
endmethod
method onPeriod takes nothing returns boolean
return isExploded
endmethod
method onRemove takes nothing returns nothing
//call BJDebugMsg("onRemove")
if isAttached then
//Cleanup
call DestroyEffect(attachedEffect)
set attachedEffect = null
endif
endmethod
endstruct
private struct PlasmaGrenadeController
timer t
PlasmaGrenade grenade
static method onExplosion takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
//call BJDebugMsg("onExplosion")
if grenade.isAttached then
//Grenade attached to unit
//call BJDebugMsg("isAttached")
//call BJDebugMsg(GetUnitName(grenade.target))
call doExplosion(GetUnitX(grenade.target), GetUnitY(grenade.target))
else
//Grenade on floor
//call BJDebugMsg("notAttached")
call doExplosion(grenade.x, grenade.y)
endif
set grenade.isExploded = true
call ReleaseTimer(t)
call deallocate()
set t = null
endmethod
method getDamageToApply takes nothing returns real
call Trig_MCPlasmaGrenadeDamageCalcAbilityDmg_Actions()
return udg_temp_real6
endmethod
method doExplosion takes real x, real y returns nothing
local group targetsWithinRadius = CreateGroup()
local unit u
local real damageBase = getDamageToApply()
local real damageForUnit
local boolean isAlly
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 325., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
set isAlly = IsUnitAlly(u, grenade.owner)
if UnitAlive(u) and not (isAlly and (MCPlasmaGrenadeNoFriendlyFireTalent or IsUnitType(u, UNIT_TYPE_STRUCTURE))) then
if u == grenade.target and not isAlly then
set damageForUnit = damageBase * (1.0 + GetCriticalHitDamageInclAgi(grenade.source))
elseif not IsUnitInRangeXY(u, x, y, 200.) then
set damageForUnit = damageBase * 0.5
else
set damageForUnit = damageBase
endif
call Damage.applySpell(grenade.source, u, damageForUnit, DAMAGE_TYPE_FORCE)
endif
call GroupRemoveUnit(targetsWithinRadius, u)
endloop
call DestroyGroup(targetsWithinRadius)
call DestroyEffect( AddSpecialEffect( "Units\\NightElf\\Wisp\\WispExplode.mdl", x, y) )
call DestroyEffect( AddSpecialEffect( "Units\\MasterChief\\Abilities\\PlasmaGrenade.mdx", x, y) )
call DestroyEffect( AddSpecialEffect( "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl", x, y) )
call SetSoundPosition(gg_snd_PlasmaGrenade, x, y, 70.0)
call StartSound(gg_snd_PlasmaGrenade)
set targetsWithinRadius = null
set u = null
endmethod
static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
local unit c = GetTriggerUnit()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real z = GetUnitFlyHeight(c) + 60.
local real tx = GetSpellTargetX()
local real ty = GetSpellTargetY()
local real distance = distanceBetween(x, y, tx, ty)
set .t = NewTimerEx(this)
set .grenade = PlasmaGrenade.create(x, y, z, tx, ty, 10.)
set grenade.source = c
set grenade.model = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareMissile.mdl" //"Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
set grenade.speed = 450. + distance / 900. * 200.
set grenade.arc = 38.
set grenade.collision = 16.
set grenade.owner = GetOwningPlayer(c)
call grenade.launch()
call TimerStart(t, 2.5, false, function thistype.onExplosion)
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A006', function thistype.onCast)
endmethod
endstruct
endscope
scope MasterChiefVehicleDrop
private struct Coordinate
real x
real y
real z
real pitch //in radians
real speed //percent to move per update (1.0 means it will do the whole thing in 1 update, 0.0 means it will never finish!)
real acceleration //percent per update to increase the speed with.
method destroy takes nothing returns nothing
call .deallocate()
endmethod
static method create takes real x, real y, real z, real pitch, real speed, real acceleration returns Coordinate
local thistype this = thistype.allocate()
set .x = x
set .y = y
set .z = z
set .pitch = pitch
set .speed = speed
set .acceleration = acceleration
return this
endmethod
method copyValues takes Coordinate c returns nothing
set .x = c.x
set .y = c.y
set .z = c.z
set .pitch = c.pitch
set .speed = c.speed
set .acceleration = c.acceleration
endmethod
static method copy takes Coordinate c returns Coordinate
local thistype this = thistype.allocate()
call copyValues(c)
return this
endmethod
endstruct
private struct Dropship
timer t
//timer t2
effect vehicleToDrop
effect dropship
unit caster
player owner
real faceAngle
integer targetId = 0 //indicates what step we are on
real origin2Target
real percentToTarget
boolean hasVehicleAttached
Coordinate current
Coordinate target //Current target, one of the ones below
Coordinate origin //Current origin
Coordinate tPreSlowDown //0
Coordinate tSlowDownPitchUp //1
Coordinate tSlowedDownPitchDown //2
Coordinate tDropGoingDown //3
Coordinate tDropFinishedGoUp //4
Coordinate tFlyAway //5 Destroy when reaching this!
method dealDamage takes nothing returns nothing
local group targetsWithinRadius = CreateGroup()
local unit u
local real damageToDeal = getDamageToApply()
call GroupEnumUnitsInRange(targetsWithinRadius, current.x, current.y, 250, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and not IsUnitAlly(u, owner) then
call UnitDamageTarget(caster, u, damageToDeal, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DEMOLITION, null)
if udg_mc_active_targeting_talent then
call UnitAddAbility(u, 'A02N') //Active Target Talent Marker
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup(targetsWithinRadius)
set targetsWithinRadius = null
set u = null
endmethod
method getDamageToApply takes nothing returns real
call Trig_MCVehicleDropDamageCalcAbilityDmg_Actions()
return udg_temp_real1 + udg_temp_real2
endmethod
method reachedTarget takes nothing returns nothing
call origin.destroy()
set origin = target
set percentToTarget = 0
if targetId == 0 then
set target = tSlowDownPitchUp
elseif targetId == 1 then
set target = tSlowedDownPitchDown
elseif targetId == 2 then
set target = tDropGoingDown
elseif targetId == 3 then
set target = tDropFinishedGoUp
//call BJDebugMsg("Drop, Time taken: " + R2S(10.0 - TimerGetRemaining(t2)))
call dealDamage()
set hasVehicleAttached = false
call BlzSetSpecialEffectPosition(vehicleToDrop, 0, -6000, -200)
call DestroyEffect(vehicleToDrop)
elseif targetId == 4 then
set target = tFlyAway
elseif targetId == 5 then
//call BJDebugMsg("Gone!")
call this.destroy()
endif
call current.copyValues(target)
set origin2Target = distanceBetweenXYZXYZ(origin.x, origin.y, origin.z, target.x, target.y, target.z)
set targetId = targetId + 1
//call BJDebugMsg("TargetId: " + I2S(targetId))
endmethod
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
//local real distanceToTarget = distanceBetweenXYZXYZ(current.x, current.y, current.z, target.x, target.y, target.z)
set percentToTarget = percentToTarget + current.speed
//call BJDebugMsg("looping. " + R2S(percentToTarget))
//100 + ((200 - 100)/100) * 0.9 * 100
//200 + ((100 - 200)/100) * 0.9 * 100
set current.speed = current.speed + current.acceleration
set current.pitch = target.pitch * percentToTarget + (1 - percentToTarget) * origin.pitch
set current.x = origin.x + ((target.x - origin.x) / origin2Target) * percentToTarget * origin2Target
set current.y = origin.y + ((target.y - origin.y) / origin2Target) * percentToTarget * origin2Target
set current.z = GetZHeight(current.x, current.y) + origin.z + ((target.z - origin.z) / origin2Target) * percentToTarget * origin2Target
call BlzSetSpecialEffectOrientation(dropship, faceAngle, current.pitch, 0)
call BlzSetSpecialEffectPosition(dropship, current.x, current.y, current.z)
if hasVehicleAttached then
call BlzSetSpecialEffectOrientation(vehicleToDrop, faceAngle, current.pitch, 0)
call BlzSetSpecialEffectPosition(vehicleToDrop, current.x, current.y, current.z - 160)
endif
if percentToTarget > .99 then
call reachedTarget()
endif
endmethod
method createVehicleToDrop takes integer levelOfAbility returns effect
local effect effectToReturn
if levelOfAbility == 1 then
set effectToReturn = AddSpecialEffect( "Warthog.mdl", 0, -6000)
elseif levelOfAbility == 2 then
set effectToReturn = AddSpecialEffect( "HaloScorpion3.mdl", 0, -6000)
else
set effectToReturn = AddSpecialEffect( "HaloScorpion3.mdl", 0, -6000)
call BlzSetSpecialEffectColor(effectToReturn, 255, 150, 150)
endif
return effectToReturn
endmethod
method destroy takes nothing returns nothing
call current.destroy()
call target.destroy()
call ReleaseTimer(t)
call BlzSetSpecialEffectPosition(dropship, 0, -6000, -200)
call DestroyEffect(dropship)
set t = null
set owner = null
set caster = null
set dropship = null
call .deallocate()
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real tx = GetSpellTargetX()
local real ty = GetSpellTargetY()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real dx = RAbsBJ(x - tx)
local real dy = RAbsBJ(y - ty)
local integer levelOfAbility = GetUnitAbilityLevel(c, 'A02E')
//local real x = tx + 1500 * Cos(GetFac(0, 2*bj_PI))
//local real y = ty + 1500 * Sin(GetRandomReal(0, 2*bj_PI))
//local real z = GetUnitFlyHeight(c) + 600
//local real tz = z
local thistype this = thistype.allocate()
set t = NewTimerEx(this)
set caster = c
set faceAngle = (Atan2(ty - y, tx - x))
set owner = GetOwningPlayer(c)
set hasVehicleAttached = true
//set t2 = CreateTimer() //Uncomment to check timing top drop if changes to speed/etc is done
//call TimerStart(t2, 10, false, null)
set vehicleToDrop = createVehicleToDrop(levelOfAbility)
set origin = Coordinate.create(tx - 1500 * Cos(faceAngle) , ty - 1500 * Sin(faceAngle), 800, Deg2Rad(-40), 0.0180, -0.0001)
set current = Coordinate.copy(origin)
set tPreSlowDown = Coordinate.create(tx - 500 * Cos(faceAngle) , ty - 500 * Sin(faceAngle), 300, Deg2Rad(-10), 0.05, 0.00)
set tSlowDownPitchUp = Coordinate.create(tx - 250 * Cos(faceAngle) , ty - 250 * Sin(faceAngle), 320, Deg2Rad(4), 0.06, 0.00)
set tSlowedDownPitchDown= Coordinate.create(tx - 100 * Cos(faceAngle) , ty - 100 * Sin(faceAngle), 200, 0, 0.07, 0.00)
set tDropGoingDown = Coordinate.create(tx - 0 * Cos(faceAngle) , ty - 0 * Sin(faceAngle), 150, 0, 0.07, 0.00)
set tDropFinishedGoUp = Coordinate.create(tx + 50 * Cos(faceAngle) , ty + 50 * Sin(faceAngle), 250, 0, 0.09, 0.0001)
set tFlyAway = Coordinate.create(tx + 1200 * Cos(faceAngle) , ty + 1200 * Sin(faceAngle), 800, Deg2Rad(40), 0.008, 0.0002)
set target = tPreSlowDown
set origin2Target = distanceBetweenXYZXYZ(origin.x, origin.y, origin.z, target.x, target.y, target.z)
set dropship = AddSpecialEffect( "Units\\MasterChief\\Abilities\\raptor.mdx", origin.x, origin.y)
call BlzPlaySpecialEffect( dropship, ANIM_TYPE_WALK )
call BlzSetSpecialEffectScale( dropship, 2.0)
call TimerStart(.t, 1./60., true, function thistype.onLoop)
set c = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A02E', function thistype.onCast)
endmethod
endstruct
endscope
function Trig_MCVehicleDropUpdateAbilityText_Actions takes nothing returns nothing
local integer levelOfAbility = GetUnitAbilityLevelSwapped('A02E', udg_mc_hero)
local string vehicleName
local string abilityText = "Calls on vehicle air support, dropping a "
local string vehicleAttackSuffix
local string vehicleAttackFrequency
local real dropDamage
local real dropDamageBase
local real dropDamageScaling
local real dropDamagePercent
local real maxHp
local integer totalDamage
local real cooldown
call Trig_MCVehicleDropDamageCalcAbilityDmg_Actions()
set dropDamageBase = udg_temp_real1
set dropDamageScaling = udg_temp_real2
set dropDamagePercent = udg_temp_real3
set dropDamage = dropDamageBase + dropDamageScaling
set cooldown = BlzGetUnitAbilityCooldown(udg_mc_hero, 'A02E', udg_temp_integer1 - 1)
if ( udg_temp_integer1 == 1 ) then
set vehicleName = "Warthog"
set vehicleAttackFrequency = " 4 times per second and deals "
set vehicleAttackSuffix = " to a single target."
call TriggerExecute( gg_trg_MCVehicleDropWarthohCalculations )
elseif udg_temp_integer1 == 2 then
set vehicleName = "Scorpion Tank"
set vehicleAttackFrequency = " attacks once per 2 seconds and deals "
set vehicleAttackSuffix = " in a small area."
call TriggerExecute( gg_trg_MCVehicleDropScorptionTankCalculations )
else
set vehicleName = "Heavy Scorpion Tank"
set vehicleAttackFrequency = " attacks once per 2 seconds and deals "
set vehicleAttackSuffix = " in a small area."
call TriggerExecute( gg_trg_MCVehicleDropHeavyScorptionTankCalculations )
endif
set maxHp = udg_temp_real5 + udg_temp_real3 * (udg_temp_real1 + udg_temp_real2)
set totalDamage = R2I(udg_temp_integer3 + udg_temp_real4 * udg_temp_integer4 + 0.001)
set abilityText = abilityText + vehicleName + " at the center of target location, dealing " + I2S(R2I(dropDamage + 0.001)) + " (" + I2S(R2I(dropDamageBase+0.1)) + " + |c00DF00FF" + I2S(R2I(dropDamageScaling + 0.1)) + " ("
set abilityText = abilityText + I2S(R2I(dropDamagePercent*100+0.1)) + "% of Spell Power)|r) damage to enemy land units in the area and stunning them for <A02E;ANin,Dur1> seconds.|n|nThe "
set abilityText = abilityText + vehicleName + " has " + I2S(R2I(maxHp + 0.001))+ " ("+I2S(R2I(udg_temp_real5 + 0.001))+" + |c0096f064"
set abilityText = abilityText + I2S(R2I(udg_temp_real3 * udg_temp_real1 + 0.001)) + "|r + |c008064EC" + I2S(R2I(udg_temp_real3 * udg_temp_real2 + 0.001)) + "|r (" + I2S(R2I(100 * udg_temp_real3 + 0.001)) + "% of "
set abilityText = abilityText + "|c0096f064MaxHP|r + |c008064ECMaxMana|r)) HP.|nThe " + vehicleName + vehicleAttackFrequency + I2S(totalDamage) + " (" + I2S(udg_temp_integer3) + " + |c00FF7800"
set abilityText = abilityText + I2S(R2I(udg_temp_real4 * udg_temp_integer4 + 0.001)) + " (" + I2S(R2I(100 * udg_temp_real4 + 0.001)) + "% of Damage)|r) damage per attack"
set abilityText = abilityText + vehicleAttackSuffix + "|n|nThe " + vehicleName + " lasts <A02E;ANin,DataB1> seconds.|nCooldown: " + R2SW(cooldown,0,2) + " seconds."
call BlzSetAbilityExtendedTooltip( 'A02E', abilityText, 0 )
endfunction
//===========================================================================
function InitTrig_MCVehicleDropUpdateAbilityText takes nothing returns nothing
set gg_trg_MCVehicleDropUpdateAbilityText = CreateTrigger( )
call TriggerAddAction( gg_trg_MCVehicleDropUpdateAbilityText, function Trig_MCVehicleDropUpdateAbilityText_Actions )
endfunction
globals
constant real KH_FLAME_SHIELD_TICK_RATE = 1.0 / 40.0
constant integer KH_FLAME_SHIELD_ABILITY_ID = 'A03A'
constant integer KH_FLAME_SHIELD_BUFF_ID = 'B00E'
boolean kh_flame_shield_soothing_talent = false
boolean kh_flame_shield_shield_talent = false
endglobals
function Trig_KHFlameShieldCast_Conditions takes nothing returns boolean
return GetSpellAbilityId() == KH_FLAME_SHIELD_ABILITY_ID and not HaveSavedHandle(udg_heroes_hash, GetHandleId(GetSpellTargetUnit()), StringHash("KHFlameShieldSource"))
endfunction
function Trig_KHFlameShieldCast_SetupFlame takes real scale, real angle, real tx, real ty, real zOffset, real distance, integer id, integer flameIndex returns nothing
local real x = tx + distance * Sin(angle)
local real y = ty + distance * Cos(angle)
local real z = GetZHeight(x, y)
local effect orbVfx = AddSpecialEffect("Abilities\\Khadgar\\OrbFireX.mdl", x, y)
local effect fireVfx = AddSpecialEffect("Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl", x, y)
call BlzSetSpecialEffectScale(orbVfx, scale)
call BlzSetSpecialEffectScale(fireVfx, scale)
call BlzSetSpecialEffectHeight(orbVfx, z + zOffset)
call BlzSetSpecialEffectHeight(fireVfx, z + 110)
call SaveEffectHandle(udg_heroes_hash, id, StringHash("KHFlameShieldVfxOrb" + I2S(flameIndex)), orbVfx)
call SaveEffectHandle(udg_heroes_hash, id, StringHash("KHFlameShieldVfxFire" + I2S(flameIndex)), fireVfx)
call SaveReal(udg_heroes_hash, id, StringHash("KHFlameShieldAngle" + I2S(flameIndex)), angle)
set orbVfx = null
set fireVfx = null
endfunction
function Trig_KHFlameShieldCast_Tick_One takes unit target, unit caster, player casterOwner, real distance, real zOffset, real damage, real radius, boolean amplified, integer index returns nothing
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local integer id = GetHandleId(target)
local real angle = LoadReal(udg_heroes_hash, id, StringHash("KHFlameShieldAngle" + I2S(index))) + 3.2 * bj_PI/180.
local real x = tx + distance * Sin(angle)
local real y = ty + distance * Cos(angle)
local real z = GetZHeight(x, y)
local effect orbVfx = LoadEffectHandle(udg_heroes_hash, id, StringHash("KHFlameShieldVfxOrb" + I2S(index)))
local effect fireVfx = LoadEffectHandle(udg_heroes_hash, id, StringHash("KHFlameShieldVfxFire" + I2S(index)))
local group targetsWithinRadius = CreateGroup()
local unit u
call RemoveSavedReal(udg_heroes_hash, id, StringHash("KHFlameShieldAngle" + I2S(index)))
call SaveReal(udg_heroes_hash, id, StringHash("KHFlameShieldAngle" + I2S(index)), angle)
call BlzSetSpecialEffectPosition(orbVfx, x, y, z + zOffset)
call BlzSetSpecialEffectPosition(fireVfx, x, y, z + 110)
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius, null)
call GroupRemoveUnit(targetsWithinRadius, target)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if kh_flame_shield_soothing_talent and IsUnitAlly(u, casterOwner) then
call AddTimedShield(u, 0.14 * damage, 4.)
elseif not UnitHasBuffBJ(u, KH_FLAME_SHIELD_BUFF_ID) and UnitAlive(u) and not (IsUnitAlly(u, casterOwner) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
call UnitDamageTarget(caster, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup(targetsWithinRadius)
set u = null
set targetsWithinRadius = null
set orbVfx = null
set fireVfx = null
endfunction
function Trig_KHFlameShieldCast_Cleanup takes integer targetId returns nothing
//call DebugPrint("Flame Shield Cleanup", "KHDebug", null, true)
call RemoveSavedHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldSource"))
call RemoveSavedHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldTarget"))
call DestroyEffect(LoadEffectHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxOrb0")))
call DestroyEffect(LoadEffectHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxOrb1")))
call DestroyEffect(LoadEffectHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxOrb2")))
call DestroyEffect(LoadEffectHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxFire0")))
call DestroyEffect(LoadEffectHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxFire1")))
call DestroyEffect(LoadEffectHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxFire2")))
call RemoveSavedHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxOrb0"))
call RemoveSavedHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxOrb1"))
call RemoveSavedHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxOrb2"))
call RemoveSavedHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxFire0"))
call RemoveSavedHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxFire1"))
call RemoveSavedHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldVfxFire2"))
call RemoveSavedReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldAngle0"))
call RemoveSavedReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldAngle1"))
call RemoveSavedReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldAngle2"))
call RemoveSavedBoolean(udg_heroes_hash, targetId, StringHash("KHFlameShieldAmplified"))
call RemoveSavedReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDamage"))
call RemoveSavedReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDistance"))
call RemoveSavedReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldZOffset"))
call RemoveSavedReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDamageRadius"))
endfunction
function Trig_KHFlameShieldCast_Tick takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer targetId = GetTimerData(t)
local unit caster = LoadUnitHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldSource"))
local unit target = LoadUnitHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldTarget"))
local player casterOwner = GetOwningPlayer(caster)
local boolean amplified = LoadBoolean(udg_heroes_hash, targetId, StringHash("KHFlameShieldAmplified"))
local real dps = LoadReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDamage"))
local real distance = LoadReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDistance"))
local real zOffset = LoadReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldZOffset"))
local real radius = LoadReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDamageRadius"))
local integer i = 0
//call DebugPrint("Flame Shield Tick", "KHDebug", null, true)
if UnitAlive(target) and UnitHasBuffBJ(target, KH_FLAME_SHIELD_BUFF_ID) then
loop
exitwhen i > 2
call Trig_KHFlameShieldCast_Tick_One(target, caster, casterOwner, distance, zOffset, dps, radius, amplified, i)
set i = i + 1
endloop
else
call Trig_KHFlameShieldCast_Cleanup(targetId)
call ReleaseTimer(t)
endif
set casterOwner = null
set t = null
set caster = null
set target = null
endfunction
function Trig_KHFlameShieldCast_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local integer casterId = GetHandleId(caster)
local integer targetId = GetHandleId(target)
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real baseAngle = GetRandomReal(0, 2 * bj_PI)
local timer t = NewTimerEx(targetId)
local real scale
local real angle
local real distance
local real damagePerSecond
local real zOffset
local real damageRadius
local boolean isAmplified = UnitHasBuffBJ(caster, 'B00F')
call Trig_KHFlameShieldCalculations_Actions()
if isAmplified then
//call DebugPrint("Cast Flame Shield Amplified", "KHDebug", null, true)
set damageRadius = 170.
set distance = BlzGetUnitCollisionSize(target) + damageRadius + 9.
set scale = 2.0
set damagePerSecond = udg_temp_real6 * (1.0 + udg_temp_real4)
//call DebugPrint("Flame Shield Amplified Damage/Tick: " + I2S(R2I(damagePerSecond)), "KHDebug", null, true)
set zOffset = -60.
call UnitRemoveBuffBJ( 'B00F', caster )
set udg_kh_amplify_magic_index = -1
else
//call DebugPrint("Cast Flame Shield Normal", "KHDebug", null, true)
set damageRadius = 85.
set distance = BlzGetUnitCollisionSize(target) + damageRadius + 14.
set scale = 1.0
set zOffset = 20.
set damagePerSecond = udg_temp_real6
//call DebugPrint("Flame Shield Damage/Tick: " + I2S(R2I(damagePerTick)), "KHDebug", null, true)
endif
if kh_flame_shield_shield_talent and IsUnitAlly(target, GetOwningPlayer(caster)) then
call AddTimedShield(target, 1.25 * damagePerSecond, 10.)
endif
call SaveUnitHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldSource"), caster)
call SaveUnitHandle(udg_heroes_hash, targetId, StringHash("KHFlameShieldTarget"), target)
call SaveBoolean(udg_heroes_hash, targetId, StringHash("KHFlameShieldAmplified"), isAmplified)
call SaveReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDamage"), damagePerSecond * KH_FLAME_SHIELD_TICK_RATE)
call SaveReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDistance"), distance)
call SaveReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldZOffset"), zOffset)
call SaveReal(udg_heroes_hash, targetId, StringHash("KHFlameShieldDamageRadius"), damageRadius)
call Trig_KHFlameShieldCast_SetupFlame(scale, baseAngle, targetX, targetY, zOffset, distance, targetId, 0)
set angle = baseAngle + 2.0 * bj_PI / 3.0
call Trig_KHFlameShieldCast_SetupFlame(scale, angle, targetX, targetY, zOffset, distance, targetId, 1)
set angle = baseAngle - 2.0 * bj_PI / 3.0
call Trig_KHFlameShieldCast_SetupFlame(scale, angle, targetX, targetY, zOffset, distance, targetId, 2)
call TimerStart(t, KH_FLAME_SHIELD_TICK_RATE, true, function Trig_KHFlameShieldCast_Tick)
set t = null
set caster = null
set target = null
endfunction
function InitTrig_KHFlameShieldCast takes nothing returns nothing
set gg_trg_KHFlameShieldCast = CreateTrigger( )
//call RegisterSpellEffectEvent('A01R', function thistype.onCast)
call TriggerRegisterAnyUnitEventBJ( gg_trg_KHFlameShieldCast, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_KHFlameShieldCast, Condition( function Trig_KHFlameShieldCast_Conditions ) )
call TriggerAddAction( gg_trg_KHFlameShieldCast, function Trig_KHFlameShieldCast_Actions )
endfunction
scope Khadgar
globals
integer kh_fireball_bounces = 5
boolean kh_fireball_split_on_amp_flameshield = false
endglobals
private keyword KHFireballCast
function clearKHAmpFlameShieldSplitFireballs takes unit u1, unit u2 returns nothing
call ClearFlag(u1, "KHAmpFlameShieldSplitFireballs")
endfunction
private struct KHFireball extends Missiles
static string KH_BIG_EXPLOSION = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
static string KH_EXPLOSION = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
string vfxPath
integer bounceCounter
timer t
real aoeRadius
real timeBetweenBounces
boolean shouldDestroy
method prepareDestroy takes nothing returns nothing
set shouldDestroy = true
set vfxPath = null
call ReleaseTimer(t)
set t = null
endmethod
method doSplitFireballTalent takes unit target returns nothing
local integer id = GetHandleId(target)
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local real timeToFirstBounce = LoadReal(udg_heroes_hash, id, StringHash("KHFlameShieldDistance")) / 1000.
local real angle = LoadReal(udg_heroes_hash, id, StringHash("KHFlameShieldAngle0")) + 6. * bj_DEGTORAD
call KHFireballCast.evaluate(.source, tx, ty, angle, timeToFirstBounce, false)
set angle = LoadReal(udg_heroes_hash, id, StringHash("KHFlameShieldAngle1")) + 6. * bj_DEGTORAD
call KHFireballCast.evaluate(.source, tx, ty, angle, timeToFirstBounce, false)
set angle = LoadReal(udg_heroes_hash, id, StringHash("KHFlameShieldAngle2")) + 6. * bj_DEGTORAD
call KHFireballCast.evaluate(.source, tx, ty, angle, timeToFirstBounce, false)
call DelayedUnitAction.setup(target, null, UnitAction.clearKHAmpFlameShieldSplitFireballs, 0.8)
endmethod
method dealDamage takes nothing returns nothing
//source, x, y, damage, aoeRadius, DAMAGE_TYPE_FIRE
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, aoeRadius + 64, null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitHasBuffBJ(u, KH_FLAME_SHIELD_BUFF_ID) then
if kh_fireball_split_on_amp_flameshield and LoadBoolean(udg_heroes_hash, GetHandleId(u), StringHash("KHFlameShieldAmplified")) and not IsFlagSet(u, "KHAmpFlameShieldSplitFireballs") then
call SetFlag(u, "KHAmpFlameShieldSplitFireballs")
call doSplitFireballTalent(u)
endif
//TODO:
endif
if UnitAlive(u) and not IsUnitAlly(u, owner) and IsUnitInRangeXY(u, x, y, aoeRadius) then
//call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, dmgType, null)
call Damage.applySpell(source, u, damage, DAMAGE_TYPE_FIRE)
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endmethod
static method fireballBounce takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call DestroyEffect(AddSpecialEffect(vfxPath, x, y))
call dealDamage()
//call damageAoENoFF(source, x, y, damage, aoeRadius, DAMAGE_TYPE_FIRE)
set bounceCounter = bounceCounter + 1
if bounceCounter >= kh_fireball_bounces then
call prepareDestroy()
else
call TimerStart(t, timeBetweenBounces, false, function thistype.fireballBounce)
endif
endmethod
method onPeriod takes nothing returns boolean
return shouldDestroy
endmethod
static method castFiraball takes unit caster, real x, real y, real angle, real initialTimeToBounce, boolean isAmplified returns nothing
local real z = GetUnitFlyHeight(caster) + 70.
local real tx = x + 2500 * Cos(angle)
local real ty = y + 2500 * Sin(angle)
local thistype this = thistype.create(x, y, z, tx, ty, GetZHeight(tx, ty) + 50.)
call Trig_KHFireballCalculations_Actions()
if isAmplified then
set damage = udg_temp_real6 * (1 + udg_temp_real4)
set vfxPath = KH_BIG_EXPLOSION
set aoeRadius = 320.
set timeBetweenBounces = 0.225
call UnitRemoveAbility(caster, 'B00F')
else
set damage = udg_temp_real6
set vfxPath = KH_EXPLOSION
set aoeRadius = 160.
set timeBetweenBounces = 0.12
endif
set source = caster
set model = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
set speed = 1000
set collision = 0
set owner = GetOwningPlayer(caster)
set bounceCounter = 0
set shouldDestroy = false
set t = NewTimerEx(this)
call TimerStart(t, initialTimeToBounce, false, function thistype.fireballBounce)
call launch()
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real targetX = GetSpellTargetX()
local real targetY = GetSpellTargetY()
local real angle = Atan2(targetY - y, targetX - x)
local boolean isAmplified = GetUnitAbilityLevel(c, 'B00F') > 0
local real initialTimeToExplision = distanceBetweenXYXY(x, y, targetX, targetY) / 1000.
call castFiraball(c, x, y, angle, initialTimeToExplision, isAmplified)
set c = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A01R', function thistype.onCast)
endmethod
endstruct
function KHFireballCast takes unit caster, real x, real y, real angle, real initialTimeToBounce, boolean isAmplified returns nothing
call KHFireball.castFiraball(caster, x, y, angle, initialTimeToBounce, isAmplified)
endfunction
endscope
function Trig_MTTuskMissilesFire_Conditions takes nothing returns boolean
return udg_mt_tusk_target != null
endfunction
function Trig_MTTuskMissilesFire_Actions_Hit takes real x, real y, real angleNA returns nothing
call TriggerExecute( gg_trg_MTTuskMissilesSetDmgCalculations )
call damageAoENoFF(udg_mammoth_tank_hero, x, y, udg_temp_real4, udg_mt_tusk_radius, DAMAGE_TYPE_DEMOLITION)
endfunction
function Trig_MTTuskMissilesFireAtTarget takes real tx, real ty returns nothing
local real x = GetUnitX(udg_mammoth_tank_hero)
local real y = GetUnitY(udg_mammoth_tank_hero)
local unit dummy = TZGetDummyForUnitTimed(udg_mammoth_tank_hero, 'A033', 0.25)
call IssuePointOrder( dummy, "clusterrockets", tx, ty )
//x, y, angle (N/A), function, delay
//call TZRecycleDummy(dummy)
set dummy = null
call TimedCoordinateAction.setup(tx, ty, -1, CoordinateAction.Trig_MTTuskMissilesFire_Actions_Hit, 0.75)
endfunction
function Trig_MTTuskMissilesFire_Actions takes nothing returns nothing
local real tx = GetUnitX(udg_mt_tusk_target)
local real ty = GetUnitY(udg_mt_tusk_target)
call Trig_MTTuskMissilesFireAtTarget(tx, ty)
set udg_mt_tusk_target = null
call TimerStart(udg_mt_tusk_cooldown_timer, BlzGetUnitAbilityCooldown(udg_mammoth_tank_hero, 'A02D', ( GetUnitAbilityLevel(udg_mammoth_tank_hero, 'A02D') - 1 )) - 0.75 * GetBonusAttackSpeed(udg_mammoth_tank_hero), false, null)
endfunction
//===========================================================================
function InitTrig_MTTuskMissilesFire takes nothing returns nothing
set gg_trg_MTTuskMissilesFire = CreateTrigger( )
call TriggerAddCondition( gg_trg_MTTuskMissilesFire, Condition( function Trig_MTTuskMissilesFire_Conditions ) )
call TriggerAddAction( gg_trg_MTTuskMissilesFire, function Trig_MTTuskMissilesFire_Actions )
endfunction
scope MammothTank
globals
private constant string INDICATOR_VFX = "vfx\\TargetIndicatorThinner_TC_100.mdx"
private constant integer V2_CODE = 'A028'
endglobals
private struct V2Rocket extends Missiles
private effect targetIndicator
method getDamageToApply takes nothing returns real
call Trig_MTV2RocketDmgCalculations_Actions()
return udg_temp_real5
endmethod
method onFinish takes nothing returns boolean
local group targetsWithinRadius = CreateGroup()
local unit u
local real damage = getDamageToApply()
local real distanceToCenter
local real ux
local real uy
local real dx
local real dy
local real radius = BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, V2_CODE), ABILITY_RLF_AREA_OF_EFFECT, 0)
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius + 50., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and not (IsUnitAlly(u, owner) and IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
if udg_mt_v2_krak_talent and IsUnitInRangeXY(u, x, y, 75.) then
call UnitDamageTarget(source, u, 1.4 * damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DEMOLITION, null)
call StunTarget(source, u, 1., 1.)
elseif IsUnitInRangeXY(u, x, y, radius) then
call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DEMOLITION, null)
//Knockback
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set dx = ux - x
set dy = uy - y
set distanceToCenter = SquareRoot(dx * dx + dy * dy)
set udg_Knockback2DUnit = u
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(dy, dx)
set udg_Knockback2DDistance = (100. + radius - distanceToCenter) * 0.66
set udg_Knockback2DTime = 0.15 + udg_Knockback2DDistance / 650.0
set udg_Knockback2DCollision = 8.0
call TriggerExecute(gg_trg_Knockback_2D)
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup(targetsWithinRadius)
call DestroyEffect( AddSpecialEffect( "Objects\\Spawnmodels\\Human\\HCancelDeath\\HCancelDeath.mdl", x, y) )
call DestroyEffect(targetIndicator)
set targetsWithinRadius = null
set u = null
set targetIndicator = null
set udg_g_real1 = x
set udg_g_real2 = y
return true
endmethod
static method fireMissile takes unit c, real tx, real ty returns nothing
local real angle = GetRandomReal(0, 2*bj_PI)
local real x = tx + 2000. * Cos(angle)
local real y = ty + 2000. * Sin(angle)
local thistype this = thistype.create(x, y, 600., tx, ty, 10.)
local string sound_file_name = "war3mapImported\\V2Missile.wav"
local sound snd = CreateSound(sound_file_name, false, true, true, 10, 10, "")
local integer sound_duration_msec = GetSoundFileDuration(sound_file_name)
local real radius = BlzGetAbilityRealLevelField(BlzGetUnitAbility(c, V2_CODE), ABILITY_RLF_AREA_OF_EFFECT, 0)
if udg_mt_v2_tusk_targeting_talent and IsUnitInRangeXY(c, tx, ty, udg_mt_tusk_range) then
call Trig_MTTuskMissilesFireAtTarget(tx, ty)
endif
set source = c
set model = "Missiles\\MammothTank\\Rocket.mdx"
set speed = 1000.0
set arc = 22.0
set owner = GetOwningPlayer(c)
call SetSoundDistances(snd, 600.00, 10000.00)
call SetSoundDistanceCutoff(snd, 3000.00)
call SetSoundPosition(snd, x, y, 0 )
call SetSoundDuration(snd, sound_duration_msec)
call SetSoundVolume(snd, 127)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd = null
//set curve = GetRandomReal(15, 30)*GetRandomInt(-1, 1)
//set target = GetSpellTargetUnit()
//call BlzPlaySpecialEffect(effect.effect, ConvertAnimType(5))
call launch()
set targetIndicator = AddSpecialEffect(INDICATOR_VFX, tx, ty)
call BlzSetSpecialEffectColorByPlayer(targetIndicator, GetOwningPlayer(source))
call BlzSetSpecialEffectScale(targetIndicator, radius / 100.)
call BlzSetSpecialEffectAlpha(targetIndicator, 150)
set c = null
endmethod
static method onCast takes nothing returns nothing
call fireMissile(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
endmethod
static method onInit takes nothing returns nothing
call Preload("war3mapImported\\V2Missile.wav")
call RegisterSpellEffectEvent(V2_CODE, function thistype.onCast)
endmethod
endstruct
endscope
scope TassadarPsionicAssault
globals
boolean ts_assistanceOfAdun_talent
endglobals
private struct PsionicBolt extends Missiles
boolean shouldDestroyProjectile
static method fire takes unit caster, real tx, real ty, real dmg returns nothing
local real ux = GetUnitX(caster)
local real uy = GetUnitY(caster)
local real uz = GetUnitFlyHeight(caster) + 64.
local thistype this = PsionicBolt.create(ux, uy, uz, tx, ty, 64.)
set shouldDestroyProjectile = false
set .source = caster
set .model = "Units\\Tassadar\\Abilities\\Shot II Blue.mdx"
set .speed = 750.
set .collision = 54.
set .arc = GetRandomReal(5., 12.)
set .curve = GetRandomReal(-24., 24.)
set .owner = GetOwningPlayer(caster)
set .damage = dmg
call .launch()
endmethod
method onHit takes unit hit returns boolean
if not IsUnitAlly(hit, owner) and UnitAlive(hit) then
if ts_assistanceOfAdun_talent and GetUnitState(hit, UNIT_STATE_LIFE) == GetUnitState(hit, UNIT_STATE_MAX_LIFE) then
call fire(source, .x, .y, .damage)
call fire(source, .x, .y, .damage)
call fire(source, .x, .y, .damage)
endif
if udg_ts_psiassault_psi_surge_talent then
call damageAoENoFF(source, GetUnitX(hit), GetUnitY(hit), damage, 64., DAMAGE_TYPE_MAGIC)
else
call Damage.applySpell(.source, hit, damage, DAMAGE_TYPE_MAGIC)
endif
return true
endif
return false
endmethod
method onPeriod takes nothing returns boolean
return shouldDestroyProjectile
endmethod
static method timeToDestroy takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set shouldDestroyProjectile = true
call ReleaseTimer(t)
set t = null
endmethod
method onFinish takes nothing returns boolean
local timer t = NewTimerEx(this)
call TimerStart(t, 0.09, false, function thistype.timeToDestroy)
set t = null
return false
endmethod
method customInit takes nothing returns nothing
set shouldDestroyProjectile = false
endmethod
endstruct
private struct PsionicAssaultCast
timer t
unit caster
real tx
real ty
real damagePerMissile
integer numberOfMissiles
static method fireMissiles takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if numberOfMissiles > 0 then
set numberOfMissiles = numberOfMissiles - 1
call PsionicBolt.fire(caster, .tx, .ty, .damagePerMissile)
else
call ReleaseTimer(t)
set t = null
set caster = null
call deallocate()
endif
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
//local integer levelOfAbility = GetUnitAbilityLevel(c, 'A03D')
local thistype this = thistype.allocate()
//local integer additionalMissiles = R2I(GetUnitStateSwap(UNIT_STATE_MANA, c) / 400.)
call Trig_TSPsionicAssaultDmgCalculations_Actions()
set .damagePerMissile = udg_temp_real6
set .numberOfMissiles = udg_ts_psiassault_projectiles_base + udg_temp_integer5
set udg_temp_unit = c
set udg_temp_integer1 = udg_temp_integer5 * R2I(udg_temp_real4 + 0.001)
if udg_ts_psiassault_psi_surge_talent then
set .numberOfMissiles = R2I(.numberOfMissiles / 2.0 + 0.51)
set .damagePerMissile = .damagePerMissile * 2.0
endif
//call BJDebugMsg("Additinoal Projectiles: " + I2S(udg_temp_integer4))
//call BJDebugMsg("Mana-cost form additional Projectiles: " + I2S(udg_temp_integer1))
call SetUnitState(c, UNIT_STATE_MANA, GetUnitState(udg_ts_hero, UNIT_STATE_MANA) - udg_temp_integer1)
call Trig_ManaRefundAction_Actions()
set .t = NewTimerEx(this)
set .caster = c
set .tx = GetSpellTargetX()
set .ty = GetSpellTargetY()
call TimerStart(.t, 1.0 / I2R(numberOfMissiles), true, function thistype.fireMissiles)
set c = null
endmethod
static method onInit takes nothing returns nothing
set ts_assistanceOfAdun_talent = false
call RegisterSpellEffectEvent('A03D', function thistype.onCast)
endmethod
endstruct
endscope
scope TassadarVoidRift
private struct VoidRift
timer tMoveLoop
timer tPullLoop
timer timeToDestroy
effect vfx
unit caster
unit dummySlowDude
player owner
real x
real y
real faceAngle //in radians
real damage
//real pullFactor
real speed
real period
static method onLoopMoveAndDamage takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local group targetsWithinRadius = CreateGroup()
local unit u
local real unitCollisonSize
local real distanceToCenter
local real ux
local real uy
local real dx
local real dy
set x = x + speed * Cos(faceAngle)
set y = y + speed * Sin(faceAngle)
call BlzSetSpecialEffectPosition(vfx, x, y, GetZHeight(x, y) + 20.)
//local real distanceToTarget = distanceBetweenXYZXYZ(current.x, current.y, current.z, target.x, target.y, target.z)
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 325., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if not IsUnitAlly(u, owner) and UnitAlive(u) then
set unitCollisonSize = BlzGetUnitCollisionSize(u)
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set dx = x - ux
set dy = y - uy
set distanceToCenter = SquareRoot(dx * dx + dy * dy)
if distanceToCenter < 250.0 + unitCollisonSize then
if distanceToCenter < 60.0 + unitCollisonSize then
call Damage.applySpell(.caster, u, .damage * 2, DAMAGE_TYPE_MAGIC)
else
call Damage.applySpell(.caster, u, .damage, DAMAGE_TYPE_MAGIC)
endif
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup(targetsWithinRadius)
set targetsWithinRadius = null
endmethod
static method onLoopPull takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local group targetsWithinRadius = CreateGroup()
local unit u
local real unitCollisonSize
local real distanceToCenter
local real ux
local real uy
local real dx
local real dy
local real pullEffectiveness
call SetUnitX(.dummySlowDude, x)
call SetUnitY(.dummySlowDude, y)
//local real distanceToTarget = distanceBetweenXYZXYZ(current.x, current.y, current.z, target.x, target.y, target.z)
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 325., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if not IsUnitAlly(u, owner) and UnitAlive(u) then
set unitCollisonSize = BlzGetUnitCollisionSize(u)
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set dx = x - ux
set dy = y - uy
set distanceToCenter = SquareRoot(dx * dx + dy * dy)
if distanceToCenter < 250.0 + unitCollisonSize then
//if IsUnitType(u, UNIT_TYPE_HERO) then
// set pullEffectiveness = 0.66
//else
set pullEffectiveness = 1.0
//endif
//call BJDebugMsg("effectiveness: " + R2S(effectiveness))
set udg_Knockback2DUnit = u
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(dy, dx)
set udg_Knockback2DDistance = (250. + unitCollisonSize - distanceToCenter * 0.70) * pullEffectiveness * .35
set udg_Knockback2DTime = .35
set udg_Knockback2DLoopFX = null
set udg_Knockback2DCollision = 0.0
call TriggerExecute(gg_trg_Knockback_2D)
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup(targetsWithinRadius)
set targetsWithinRadius = null
endmethod
static method destroyTimerFired takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call this.destroy()
endmethod
method destroy takes nothing returns nothing
call ReleaseTimer(tMoveLoop)
call ReleaseTimer(tPullLoop)
call ReleaseTimer(timeToDestroy)
call DestroyEffect(.vfx)
set tMoveLoop = null
set tPullLoop = null
set timeToDestroy = null
set owner = null
set caster = null
set .vfx = null
call TZRecycleDummy(.dummySlowDude)
call .deallocate()
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real tx = GetSpellTargetX()
local real ty = GetSpellTargetY()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local integer levelOfAbility = GetUnitAbilityLevel(c, 'A032')
local thistype this = thistype.allocate()
set .tMoveLoop = NewTimerEx(this)
set .tPullLoop = NewTimerEx(this)
set .timeToDestroy = NewTimerEx(this)
set .caster = c
set .owner = GetOwningPlayer(c)
set .x = x
set .y = y
set .faceAngle = (Atan2(ty - y, tx - x))
set .period = 1./40.
set .speed = 240. * .period
call Trig_TSVoidRiftDmgCalculations_Actions()
set .damage = udg_temp_real6 * .period
set .dummySlowDude = GetDummy(c, 'A04A')
//call PauseUnit(.dummySlowDude, false)
//call SetUnitOwner(.dummySlowDude, .owner, true)
//call UnitAddAbility(.dummySlowDude, 'A04A')
//set addedAbility = UnitAddAbility(.caster, 'A03H')
//call UnitAddAbility(.dummySlowDude, 'A04A')
//set .pullFactor = 1.0 + (0.001 * udg_temp_integer2)
//call BJDebugMsg("Level of ability: " + I2S(levelOfAbility))
//call TimerStart(t2, 10, false, null)
set .vfx = AddSpecialEffect( "Units\\Tassadar\\Abilities\\Void Rift II Purple.mdx", .x, .y)
call BlzSetSpecialEffectOrientation(.vfx, faceAngle, 0, 0)
//call BlzPlaySpecialEffect( dropship, ANIM_TYPE_WALK )
//call BlzSetSpecialEffectScale( dropship, 2.0)
call TimerStart(.tMoveLoop, .period, true, function thistype.onLoopMoveAndDamage)
call TimerStart(.tPullLoop, .21, true, function thistype.onLoopPull)
call TimerStart(.timeToDestroy, 7., false, function thistype.destroyTimerFired)
set c = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A032', function thistype.onCast)
endmethod
endstruct
endscope
scope TassadarForceField
private struct ForceField
timer tLoop
timer timeToDestroy
//timer t2
effect forceFieldEffect
player owner
real x
real y
real friendlyEffectiveness
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local group targetsWithinRadius = CreateGroup()
local unit u
local real unitCollisonSize
local real distanceToCenter
local real ux
local real uy
local real dx
local real dy
local real effectiveness
//local real distanceToTarget = distanceBetweenXYZXYZ(current.x, current.y, current.z, target.x, target.y, target.z)
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, 256., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
set unitCollisonSize = BlzGetUnitCollisionSize(u)
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set dx = ux - x
set dy = uy - y
set distanceToCenter = SquareRoot(dx * dx + dy * dy)
if distanceToCenter < 128. + unitCollisonSize then
if IsUnitAlly(u, owner) then
set effectiveness = .friendlyEffectiveness
elseif IsUnitType(u, UNIT_TYPE_HERO) then
set effectiveness = 0.66
else
set effectiveness = 1.0
endif
set effectiveness = effectiveness * 32. / unitCollisonSize
//call BJDebugMsg("effectiveness: " + R2S(effectiveness))
if effectiveness > 0.03 then
set udg_Knockback2DUnit = u
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(dy, dx)
set udg_Knockback2DDistance = (136 + unitCollisonSize - distanceToCenter) * effectiveness
set udg_Knockback2DTime = (0.10 + udg_Knockback2DDistance / 600.0) // * (1. / effectiveness)
set udg_Knockback2DCollision = 8.0
call TriggerExecute(gg_trg_Knockback_2D)
endif
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup(targetsWithinRadius)
set targetsWithinRadius = null
endmethod
static method destroyTimerFired takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call this.destroy()
endmethod
method destroy takes nothing returns nothing
call ReleaseTimer(tLoop)
call ReleaseTimer(timeToDestroy)
call DestroyEffect(forceFieldEffect)
set tLoop = null
set timeToDestroy = null
set owner = null
set forceFieldEffect = null
call .deallocate()
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real tx = GetSpellTargetX()
local real ty = GetSpellTargetY()
local real duration = 11.0
local thistype this = thistype.allocate()
local group bonurArmorGroup = CreateGroup()
local unit u
local real totalArmorBonus
local real totalArmorDuration
call Trig_TSForceFieldCalculations_Actions()
set .tLoop = NewTimerEx(this)
set .timeToDestroy = NewTimerEx(this)
set .owner = GetOwningPlayer(c)
set .x = tx
set .y = ty
set .friendlyEffectiveness = udg_temp_real6
//call BJDebugMsg("Level of ability: " + I2S(levelOfAbility))
//call TimerStart(t2, 10, false, null)
set forceFieldEffect = AddSpecialEffect( "Units\\Tassadar\\Abilities\\SC2ForceField_ByEpsilon.mdx", .x, .y)
//call BlzPlaySpecialEffect( dropship, ANIM_TYPE_WALK )
//call BlzSetSpecialEffectScale( dropship, 2.0)
call TimerStart(.tLoop, 1./10., true, function thistype.onLoop)
call TimerStart(.timeToDestroy, duration, false, function thistype.destroyTimerFired)
set totalArmorBonus = udg_temp_integer3 + udg_temp_real2 * udg_temp_integer2
set totalArmorDuration = udg_temp_real3 + udg_temp_real5
call GroupEnumUnitsInRange(bonurArmorGroup, tx, ty, 1000, null)
loop
set u = FirstOfGroup(bonurArmorGroup)
exitwhen u == null
call GroupRemoveUnit(bonurArmorGroup, u)
if UnitAlive(u) and IsUnitAlly(u, owner) then
call AddUnitBonusTimed(u, BONUS_ARMOR, totalArmorBonus, totalArmorDuration)
endif
endloop
call SetSoundPosition(gg_snd_ForceField, x, y, 70.0)
call StartSound(gg_snd_ForceField)
call DestroyGroup(bonurArmorGroup)
set c = null
set bonurArmorGroup = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A030', function thistype.onCast)
endmethod
endstruct
endscope
scope TassadarPsionicStorm
globals
constant integer TS_PS_ABILITY_ID = 'A02Z'
real ts_ps_damage_modifier = 1.0
real ts_ps_wave_delay = 0.5
integer ts_ps_wave_count = 6
endglobals
private struct PsionicStorm
timer t
integer counter
real x
real y
real damage
method getDamageToApply takes nothing returns real
call Trig_TSPsionicStormDmgCalculations_Actions()
return udg_temp_real6
endmethod
method spawnVfx takes real minRadius, real maxRadius returns nothing
local real angle = GetRandomReal(0, 2*bj_PI)
local effect tempEffect = AddSpecialEffect( "Units\\Tassadar\\Abilities\\Lightnings Long.mdx", x + GetRandomReal(minRadius, maxRadius) * Cos(angle), y + GetRandomReal(minRadius, maxRadius) * Sin(angle))
call BlzSetSpecialEffectAlpha(tempEffect, 100)
call DestroyEffectLater(tempEffect, ts_ps_wave_delay)
set tempEffect = null
endmethod
method destroy takes nothing returns nothing
call ReleaseTimer(t)
set t = null
call deallocate()
endmethod
static method tick takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call spawnVfx(0, 80)
call spawnVfx(80, 120)
call spawnVfx(120, 160)
call spawnVfx(160, 200)
call spawnVfx(200, 250)
call spawnVfx(250, 310)
set counter = counter + 1
call damageAoENoFriendlyBuildingDamage(udg_ts_hero, x, y, damage, 350, DAMAGE_TYPE_LIGHTNING)
if counter >= ts_ps_wave_count then
call destroy()
endif
endmethod
static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
set .x = GetSpellTargetX()
set .y = GetSpellTargetY()
set .counter = 0
set .t = NewTimerEx(this)
set .damage = getDamageToApply()
call PlayTimedSound("war3mapImported\\PsionicStorm.wav", GetSpellTargetLoc(), 0.5)
call TimerStart(.t, ts_ps_wave_delay, true, function thistype.tick)
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(TS_PS_ABILITY_ID, function thistype.onCast)
endmethod
endstruct
endscope
globals
constant real HH_NINJA_STRIKE_INVIS_BONUS = 1.25
constant integer HH_NINJA_STRIKE_COMBO_ABILITY = 'A01A'
constant integer HH_NINJA_STRIKE_COMBO_BUFF = 'B01Q'
constant integer HH_SHADOW_STEP_ABILITY = 'A01A'
constant integer HH_NINJA_STRIKE_ABILITY = 'A01C'
constant integer HH_INVISIBILITY_ABILITY = 'Agho' //Undead - Shade's Ghost ability
constant real HH_LIVING_SHADOW_ABILITY_DAMAGE_PERCENT = 0.50
constant real HH_LIVING_SHADOW_STEP_DURATION = 6.0
//When gaining momentum, timer is set to HH_MOMENTUM_BUILDUP_TIME, after that, 1 point is lost per HH_MOMENTUM_FALLOFF_TIMER seconds.
constant real HH_MOMENTUM_BUILDUP_TIME = 6.0
constant real HH_MOMENTUM_FALLOFF_TIMER = 0.5
//Bonus per momentum
constant real HH_MOMENTUM_MANA_REG = 0.15
constant real HH_MOMENTUM_HP_REG = 0.15
constant real HH_MOMENTUM_ATTACK_SPEED = 0.03
constant real HH_MOMENTUM_MOVE_SPEED = 3.0
constant real HH_MOMENTUM_CRIT_CHANCE = 0.002
integer hh_momentum = 0
timer hh_ninjastrike_combo_timer
timer hh_invisibility_timer
endglobals
function HHInitUtils takes nothing returns nothing
set hh_ninjastrike_combo_timer = null
set hh_invisibility_timer = CreateTimer()
endfunction
//----------------- MOMENTUM -----------------
/*
function HHMomentumFalloff takes nothing returns nothing
call AddUnitBonus(udg_hh_hero, BonusAttackSpeed.typeid, -HH_MOMENTUM_ATTACK_SPEED)
call AddUnitBonus(udg_hh_hero, BonusMovementSpeed.typeid, -HH_MOMENTUM_MOVE_SPEED)
call AddUnitBonus(udg_hh_hero, BonusCriticalChance.typeid, -HH_MOMENTUM_CRIT_CHANCE)
set hh_momentum = hh_momentum - 1
if hh_momentum == 0 then
call PauseTimer(hh_momentum_timer)
else
call TimerStart(hh_momentum_falloff_timer, HH_MOMENTUM_FALLOFF_TIMER, false, function HHMomentumFalloff)
endif
endfunction
function HHMomentumTick takes nothing returns nothing
local real textsizeAdjustment = RMaxBJ(1.0, 0.50 + hh_momentum * 0.02)
call RestoreHpWithText(udg_hh_hero, hh_momentum * HH_MOMENTUM_HP_REG, textsizeAdjustment)
call RestoreManaWithText(udg_hh_hero, hh_momentum * HH_MOMENTUM_MANA_REG, textsizeAdjustment)
endfunction
function HHMomentumGain takes integer amount returns nothing
call TimerStart(hh_momentum_falloff_timer, HH_MOMENTUM_BUILDUP_TIME, false, function HHMomentumFalloff)
if hh_momentum == 0 then
call TimerStart(hh_momentum_timer, 1.0, true, function HHMomentumTick)
endif
set hh_momentum = hh_momentum + amount
call AddUnitBonus(udg_hh_hero, BonusAttackSpeed.typeid, amount * HH_MOMENTUM_ATTACK_SPEED)
call AddUnitBonus(udg_hh_hero, BonusMovementSpeed.typeid, amount * HH_MOMENTUM_MOVE_SPEED)
call AddUnitBonus(udg_hh_hero, BonusCriticalChance.typeid, amount * HH_MOMENTUM_CRIT_CHANCE)
endfunction
*/
function HHIsInvis takes nothing returns boolean
return GetUnitAbilityLevel(udg_hh_hero, HH_INVISIBILITY_ABILITY) != 0
endfunction
function HHEndInvisibility takes nothing returns nothing
call UnitRemoveAbility(udg_hh_hero, HH_INVISIBILITY_ABILITY)
if GetLocalPlayer() == GetOwningPlayer(udg_hh_hero) then
call DisplayCineFilter(false) //Turn filter off
endif
endfunction
function HHStartInvisibility takes real duration returns nothing
local real oldDuration
if HHIsInvis() then
set duration = RMaxBJ(TimerGetRemaining(hh_invisibility_timer), duration)
else
call UnitAddAbility(udg_hh_hero, HH_INVISIBILITY_ABILITY)
if GetLocalPlayer() == GetOwningPlayer(udg_hh_hero) then
//call CinematicFilterGenericBJ( 2, BLEND_MODE_BLEND, "ReplaceableTextures\\CameraMasks\\DreamFilter_Mask.blp", 100, 100, 100, 100, 0, 0, 0, 60.00 )
//Setup Filter
call SetCineFilterTexture("ReplaceableTextures\\CameraMasks\\DreamFilter_Mask.blp")
call SetCineFilterBlendMode(BLEND_MODE_BLEND)
call SetCineFilterTexMapFlags(TEXMAP_FLAG_NONE)
call SetCineFilterStartUV(0, 0, 1, 1)
call SetCineFilterEndUV(0, 0, 1, 1)
call SetCineFilterStartColor(0, 0, 0, 255)
call SetCineFilterEndColor(0, 0, 0, 100) //255 is fully opaque, 0 is fully transparent
call SetCineFilterDuration(0.25)
call DisplayCineFilter(true) //Turn filter ON
endif
endif
call TimerStart(hh_invisibility_timer, duration, false, function HHEndInvisibility)
endfunction
function HHLivingShadowHpPercent takes integer levelOfAbility returns real
return 0.50
endfunction
function HHLivingShadowArmorPercent takes integer levelOfAbility returns real
return 1.0
endfunction
function HHLivingShadowDamagePercent takes integer levelOfAbility returns real
return 0.20 + 0.03 * levelOfAbility
endfunction
function HHLivingShadowSpellPowerPercent takes integer levelOfAbility returns real
return 0.25 + 0.05 * levelOfAbility
endfunction
function HHLivingShadowHealBase takes integer levelOfAbility returns real
return 50.0 + 10.0 * levelOfAbility
endfunction
function HHLivingShadowHealMissingHp takes integer levelOfAbility returns real
return 0.01 * levelOfAbility
endfunction
function HHLivingShadowHealDmgPercent takes integer levelOfAbility returns real
return 0.20 + 0.03 * levelOfAbility
endfunction
//This is called when expired or killed
function HHLivingShadowCleanup takes unit u returns nothing
call GroupRemoveUnit( udg_hh_living_shadows, u )
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Undead\\CarrionSwarm\\CarrionSwarmDamage.mdl", GetUnitX(u), GetUnitY(u)))
call ReleaseTimer(udg_hh_living_shadows_timers[GetUnitUserData(u)])
call RemoveUnit( u )
if ( CountUnitsInGroup(udg_hh_living_shadows) == 0 ) then
call SetPlayerTechResearched( GetOwningPlayer(udg_hh_hero), 'R00A', 0 )
endif
endfunction
function HHLivingShadowExpire takes nothing returns nothing
call HHLivingShadowCleanup(udg_UDexUnits[GetTimerData(GetExpiredTimer())])
endfunction
function HHLivingShadowGetRemaining takes unit u returns real
return TimerGetRemaining(udg_hh_living_shadows_timers[GetUnitUserData(u)])
endfunction
function HHLivingShadowSetRemaining takes unit u, real remaining returns nothing
call TimerStart(udg_hh_living_shadows_timers[GetUnitUserData(u)], remaining, false, function HHLivingShadowExpire)
endfunction
function HHCreateLivingShadow takes unit source, real x, real y, real faceing, real duration returns nothing
local integer levelOfAbility = GetUnitAbilityLevel(source, HH_SHADOW_STEP_ABILITY)
local real hpPercent = HHLivingShadowHpPercent(levelOfAbility)
local real armorPercent = HHLivingShadowArmorPercent(levelOfAbility)
local integer hp = R2I(hpPercent * BlzGetUnitMaxHP(source))
local real armor = armorPercent * BlzGetUnitArmor(source)
local integer damage = R2I(HHLivingShadowDamagePercent(levelOfAbility) * GetTotalDamage(source) + HHLivingShadowSpellPowerPercent(levelOfAbility) * GetSpellPower(source))
local unit livingShadow = CreateUnit(GetOwningPlayer(source), 'h000', x, y, faceing) //GetUnitFacing(udg_hh_hero)
local effect vfx = AddSpecialEffect("Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl", x, y)
local integer index = GetUnitUserData(livingShadow)
local timer t = NewTimerEx(index)
//call BJDebugMsg("Living Shadow, dur=" + R2S(duration))
set udg_hh_living_shadows_timers[index] = t
set t = null
call SetPlayerTechResearched( GetOwningPlayer(udg_hh_hero), 'R00A', 1 )
call BlzSetSpecialEffectColor( vfx, 0, 0, 0 )
call DestroyEffect( vfx )
set vfx = null
call SetUnitVertexColor( livingShadow, 140, 140, 140, 128 )
call HHLivingShadowSetRemaining(livingShadow, duration)
call BlzSetUnitMaxHP( livingShadow, hp )
call SetUnitState(livingShadow, UNIT_STATE_LIFE, I2R(hp))
call BlzSetUnitArmor( livingShadow, armor )
call BlzSetUnitBaseDamage( livingShadow, damage, 0 )
call GroupAddUnit(udg_hh_living_shadows, livingShadow)
set bj_lastCreatedUnit = livingShadow
set livingShadow = null
endfunction
//-------------------------------------------------------------------
function HHNinjaStrikeDamageBase takes integer levelOfAbility returns real
return 55.0 + 15.0 * levelOfAbility
endfunction
function HHNinjaStrikeDamagePercent takes integer levelOfAbility returns real
return 0.55 + 0.15 * levelOfAbility
endfunction
function HHNinjaStrikeSpellPowerPercent takes integer levelOfAbility returns real
return 0.70 + 0.08 * levelOfAbility
endfunction
//--------------------------------------------------------------------
function HHNinjaStrikeComboOnHit_Cleanup takes nothing returns nothing
call UnitRemoveAbility(udg_hh_hero, HH_NINJA_STRIKE_COMBO_ABILITY)
call UnitRemoveAbility(udg_hh_hero, HH_NINJA_STRIKE_COMBO_BUFF)
call ReleaseTimer(hh_ninjastrike_combo_timer)
set hh_ninjastrike_combo_timer = null
endfunction
function HHNinjaStrikeComboOnHit_Expire takes nothing returns nothing
local timer t = GetExpiredTimer()
call ReleaseTimer(t)
call RemoveCustomOnHit(udg_hh_hero, NinjaStrikeComboOnHit.typeid)
call HHNinjaStrikeComboOnHit_Cleanup()
set t = null
endfunction
struct NinjaStrikeComboOnHit extends OnHit
method onHit takes unit source, integer hitCounter returns real
if not this.HasFiered() then
call this.SetHasFiered()
call HHNinjaStrikeComboOnHit_Cleanup()
call RestoreManaWithText(udg_hh_hero, 10.0, 1.0)
//allow danage bonus to work if 2 ninja strike in a row happens (I.E. AoE attack)
call TimerStart(NewTimer(), 0.001, false, function HHNinjaStrikeComboOnHit_Expire)
endif
set Damage.amount = 1.2 * Damage.amount
return 0.0
endmethod
endstruct
function HHAddNinjaStrikeCombo takes nothing returns nothing
if hh_ninjastrike_combo_timer == null then
set hh_ninjastrike_combo_timer = NewTimer()
call UnitAddAbility(udg_hh_hero, HH_NINJA_STRIKE_COMBO_ABILITY)
call AddCustomOnHit(udg_hh_hero, NinjaStrikeComboOnHit.create())
endif
call TimerStart(hh_ninjastrike_combo_timer, 4.0, false, function HHNinjaStrikeComboOnHit_Expire)
endfunction
scope HHShuriken
globals
constant real HH_SHURIKEN_INVIS_BONUS = 1.25
constant real HH_SHURIKEN_MINIMUM_DMG_PERCENT = 0.30
constant real HH_SHURIKEN_REDUCTION_PER_TARGET = 0.15
constant real HH_SHURIKEN_DECAY_TIME = 6.0
constant real HH_SHURIKEN_DELAY_BETWEEN_CASTS = 0.25
constant integer HH_SHURIKEN_ABILITY = 'A019'
MissileGroup hh_shurikens
endglobals
function HHShurikenDamageBase takes integer levelOfAbility returns real
return 34.0 + 6.0 * levelOfAbility
endfunction
function HHShurikenDamagePercent takes integer levelOfAbility returns real
return 0.18 + 0.06 * levelOfAbility
endfunction
function HHShurikenSpellPowerPercent takes integer levelOfAbility returns real
return 0.20 + 0.04 * levelOfAbility
endfunction
struct Shuriken extends Missiles
integer targetsHit
timer decayTimer
method pullShuriken takes nothing returns nothing
call deflectTarget(udg_hh_hero)
call pause(false)
set timeScale = 1.0
set targetsHit = 0
call flushAll()
if decayTimer != null then
call ReleaseTimer(decayTimer)
set decayTimer = null
endif
endmethod
static method pullAll takes nothing returns nothing
local MGroup node = hh_shurikens.group.next
local integer i = 0
if hh_shurikens.size > 0 then
loop
exitwhen i == hh_shurikens.size
call Shuriken(node.missile).pullShuriken()
set node = node.next
set i = i + 1
endloop
endif
endmethod
static method decay takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set timeScale = 1.0
call hh_shurikens.remove(this)
call terminate()
set t = null
endmethod
method onPause takes nothing returns boolean
set timeScale = 0.08
set decayTimer = NewTimerEx(this)
call TimerStart(decayTimer, HH_SHURIKEN_DECAY_TIME, false, function thistype.decay)
return false
endmethod
method onRemove takes nothing returns nothing
if decayTimer != null then
call ReleaseTimer(decayTimer)
set decayTimer = null
endif
endmethod
method onFinish takes nothing returns boolean
if .target == null then
call pause(true)
return false
else
return true
endif
endmethod
method onHit takes unit hit returns boolean
local real actualDamage
local real minimumDamage
if .target == hit then
//target is null until pulling back. target -> hero when pulling
call hh_shurikens.remove(this)
return true
elseif IsUnitEnemy(hit, owner) then
set actualDamage = damage * (1.0 - HH_SHURIKEN_REDUCTION_PER_TARGET * targetsHit)
set minimumDamage = damage * HH_SHURIKEN_MINIMUM_DMG_PERCENT
if actualDamage < minimumDamage then
set actualDamage = minimumDamage
endif
call Damage.applySpell(.source, hit, actualDamage, DAMAGE_TYPE_DEATH)
set targetsHit = targetsHit + 1
endif
return false
endmethod
endstruct
struct ShurikenSpell
private real damage
private real angle
private unit source
private integer numberOfMissilesLeft
private method fire takes nothing returns nothing
local real ux = GetUnitX(source)
local real uy = GetUnitY(source)
local real uz = GetZHeight(ux, uy) + 64.0
local real tx = ux + 850.0 * Cos(angle + numberOfMissilesLeft * bj_DEGTORAD * 4.5)
local real ty = uy + 850.0 * Sin(angle + numberOfMissilesLeft * bj_DEGTORAD * 4.5)
local real tz = GetZHeight(tx, ty) + 64.0
local Shuriken missile = Shuriken.create(ux, uy, uz, tx, ty, tz)
set missile.source = source
set missile.model = "Units\\ShadowDancer\\Shuriken.mdx"
set missile.speed = 925.0
set missile.collision = 32.0
set missile.damage = .damage
set missile.owner = GetOwningPlayer(source)
set missile.targetsHit = 0
set missile.decayTimer = null
call hh_shurikens.insert(missile)
call missile.launch()
set numberOfMissilesLeft = numberOfMissilesLeft - 1
endmethod
public static method fireMissiles takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if source != null and UnitAlive(source) then
call fire()
if numberOfMissilesLeft == 0 then
set source = null
call ReleaseTimer(t)
call deallocate()
endif
else
set source = null
call ReleaseTimer(t)
call deallocate()
endif
set t = null
endmethod
public static method throwSingleShuriken takes unit u, real tx, real ty returns nothing
local thistype this = thistype.allocate()
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local integer levelOfAbility = GetUnitAbilityLevel(u, HH_SHURIKEN_ABILITY)
local real damageBase = HHShurikenDamageBase(levelOfAbility) + HHShurikenDamagePercent(levelOfAbility) * GetTotalDamage(u) + HHShurikenSpellPowerPercent(levelOfAbility) * GetSpellPower(u)
local real hhDamage
local group shadows = CreateGroup()
if HHIsInvis() then
set hhDamage = damageBase * HH_SHURIKEN_INVIS_BONUS
else
set hhDamage = damageBase
endif
set .angle = Atan2(ty - uy, tx - ux)
set .source = u
set .numberOfMissilesLeft = 1
set .damage = hhDamage * udg_hh_ability_damage_modifier
call fire()
call deallocate()
set damageBase = damageBase * udg_hh_living_shadow_mirror_percen
call BlzGroupAddGroupFast(udg_hh_living_shadows, shadows)
loop
set u = FirstOfGroup(shadows)
exitwhen u == null
if UnitAlive(u) then
set this = thistype.allocate()
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set .angle = Atan2(ty - uy, tx - ux) - bj_DEGTORAD * 4.5
set .source = u
set .numberOfMissilesLeft = 1
set .damage = damageBase
call fire()
call deallocate()
else
call GroupRemoveUnit(udg_hh_living_shadows, u)
endif
call GroupRemoveUnit(shadows,u)
endloop
call DestroyGroup(shadows)
set u = null
set shadows = null
endmethod
public static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
local unit u = GetTriggerUnit()
local real targetx = GetSpellTargetX()
local real targety = GetSpellTargetY()
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local integer levelOfAbility = GetUnitAbilityLevel(u, HH_SHURIKEN_ABILITY)
local real damageBase = HHShurikenDamageBase(levelOfAbility) + HHShurikenDamagePercent(levelOfAbility) * GetTotalDamage(u) + HHShurikenSpellPowerPercent(levelOfAbility) * GetSpellPower(u)
local real hhDamage
local timer t = NewTimerEx(this)
local group shadows = CreateGroup()
if HHIsInvis() then
set hhDamage = damageBase * HH_SHURIKEN_INVIS_BONUS
else
set hhDamage = damageBase
endif
set .angle = Atan2(targety - uy, targetx - ux) - bj_DEGTORAD * 6.75
set .source = u
set .numberOfMissilesLeft = 4
set .damage = hhDamage * udg_hh_ability_damage_modifier
call fire()
call TimerStart(t, HH_SHURIKEN_DELAY_BETWEEN_CASTS, true, function thistype.fireMissiles)
set damageBase = damageBase * udg_hh_living_shadow_mirror_percen
call BlzGroupAddGroupFast(udg_hh_living_shadows, shadows)
loop
set u = FirstOfGroup(shadows)
exitwhen u == null
if UnitAlive(u) then
set this = thistype.allocate()
set t = NewTimerEx(this)
set ux = GetUnitX(u)
set uy = GetUnitY(u)
set .angle = Atan2(targety - uy, targetx - ux) - bj_DEGTORAD * 6.75
set .source = u
set .numberOfMissilesLeft = 4
set .damage = damageBase
call fire()
call TimerStart(t, HH_SHURIKEN_DELAY_BETWEEN_CASTS, true, function thistype.fireMissiles)
else
call GroupRemoveUnit(udg_hh_living_shadows, u)
endif
call GroupRemoveUnit(shadows,u)
endloop
call DestroyGroup(shadows)
set u = null
set shadows = null
set t = null
endmethod
private static method onInit takes nothing returns nothing
set hh_shurikens = CreateMissileGroup()
call RegisterSpellEffectEvent(HH_SHURIKEN_ABILITY, function thistype.onCast)
endmethod
endstruct
function HHShurikenThrowSingle takes real tx, real ty returns nothing
call ShurikenSpell.throwSingleShuriken(udg_hh_hero, tx, ty)
endfunction
function HHShurikenPull takes nothing returns nothing
call Shuriken.pullAll()
endfunction
endscope
globals
constant real HH_DOUBLE_STRIKE_SHADOW_DURATION = 18.0
constant integer HH_DOUBLE_STRIKE_ABILLITY = 'A01F'
endglobals
scope HHDoubleStrikeAssasination
globals
private constant integer HH_DOUBLE_STRIKE_ANIM_ABILLITY = 'A01H'
private constant integer HH_DOUBLE_STRIKE_AMPLIFY_ABILITY = 'A01G'
private constant integer HH_DOUBLE_STRIKE_AMPLIFY_BUFF = 'B01H'
private constant string HH_DOUBLE_STRIKE_ANIM_ORDER = "bloodlust"
private constant string HH_DOUBLE_STRIKE_VFX = "Units\\ShadowDancer\\Ephemeral Cut Midnight.mdl"
endglobals
//-------------------------------------------------------------------
function HHDoubleStrikeDamageBase takes integer levelOfAbility returns real
return 150.0 + 50.0 * levelOfAbility
endfunction
function HHDoubleStrikeDamagePercent takes integer levelOfAbility returns real
return 1.20 + 0.20 * levelOfAbility
endfunction
function HHDoubleStrikeSpellPowerPercent takes integer levelOfAbility returns real
return 1.25 + 0.25 * levelOfAbility
endfunction
function HHDoubleStrikeAmplificationAmount takes integer levelOfAbility returns real
return 0.10 + 0.10 * levelOfAbility
endfunction
function HHDoubleStrikeDamageTotal takes unit u returns real
local integer levelOfAbility = GetUnitAbilityLevel(u, HH_DOUBLE_STRIKE_ABILLITY)
//local real heroDmg = GetTotalDamage(u)
//local real heroSp = GetSpellPower(u)
//local real dmgBase = HHDoubleStrikeDamageBase(levelOfAbility)
//local real dmgPercent = HHDoubleStrikeDamagePercent(levelOfAbility)
//local real spPercent = HHDoubleStrikeSpellPowerPercent(levelOfAbility)
return HHDoubleStrikeDamageBase(levelOfAbility) + GetTotalDamage(u) * HHDoubleStrikeDamagePercent(levelOfAbility) + GetSpellPower(u) * HHDoubleStrikeSpellPowerPercent(levelOfAbility)
endfunction
private struct DoubleStrikeAssasination
static unit target
static method damageAmplification takes nothing returns nothing
if GetUnitAbilityLevel(Damage.target, HH_DOUBLE_STRIKE_AMPLIFY_BUFF) > 0 then
set Damage.amount = Damage.amount * (1.0 + HHDoubleStrikeAmplificationAmount(GetUnitAbilityLevel(udg_hh_hero, HH_DOUBLE_STRIKE_ABILLITY)))
endif
endmethod
static method removeDamageAmplifier takes nothing returns nothing
local timer t = GetExpiredTimer()
call UnitRemoveAbility(target, HH_DOUBLE_STRIKE_AMPLIFY_ABILITY)
call UnitRemoveAbility(target, HH_DOUBLE_STRIKE_AMPLIFY_BUFF)
call ReleaseTimer(t)
set t = null
endmethod
static method dealDamageFinal takes nothing returns nothing
local timer t = GetExpiredTimer()
local real dmg = HHDoubleStrikeDamageTotal(udg_hh_hero) * udg_hh_ability_damage_modifier
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local effect vfx = AddSpecialEffect(HH_DOUBLE_STRIKE_VFX, tx, ty)
call BlzPlaySpecialEffect(vfx, ANIM_TYPE_BIRTH)
call DestroyEffectLater(vfx, 1.0)
call Damage.applySpell(udg_hh_hero, target, dmg, DAMAGE_TYPE_DEATH)
call AddTimedShield(udg_hh_hero, dmg, 6.0)
call ReleaseTimer(t)
set vfx = null
set t = null
endmethod
static method dealDamageShadow takes nothing returns nothing
local real shadowDamage = HHDoubleStrikeDamageTotal(udg_hh_hero) * udg_hh_living_shadow_mirror_percen
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local effect vfx = AddSpecialEffect(HH_DOUBLE_STRIKE_VFX, tx, ty)
local timer t = NewTimer()
call BlzPlaySpecialEffect(vfx, ANIM_TYPE_BIRTH)
call BlzSetSpecialEffectHeight(vfx, 25)
call DestroyEffectLater(vfx, 1.0)
call Damage.applySpell(udg_hh_hero, target, shadowDamage, DAMAGE_TYPE_DEATH)
if not udg_hh_tripple_shadow_talent then
call UnitAddAbility(target, HH_DOUBLE_STRIKE_AMPLIFY_ABILITY)
endif
call TimerStart(t, 4.0, false, function thistype.removeDamageAmplifier)
set vfx = null
set t = null
endmethod
static method reappear takes nothing returns nothing
local timer t = GetExpiredTimer()
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local real tf = GetUnitFacing(target) * bj_DEGTORAD
local real behindAngle = tf + bj_PI
local real tc = BlzGetUnitCollisionSize(target)
local real distance = tc + BlzGetUnitCollisionSize(udg_hh_hero) + 40.
call ShowUnit(udg_hh_hero, true)
call SetUnitInvulnerable(udg_hh_hero, false)
call SetUnitVertexColor(udg_hh_hero, 255, 255, 255, 255)
call SetUnitX(udg_hh_hero, tx + distance * Cos(behindAngle))
call SetUnitY(udg_hh_hero, ty + distance * Sin(behindAngle))
call UnitAddAbility(udg_hh_hero, HH_DOUBLE_STRIKE_ANIM_ABILLITY)
call IssueTargetOrder(udg_hh_hero, HH_DOUBLE_STRIKE_ANIM_ORDER, target)
call dealDamageShadow()
if (GetLocalPlayer() == GetOwningPlayer(udg_hh_hero)) then
call ClearSelection()
call SelectUnit(udg_hh_hero, true)
endif
call TimerStart(t, 0.5, false, function thistype.dealDamageFinal)
set t = null
endmethod
static method spawnShadow takes nothing returns nothing
local timer t = NewTimer()
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local real tf = GetUnitFacing(target) * bj_DEGTORAD
local real angleToFace = tf + bj_PI
local real tc = BlzGetUnitCollisionSize(target)
local real distance = tc + BlzGetUnitCollisionSize(udg_hh_hero) + 40.
if udg_hh_tripple_shadow_talent then
call HHCreateLivingShadow(udg_hh_hero, GetUnitX(udg_hh_hero), GetUnitY(udg_hh_hero), GetUnitFacing(udg_hh_hero), HH_DOUBLE_STRIKE_SHADOW_DURATION + udg_hh_living_shadow_time_modifier)
endif
call HHCreateLivingShadow(udg_hh_hero, tx + distance * Cos(tf), ty + distance * Sin(tf), angleToFace, HH_DOUBLE_STRIKE_SHADOW_DURATION + udg_hh_living_shadow_time_modifier)
call UnitAddAbility(bj_lastCreatedUnit, HH_DOUBLE_STRIKE_ANIM_ABILLITY)
call IssueTargetOrder(bj_lastCreatedUnit, HH_DOUBLE_STRIKE_ANIM_ORDER, target)
call TimerStart(t, 0.5, false, function thistype.reappear)
set t = null
endmethod
static method onCast takes nothing returns nothing
set target = GetSpellTargetUnit()
call SetUnitInvulnerable(udg_hh_hero, true)
call SetUnitVertexColor(udg_hh_hero, 255, 255, 255, 0)
call ShowUnit(udg_hh_hero, false)
call spawnShadow()
endmethod
endstruct
function HHDoubleStrikeRegisterAbilities takes nothing returns nothing
//Damage Amplification, register to damage engine
local trigger t = CreateTrigger()
call TriggerRegisterDamageEngine(t, "Mod", 2.00)
call TriggerAddCondition(t, Condition(function DoubleStrikeAssasination.damageAmplification))
//Spell Cast callback registering
call RegisterSpellEffectEvent(HH_DOUBLE_STRIKE_ABILLITY, function DoubleStrikeAssasination.onCast)
endfunction
endscope
//
// Prints the most recent handle id.
//
function Trig_HandleCounter_Actions takes nothing returns nothing
local location dummy_location = Location(0,0)
if udg_debug_print_handle then
call BJDebugMsg(I2S(GetHandleId(dummy_location)-0x100000))
endif
call RemoveLocation(dummy_location)
set dummy_location = null
endfunction
//===========================================================================
function InitTrig_HandleCounter takes nothing returns nothing
set gg_trg_HandleCounter = CreateTrigger( )
call TriggerRegisterTimerEventPeriodic( gg_trg_HandleCounter, 0.10 )
call TriggerAddAction( gg_trg_HandleCounter, function Trig_HandleCounter_Actions )
endfunction