Name | Type | is_array | initial_value |
library PillarOfLight initializer init
globals
private integer Ability_Id = 'A000'
private integer Buff_Id = 'A001'
private integer Pillar_Id = 'e000'
private real Dt = 1.0 / 32.0
private real Pillar_Damage_Again_Delay = 1.0 // in seconds
private real Fire_Damage_Again_Delay = 1.0 // in seconds
endglobals
globals
private constant integer Pillar = 1
private constant integer Fire = 2
endglobals
private struct Any
// common
Any prev
Any next
integer abi_lvl
unit u
integer duration
integer ty
// Pillar
integer ref_count // we keep the Pillar instance around until all the Fire instances have expired
unit caster
player caster_owner
real range
// SelectionCircle sc
real max_dist_sq
// Fire
Any pillar
effect effect
integer initial_duration
integer damage_delay
static method create takes integer ty returns Any
local Any a = allocate()
set a.ty = ty
return a
endmethod
endstruct
private function pillar_duration takes Any p returns real
return 5.0 + 2.0*p.abi_lvl
endfunction
private function pillar_movement_speed takes Any p returns real
return 250.0 + 50.0*p.abi_lvl
endfunction
private function pillar_range takes Any p returns real
return 64.0
endfunction
private function pillar_max_distance_away_from_caster takes Any p returns real
return 0.0 // no limit
// return 1200.0
endfunction
private function fire_duration takes Any f returns real
return 3.0 + f.pillar.abi_lvl*1.0
endfunction
native UnitAlive takes unit u returns boolean
private function target_allowed takes unit target, Any p returns boolean
return UnitAlive(target) and IsUnitEnemy(target, p.caster_owner)
endfunction
private function pillar_damage_target takes Any p, 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 = 50.0 + 50.0*p.abi_lvl
// set damage = damage + 2.0 * GetHeroInt(p.caster, /*include_bonuses:*/ true)
// if IsUnitType(target, UNIT_TYPE_UNDEAD) then
// set damage = 2.0 * damage
// endif
call UnitDamageTarget(p.caster, target, damage, melee_attack, range_attack, attack_type, damage_type, weapon_type)
endfunction
private function fire_damage_target takes Any f, 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 = 0.0 + 10.0*f.pillar.abi_lvl
set damage = 20.0
call UnitDamageTarget(f.pillar.caster, target, damage, melee_attack, range_attack, attack_type, damage_type, weapon_type)
endfunction
globals
private constant boolean Show_Emotes = true
private string array emotes
private integer emotes_count
endglobals
static if Show_Emotes then
private function emotes_init takes nothing returns nothing
set emotes[1] = "The Light shall burn you!"
set emotes[2] = "Holy Light!"
set emotes[3] = "The Light shines this day!"
set emotes[4] = "I will command the light."
set emotes[5] = "Light smiles upon the just!"
set emotes[6] = "The Light does not betray me!"
set emotes_count = 6
endfunction
private function show_emote takes unit caster returns nothing
local texttag tt = CreateTextTag()
call SetTextTagText(tt, emotes[GetRandomInt(1, emotes_count)], 11.0 * 0.0023)
call SetTextTagPosUnit(tt, caster, 16.0)
call SetTextTagVisibility(tt, true)
call SetTextTagPermanent(tt, false)
call SetTextTagLifespan(tt, 4.0)
call SetTextTagFadepoint(tt, 2.5)
call SetTextTagColor(tt, 255, 255, 100, 255)
endfunction
endif
globals
private integer now = 0
endglobals
private function update_now takes nothing returns nothing
set now = now + 1
endfunction
globals
private hashtable ht = InitHashtable()
endglobals
private function unit_set_pillar takes unit u, Any p returns nothing
call SaveInteger(ht, -1, GetHandleId(u), p)
endfunction
private function unit_get_pillar takes unit u returns Any
return LoadInteger(ht, -1, GetHandleId(u))
endfunction
private function unit_remove_pillar takes unit u returns nothing
call RemoveSavedInteger(ht, -1, GetHandleId(u))
endfunction
private function unit_set_fire takes unit u, Any f returns nothing
call SaveInteger(ht, -2, GetHandleId(u), f)
endfunction
private function unit_get_fire takes unit u returns Any
return LoadInteger(ht, -2, GetHandleId(u))
endfunction
private function unit_remove_fire takes unit u returns nothing
call RemoveSavedInteger(ht, -2, GetHandleId(u))
endfunction
private function unit_set_last_time_damaged_by_pillar takes unit u, Any p, integer value returns nothing
call SaveInteger(ht, integer(p), GetHandleId(u), value)
endfunction
private function unit_get_last_time_damaged_by_pillar takes unit u, Any p returns integer
return LoadInteger(ht, integer(p), GetHandleId(u))
endfunction
private function pillar_clear_units_last_damaged_times takes Any p returns nothing
call FlushChildHashtable(ht, integer(p))
endfunction
globals
private timer ticker = CreateTimer()
private code update_cb = null // set in the init function
private group targets = CreateGroup()
private constant integer Pillar_Damage_Again_Delay_Ticks = R2I(Pillar_Damage_Again_Delay * (1.0 / Dt))
private constant integer Fire_Damage_Again_Delay_Ticks = R2I(Fire_Damage_Again_Delay * (1.0 / Dt))
endglobals
// implicitly we have
// Any(0) == Any(0).prev == Any(0).next
// which makes Any(0) a sentinel node of a doubly linked list
//
private function add_to_update_list takes Any a returns nothing
set a.prev = Any(0).prev
set a.next = Any(0)
set a.prev.next = a
set a.next.prev = a
if a.prev == Any(0) then
call TimerStart(ticker, Dt, true, update_cb)
endif
endfunction
private function remove_from_update_list takes Any a returns nothing
set a.prev.next = a.next
set a.next.prev = a.prev
if Any(0).prev == Any(0) then
call PauseTimer(ticker)
endif
endfunction
private function fire_create takes unit target, Any pillar returns Any
local Any f = Any.create(Fire)
set f.u = target
call unit_set_fire(target, f)
set f.pillar = pillar
set pillar.ref_count = pillar.ref_count + 1
call UnitAddAbility(target, Buff_Id)
set f.effect = AddSpecialEffectTarget("Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdx", target, "chest")
set f.duration = R2I(fire_duration(f) * 1.0 / Dt)
set f.initial_duration = f.duration
set f.damage_delay = 0
call add_to_update_list(f)
return f
endfunction
private function pillar_spawn takes unit caster, real x, real y returns Any
local Any p = Any.create(Pillar)
set p.caster = caster
call unit_set_pillar(caster, p)
set p.ref_count = 1
set p.abi_lvl = GetUnitAbilityLevel(caster, Ability_Id)
set p.caster_owner = GetOwningPlayer(caster)
set p.u = CreateUnit(p.caster_owner, Pillar_Id, x, y, 270.0)
call SetUnitTimeScale(p.u, 0.66)
call SetUnitScale(p.u, 1.7, 1.7, 1.7)
set p.duration = R2I(pillar_duration(p) * (1.0 / Dt))
call SetUnitMoveSpeed(p.u, pillar_movement_speed(p))
set p.range = pillar_range(p)
set p.max_dist_sq = Pow(pillar_max_distance_away_from_caster(p), 2.0)
// set p.sc = SelectionCircle.attach_to_unit(p.u, p.range, 0x00FF00FF)
call add_to_update_list(p)
return p
endfunction
private function pillar_dec_ref_count takes Any p returns nothing
set p.ref_count = p.ref_count - 1
if p.ref_count == 0 then
set p.caster = null
call p.destroy()
endif
endfunction
private function pillar_remove takes Any p returns nothing
call pillar_clear_units_last_damaged_times(p)
call remove_from_update_list(p)
call RemoveUnit(p.u)
set p.u = null
call unit_remove_pillar(p.caster)
// call p.sc.destroy()
call pillar_dec_ref_count(p)
endfunction
private function fire_remove takes Any f returns nothing
call remove_from_update_list(f)
call DestroyEffect(f.effect)
set f.effect = null
call unit_remove_fire(f.u)
call UnitRemoveAbility(f.u, Buff_Id)
set f.u = null
call pillar_dec_ref_count(f.pillar)
call f.destroy()
endfunction
public function on_dispel takes unit u returns nothing
local Any f = unit_get_fire(u)
if f != 0 then
call fire_remove(f)
endif
endfunction
private function fire_on_tick takes Any f returns nothing
if f.damage_delay == 0 then
set f.damage_delay = Fire_Damage_Again_Delay_Ticks
call fire_damage_target(f, f.u)
endif
set f.damage_delay = f.damage_delay - 1
endfunction
private function pillar_on_tick takes Any p returns nothing
local unit u
local integer t
local Any f
local real px = GetUnitX(p.u)
local real py = GetUnitY(p.u)
local real cx = GetUnitX(p.caster)
local real cy = GetUnitY(p.caster)
local real dx
local real dy
// the caster always faces the pillar
call SetUnitFacing(p.caster, Atan2(py - cy, px - cx) * 57.295779)
if p.max_dist_sq != 0.0 then
set dx = px - cx
set dy = py - cy
if dx*dx + dy*dy > p.max_dist_sq then
call IssueImmediateOrderById(p.caster, 851993) // hold position, force spell endcast
return
endif
endif
call GroupEnumUnitsInRange(targets, px, py, p.range + 192.0, null)
loop
set u = FirstOfGroup(targets)
exitwhen u == null
call GroupRemoveUnit(targets, u)
if IsUnitInRangeXY(u, px, py, p.range) and target_allowed(u, p) then
set t = unit_get_last_time_damaged_by_pillar(u, p)
if now - t >= Pillar_Damage_Again_Delay_Ticks then
call unit_set_last_time_damaged_by_pillar(u, p, now)
call pillar_damage_target(p, u)
set f = unit_get_fire(u)
if f == 0 then
set f = fire_create(u, p)
call fire_on_tick(f)
else
set f.duration = f.initial_duration
endif
endif
endif
endloop
endfunction
private function on_spell_channel takes nothing returns nothing
local Any p = pillar_spawn(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
call pillar_on_tick(p)
call SelectUnitForPlayerSingle(p.u, p.caster_owner)
static if Show_Emotes then
call show_emote(p.caster)
endif
endfunction
private function on_spell_endcast takes nothing returns nothing
local Any p = unit_get_pillar(GetTriggerUnit())
call SelectUnitForPlayerSingle(p.caster, p.caster_owner)
call IssueImmediateOrderById(p.caster, 851993) // hold position
call pillar_remove(p)
endfunction
private function on_spell takes nothing returns nothing
local eventid ev
if Ability_Id != GetSpellAbilityId() then
return
endif
set ev = GetTriggerEventId()
if ev == EVENT_PLAYER_UNIT_SPELL_CHANNEL then
call on_spell_channel()
elseif ev == EVENT_PLAYER_UNIT_SPELL_ENDCAST then
call on_spell_endcast()
endif
endfunction
private function update takes nothing returns nothing
local Any a
local Any next
set a = Any(0).next
loop
exitwhen a == Any(0)
set next = a.next
if a.ty == Pillar then
if a.duration == 0 then
call IssueImmediateOrderById(a.caster, 851993) // hold position, force spell endcast
else
set a.duration = a.duration - 1
call pillar_on_tick(a)
endif
elseif a.ty == Fire then
if a.duration == 0 then
call fire_remove(a)
else
set a.duration = a.duration - 1
call fire_on_tick(a)
endif
endif
set a = next
endloop
endfunction
private function init takes nothing returns nothing
local trigger t
if Show_Emotes then
call emotes_init()
endif
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddAction(t, function on_spell)
set update_cb = function update
call TimerStart(CreateTimer(), Dt, true, function update_now)
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 a
local unit u
local integer array uids
local integer uids_count
local integer uid
set uids[1] = 'hmpr'
set uids[2] = 'ugho'
set uids[3] = 'edry'
set uids[4] = 'ogru'
set uids_count = 4
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 x = 1000.0 * Cos(a)
set y = 1000.0 * Sin(a)
set uid = uids[GetRandomInt(1, uids_count)]
set u = CreateUnit(Player(1), uid, x, y, GetRandomReal(0.0, 360.0))
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)
call CreateUnit(blue, 'hmkg', 1152.0, 0.0, 270.0)
set dist = 900.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]))
if IsUnitType(hero, UNIT_TYPE_DEAD) then
call ReviveHero(hero, 0.0, 0.0, /*do_eyecandy:*/ false)
endif
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
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i setobjecttype("units")
//! i createobject("ewsp","e000")
//! i makechange(current,"uabi","Avul,Aeth")
//! i makechange(current,"ubui","")
//! i makechange(current,"ucol","0.0")
//! i makechange(current,"uhpm","1")
//! i makechange(current,"uico","ReplaceableTextures\\CommandButtons\\BTNHealingWave.blp")
//! i makechange(current,"umdl","war3mapImported\\pillar-of-light.mdl")
//! i makechange(current,"umvr","1000000000.0")
//! i makechange(current,"umvt","fly")
//! i makechange(current,"unam","Pillar of Light")
//! i makechange(current,"unsf","(Pillar of Light)")
//! i makechange(current,"upgr","")
//! i makechange(current,"utyp","_")
//! i setobjecttype("abilities")
//! i createobject("ANcl","A000")
//! i makechange(current,"Ncl1","1","99999.0")
//! i makechange(current,"Ncl1","2","99999.0")
//! i makechange(current,"Ncl1","3","99999.0")
//! i makechange(current,"Ncl2","1","3")
//! i makechange(current,"Ncl2","2","3")
//! i makechange(current,"Ncl2","3","3")
//! i makechange(current,"Ncl3","1","17")
//! i makechange(current,"Ncl3","2","17")
//! i makechange(current,"Ncl3","3","17")
//! i makechange(current,"Ncl4","1","0.0")
//! i makechange(current,"Ncl4","2","0.0")
//! i makechange(current,"Ncl4","3","0.0")
//! i makechange(current,"Ncl5","1","0")
//! i makechange(current,"Ncl5","2","0")
//! i makechange(current,"Ncl5","3","0")
//! i makechange(current,"Ncl6","1","charm")
//! i makechange(current,"Ncl6","2","charm")
//! i makechange(current,"Ncl6","3","charm")
//! i makechange(current,"aani","channel")
//! i makechange(current,"aart","ReplaceableTextures\\CommandButtons\\BTNHealingWave.blp")
//! i makechange(current,"abpx","0")
//! i makechange(current,"abpy","2")
//! i makechange(current,"acat","")
//! i makechange(current,"acdn","1","0.33")
//! i makechange(current,"acdn","2","0.33")
//! i makechange(current,"acdn","3","0.33")
//! i makechange(current,"achd","0")
//! i makechange(current,"aeat","")
//! i makechange(current,"ahky","Q")
//! i makechange(current,"amcs","1","10")
//! i makechange(current,"amcs","2","10")
//! i makechange(current,"amcs","3","10")
//! i makechange(current,"anam","Pillar of Light")
//! i makechange(current,"aran","1","800.0")
//! i makechange(current,"aran","2","800.0")
//! i makechange(current,"aran","3","800.0")
//! i makechange(current,"arar","ReplaceableTextures\\CommandButtons\\BTNHealingWave.blp")
//! i makechange(current, "aret","\nLearn |cffFFCC00Pillar of Light|r Level %d (|cffFFCC00Q|r)")
//! i makechange(current,"arhk","Q")
//! i makechange(current, "arut","Summon a controllable |cffFFCC00Pillar of Light|r\nthat incinerates any opponents\nstanding within its reach.\n\n|cffFFCC00Level|r: 1\nPillar Damage: 100 every second\nBurn Damage: 20 for 4 seconds\nMovement Speed: 300\nDuration: 7 seconds\n\n|cffFFCC00Level|r: 2\nPillar Damage: 150 every second\nBurn Damage: 20 for 5 seconds\nMovement Speed: 350\nDuration: 9 seconds\n\n|cffFFCC00Level|r: 3\nPillar Damage: 200 every second\nBurn Damage: 20 for 6 seconds\nMovement Speed: 400\nDuration: 11 seconds\n\n")
//! i makechange(current,"ata0","")
//! i makechange(current,"atat","")
//! i makechange(current,"atp1","1"," ")
//! i makechange(current,"atp1","2"," ")
//! i makechange(current,"atp1","3"," ")
//! i makechange(current,"aub1","1","|cffFFCC00Pillar of Light|r\nSummon a controllable |cffFFCC00Pillar of Light|r\nthat incinerates any opponents\nstanding within its reach.\n\n|cffFFCC00Current Skill Level|r: 1\nPillar Damage: 100 every second\nBurn Damage: 20 for 4 seconds\nMovement Speed: 300\nDuration: 7 seconds\n\n")
//! i makechange(current,"aub1","2","|cffFFCC00Pillar of Light|r\nSummon a controllable |cffFFCC00Pillar of Light|r\nthat incinerates any opponents\nstanding within its reach.\n\n|cffFFCC00Current Skill Level|r: 2\nPillar Damage: 150 every second\nBurn Damage: 20 for 5 seconds\nMovement Speed: 350\nDuration: 9 seconds\n\n")
//! i makechange(current,"aub1","3","|cffFFCC00Pillar of Light|r\nSummon a controllable |cffFFCC00Pillar of Light|r\nthat incinerates any opponents\nstanding within its reach.\n\n|cffFFCC00Current Skill Level|r: 3\nPillar Damage: 200 every second\nBurn Damage: 20 for 6 seconds\nMovement Speed: 400\nDuration: 11 seconds\n\n")
//! i createobject("Aasl","A001")
//! i makechange(current,"Slo1","1","0.0")
//! i makechange(current,"aare","1","0.0")
//! i makechange(current,"aart","ReplaceableTextures\\CommandButtons\\BTNHealingWave.blp")
//! i makechange(current,"abuf","1","B000")
//! i makechange(current,"anam","Pillar of Light Buff")
//! i makechange(current,"ansf","")
//! i makechange(current,"atar","1","self")
//! i setobjecttype("buffs")
//! i createobject("Basl","B000")
//! i makechange(current,"fart","ReplaceableTextures\\CommandButtons\\BTNHealingWave.blp")
//! i makechange(current,"fnsf","")
//! i makechange(current,"ftat","")
//! i makechange(current,"ftip","|cffEE2222Pillar of Light|r")
//! i makechange(current,"fube","This unit was burned by |cffFFCC00Pillar of Light|r\nand is taking damage over time.")
//! endexternalblock