Name | Type | is_array | initial_value |
library Hammer initializer init
globals
private constant real Tau = 6.283185
private constant integer Ability_Id = 'A000'
private constant integer Dummy_Id = 'e000'
private constant real Dt = 1.0 / 32.0
private constant real Hammer_Duration = 6.0 // in seconds
private constant real Hammer_Rot_Speed = Tau / 1.65 // in radians per second
private constant real Hammer_Outward_Speed = 112.0 // in units per second
private constant real Hammer_Damage_Range = 128.0
private constant real Hammer_Damage_Again_Delay = 0.85
private constant string Hammer_On_Hit_Effect_Model = "Abilities\\Weapons\\GryphonRiderMissile\\GryphonRiderMissile.mdl"
private constant real Hammer_Fly_Height = 96.0
endglobals
private struct Hammer
Hammer prev
Hammer next
integer abi_lvl
unit du
unit caster
player caster_owner
real start_pos_x
real start_pos_y
real pos_x
real pos_y
real outward_radius
real rot_dir
integer duration // in ticks per second
endstruct
native UnitAlive takes unit u returns boolean
private function target_allowed takes unit target, Hammer h returns boolean
return UnitAlive(target) and IsUnitEnemy(target, h.caster_owner)
endfunction
private function hammer_damage_target takes Hammer h, unit target returns nothing
local boolean melee_attack = false
local boolean range_attack = true
local attacktype attack_type = ATTACK_TYPE_NORMAL // spell
local damagetype damage_type = DAMAGE_TYPE_UNIVERSAL // ignore armor value
local weapontype weapon_type = WEAPON_TYPE_WHOKNOWS
local real damage
set damage = 25.0 + 25.0*h.abi_lvl
set damage = damage + 2.0 * GetHeroStr(h.caster, /*include_bonuses:*/ true)
if IsUnitType(target, UNIT_TYPE_UNDEAD) then
// evil beware!
set damage = 2.0 * damage
endif
call UnitDamageTarget(h.caster, target, damage, melee_attack, range_attack, attack_type, damage_type, weapon_type)
endfunction
// These should suffice, but we should probably use a dummy recycling library instead.
//
globals
private unit dummy_spawn_result
endglobals
private function dummy_spawn takes player caster_owner, real start_pos_x, real start_pos_y returns unit
set dummy_spawn_result = CreateUnit(caster_owner, Dummy_Id, start_pos_x, start_pos_y, 0.0)
call SetUnitFlyHeight(dummy_spawn_result, Hammer_Fly_Height, 0.0)
return dummy_spawn_result
endfunction
private function dummy_remove takes unit dummy returns nothing
call RemoveUnit(dummy)
endfunction
// We need to save the time when a unit was last damaged by a hammer.
//
globals
private hashtable ht = InitHashtable()
endglobals
private function unit_get_last_time_damaged_by_hammer takes unit u, Hammer hammer returns real
return LoadReal(ht, integer(hammer), GetHandleId(u))
endfunction
private function unit_set_last_time_damaged_by_hammer takes unit u, Hammer hammer, real value returns nothing
call SaveReal(ht, integer(hammer), GetHandleId(u), value)
endfunction
private function hammer_clear_units_last_damaged_times takes Hammer hammer returns nothing
call FlushChildHashtable(ht, integer(hammer))
endfunction
private function hammer_spawn takes unit caster returns Hammer
local Hammer h = Hammer.create() // Hammer.allocate()
local real start_pos_x = GetUnitX(caster)
local real start_pos_y = GetUnitY(caster)
set h.abi_lvl = GetUnitAbilityLevel(caster, Ability_Id)
set h.caster = caster
set h.caster_owner = GetOwningPlayer(caster)
set h.du = dummy_spawn(h.caster_owner, start_pos_x, start_pos_y)
set h.start_pos_x = start_pos_x
set h.start_pos_y = start_pos_y
set h.pos_x = start_pos_x
set h.pos_y = start_pos_y
set h.outward_radius = 0.0
set h.rot_dir = 0.0
set h.duration = R2I(Hammer_Duration * (1.0 / Dt))
return h
endfunction
private function hammer_remove takes Hammer h returns nothing
set h.caster = null
call dummy_remove(h.du)
call hammer_clear_units_last_damaged_times(h)
call h.destroy() // h.deallocate()
endfunction
globals
private real now = 0.0
endglobals
private function update_now takes nothing returns nothing
set now = now + 0.03125 // 1.0 / 32.0
endfunction
globals
private group targets = CreateGroup()
endglobals
private function deal_damage_in_range takes Hammer h, real range returns nothing
local unit u
local real t
call GroupEnumUnitsInRange(targets, h.pos_x, h.pos_y, 192.0 + Hammer_Damage_Range, /*filter:*/ null)
call GroupRemoveUnit(targets, h.caster)
loop
set u = FirstOfGroup(targets)
exitwhen u == null
call GroupRemoveUnit(targets, u)
if IsUnitInRangeXY(u, h.pos_x, h.pos_y, Hammer_Damage_Range) and target_allowed(u, h) then
set t = unit_get_last_time_damaged_by_hammer(u, h)
if now - t >= Hammer_Damage_Again_Delay then
call unit_set_last_time_damaged_by_hammer(u, h, now)
call hammer_damage_target(h, u)
if UnitAlive(u) then
call DestroyEffect(AddSpecialEffectTarget(Hammer_On_Hit_Effect_Model, u, "head"))
else
call DestroyEffect(AddSpecialEffect(Hammer_On_Hit_Effect_Model, GetUnitX(u), GetUnitY(u)))
endif
endif
endif
endloop
endfunction
globals
// the sentinel node of the doubly linked list of all hammers
private Hammer hammers = Hammer(0)
// implicitly we have
// hammers.prev = hammers
// hammers.next = hammers
private timer ticker = CreateTimer()
endglobals
private function hammer_do_every_tick takes Hammer h returns nothing
set h.pos_x = h.start_pos_x + Dt * h.outward_radius * Cos(h.rot_dir)
set h.pos_y = h.start_pos_y + Dt * h.outward_radius * Sin(h.rot_dir)
set h.rot_dir = h.rot_dir + Dt * Hammer_Rot_Speed
set h.outward_radius = h.outward_radius + Hammer_Outward_Speed
call SetUnitX(h.du, h.pos_x)
call SetUnitY(h.du, h.pos_y)
call SetUnitFacing(h.du, h.rot_dir * 57.295779 + 90.0)
call deal_damage_in_range(h, Hammer_Damage_Range)
endfunction
private function hammers_do_every_tick takes nothing returns nothing
local Hammer h
local Hammer next
set h = hammers.next
if h == hammers then
call PauseTimer(ticker)
return
endif
loop
exitwhen h == hammers
if h.duration == 0 then
set next = h.next
set h.prev.next = h.next
set h.next.prev = h.prev
call hammer_remove(h)
set h = next
else
set h.duration = h.duration - 1
call hammer_do_every_tick(h)
set h = h.next
endif
endloop
endfunction
private function on_spell_effect takes nothing returns nothing
local Hammer h
if Ability_Id != GetSpellAbilityId() then
return
endif
set h = hammer_spawn(GetTriggerUnit())
set h.prev = hammers.prev
set h.next = hammers
set h.prev.next = h
set h.next.prev = h
call hammer_do_every_tick(h)
if h.prev == hammers then
call TimerStart(ticker, Dt, true, function hammers_do_every_tick)
endif
endfunction
private function init takes nothing returns nothing
local trigger t
call TimerStart(CreateTimer(), Dt, true, function update_now)
// should probably use a "better" on ability spell effect dispatch mechanism
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t, function on_spell_effect)
endfunction
endlibrary
library playground initializer init
globals
unit hero
endglobals
globals
group blues_units = CreateGroup()
endglobals
function spawn takes integer n returns nothing
local real x
local real y
local real ur
local real a
local unit u
local integer array uids
local integer uids_count
local integer uid
set uids[1] = 'hfoo'
set uids[2] = 'ugho'
set uids[3] = 'nskf'
set uids_count = 3
call GroupEnumUnitsOfPlayer(blues_units, Player(1), null)
loop
set u = FirstOfGroup(blues_units)
exitwhen u == null
call GroupRemoveUnit(blues_units, u)
call RemoveUnit(u)
endloop
loop
set a = GetRandomReal(0.0, 1.0) * 2.0*bj_PI
set ur = GetRandomReal(0.0, 1.0) + GetRandomReal(0.0, 1.0)
if ur > 1.0 then
set ur = 2.0 - ur
endif
set x = 1000.0 * ur * Cos(a)
set y = 1000.0 * ur * Sin(a)
set uid = uids[GetRandomInt(1, uids_count)]
set u = CreateUnit(Player(1), uid, x, y, GetRandomReal(0.0, 360.0))
if uid == 'nskf' then
call SetUnitScale(u, 1.0, 0.0, 0.0)
endif
call IssueTargetOrder(u, "attack", hero)
set n = n - 1
exitwhen n == 0
endloop
endfunction
function playground takes nothing returns nothing
local player red
local player blue
local real dist
set hero = CreateUnit(Player(0), 'Hpal', 0.0, 0.0, 270.0)
call SetHeroLevel(hero, 5, /*show_eye_candy:*/ false)
set red = Player(0)
set blue = Player(1)
call SetPlayerAlliance(blue, red, ALLIANCE_SHARED_VISION, true)
call SetPlayerAlliance(blue, red, ALLIANCE_SHARED_CONTROL, true)
set dist = 768.0
call CreateUnit(blue, 'hfoo', dist, 0.0, 270.0)
call CreateUnit(blue, 'hfoo', 0.0, dist, 270.0)
call CreateUnit(blue, 'ugho', -dist, 0.0, 270.0)
call CreateUnit(blue, 'ugho', 0.0, -dist, 270.0)
endfunction
function hints takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.0, 0.0, 5.0, "type 's <number-of-units>' to spawn more enemies")
endfunction
function on_input takes nothing returns nothing
call ExecuteFunc("tokenize")
if T[1] == "s" then
call spawn(S2I(T[2]))
call UnitResetCooldown(hero)
call SetUnitState(hero, UNIT_STATE_LIFE, GetUnitState(hero, UNIT_STATE_MAX_LIFE))
call SetUnitState(hero, UNIT_STATE_MANA, GetUnitState(hero, UNIT_STATE_MAX_MANA))
endif
endfunction
globals
string array T
integer T_count = 0
endglobals
function tokenize takes nothing returns nothing
local string s = GetEventPlayerChatString()
local integer si
local integer i
local string ch
set T_count = 0
set i = 0
loop
loop
set ch = SubString(s, i, i + 1)
exitwhen ch != " "
set i = i + 1
endloop
exitwhen ch == ""
set si = i
loop
set i = i + 1
set ch = SubString(s, i, i + 1)
exitwhen ch == " " or ch == ""
endloop
set T_count = T_count + 1
set T[T_count] = SubString(s, si, i)
endloop
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "", false)
call TriggerAddAction(t, function on_input)
call playground()
call hints()
endfunction
endlibrary