library Firewall requires /*
*/ Ht /* http://www.hiveworkshop.com/threads/t-real-x-vs-t-x-real.288833/ */
globals
private constant integer SPELL_ID = 'A000'
private constant string FIRE_MODEL = "Doodads\\Cinematic\\FireRockSmall\\FireRockSmall.mdx"
private constant real TAU = 2.0 * bj_PI
private constant real DT = 1.0 / 32.0
// UnitDamageTarget arguments
private constant boolean MELEE_ATTACK = false
private constant boolean RANGE_ATTACK = true
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL // spell
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL // ignore armor value
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
private function firewall_width_for takes integer level returns real
return (6.0 * 48.0) + (level - 1) * 48.0 // must be multiple of 48.0
endfunction
private function firewall_height_for takes integer level returns real
return (3.0 * 48.0) + (level - 1) * 48.0 // must be multiple of 48.0
endfunction
private function damage_per_second_for takes integer level returns real
return 25.0 + (level - 1) * 30.0
endfunction
private function damage_interval_for takes integer level returns real
// 50.0 dmg per second => 25.0 after 0.5, another 25.0 after 0.5
return 0.5 // seconds
endfunction
private function duration_for takes integer level returns real
return 20.0 // seconds
endfunction
native UnitAlive takes unit u returns boolean
private keyword Firewall
private function targets_allowed takes Firewall this, unit target returns boolean
return UnitAlive(target) /*
*/ and IsUnitEnemy(target, this.caster_owner)
endfunction
globals
private location loc = Location(0.0, 0.0)
private destructable platform
endglobals
private struct Firewall
static thistype array ul
static integer ulc = 0
static integer array xs
static integer array ys
static integer xyc = 0
static Ht ht
static timer tmr = CreateTimer()
static group tmp_group = CreateGroup()
static group swap_group
unit caster
player caster_owner
integer level
region reg
trigger trg
group g
real damage
real dmg_per_interval
integer interval_max_ticks
integer interval_ticks
integer dur_max_ticks
integer dur_ticks
// effect list
effect e
thistype e_prev
thistype e_next
static code on_spell_effect_handler
static code on_enter_leave_region_handler
static code update_loop_handler
private static method onInit takes nothing returns nothing
local trigger t
set ht = Ht.create()
call ExecuteFunc("s__" + "thistype" + "_set_handlers")
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t, on_spell_effect_handler)
endmethod
static method on_spell_effect takes nothing returns nothing
local thistype this
local real cx
local real cy
local real tx
local real ty
local real dist
local real wall_width
local integer wall_width_x48
local integer wall_height_x48
local real x0
local real y0
local real x1
local real y1
local real ang
local real ang_octant
local integer octant
local real ox0
local real oy0
local real ox1
local real oy1
local real ox
local real oy
local real x
local real y
local real z
local integer i
local integer j
local integer i_x0
local integer i_y0
local integer i_x1
local integer i_y1
local integer dx
local integer dy
local integer sx
local integer sy
local integer err
local real ed
local integer e2
local integer x2
local integer y2
local real width
local real duration
local real interval
local real damage
local thistype e_node
if SPELL_ID != GetSpellAbilityId() then
return
endif
call ht.select()
set this = allocate()
set this.caster = GetTriggerUnit()
set this.caster_owner = GetTriggerPlayer()
set this.level = GetUnitAbilityLevel(this.caster, SPELL_ID)
set this.reg = CreateRegion()
set cx = GetUnitX(this.caster)
set cy = GetUnitY(this.caster)
set tx = GetSpellTargetX()
set ty = GetSpellTargetY()
set ang = Atan2(ty - cy, tx - cx)
set ang_octant = ang
if ang_octant < 0.0 then
set ang_octant = ang_octant + TAU
endif
set octant = R2I(ang_octant / (TAU / 8.0))
set cx = cx + Cos(ang) * 96.0
set cy = cy + Sin(ang) * 96.0
set wall_width = firewall_width_for(this.level)
set tx = cx + Cos(ang) * wall_width
set ty = cy + Sin(ang) * wall_width
set x0 = cx / 32.0
set y0 = cy / 32.0
set x1 = tx / 32.0
set y1 = ty / 32.0
// switch to octant 0 from (x0, y0, x1, y1)
if octant == 0 then
set ox0 = x0
set oy0 = y0
set ox1 = x1
set oy1 = y1
elseif octant == 1 then
set ox0 = y0
set oy0 = x0
set ox1 = y1
set oy1 = x1
elseif octant == 2 then
set ox0 = y0
set oy0 = -x0
set ox1 = y1
set oy1 = -x1
elseif octant == 3 then
set ox0 = -x0
set oy0 = y0
set ox1 = -x1
set oy1 = y1
elseif octant == 4 then
set ox0 = -x0
set oy0 = -y0
set ox1 = -x1
set oy1 = -y1
elseif octant == 5 then
set ox0 = -y0
set oy0 = -x0
set ox1 = -y1
set oy1 = -x1
elseif octant == 6 then
set ox0 = -y0
set oy0 = x0
set ox1 = -y1
set oy1 = x1
else // if octant == 7 then
set ox0 = x0
set oy0 = -y0
set ox1 = x1
set oy1 = -y1
endif
set x0 = ox0
set y0 = oy0
set x1 = ox1
set y1 = oy1
set i_x0 = R2I(x0)
set i_y0 = R2I(y0)
set i_x1 = R2I(x1)
set i_y1 = R2I(y1)
set dx = i_x1 - i_x0
if dx < 0 then
set dx = -dx
endif
if i_x0 < i_x1 then
set sx = 1
else
set sx = -1
endif
set dy = i_y1 - i_y0
if dy < 0 then
set dy = -dy
endif
if i_y0 < i_y1 then
set sy = 1
else
set sy = -1
endif
set err = dx - dy // error value e_xy
if dx + dy == 0 then
set ed = 1.0
else
set ed = SquareRoot(dx * dx + dy * dy)
endif
set width = (/*width:*/ 4.0 + 1.0) / 2.0
set xyc = 0
loop
set xyc = xyc + 1
set xs[xyc] = i_x0
set ys[xyc] = i_y0
set e2 = err
set x2 = i_x0
if 2 * e2 >= -dx then
set e2 = e2 + dy
set y2 = i_y0
loop
exitwhen e2 >= ed * width or (i_y1 != y2 and dx <= dy)
set y2 = y2 + sy
set xyc = xyc + 1
set xs[xyc] = i_x0
set ys[xyc] = i_y0
set e2 = e2 + dx
endloop
if i_x0 == i_x1 then
exitwhen true
endif
set e2 = err
set err = err - dy
set i_x0 = i_x0 + sx
endif
if 2 * e2 <= dy then
set e2 = dx - e2
loop
exitwhen e2 >= ed * width or (x1 == x2 and dx >= dy)
set x2 = x2 + sx
set xyc = xyc + 1
set xs[xyc] = i_x0
set ys[xyc] = i_y0
set e2 = e2 + dy
endloop
if i_y0 == i_y1 then
exitwhen true
endif
set err = err + dx
set i_y0 = i_y0 + sy
endif
endloop
// switch from octant zero to (x, y)
set i = 1
if octant == 0 then
loop
exitwhen i > xyc
set ox = xs[i] * 32.0
set oy = ys[i] * 32.0
call RegionAddCell(this.reg, ox, oy)
set i = i + 1
endloop
elseif octant == 1 then
loop
exitwhen i > xyc
set ox = ys[i] * 32.0
set oy = xs[i] * 32.0
call RegionAddCell(this.reg, ox, oy)
set i = i + 1
endloop
elseif octant == 2 then
loop
exitwhen i > xyc
set ox = -ys[i] * 32.0
set oy = xs[i] * 32.0
call RegionAddCell(this.reg, ox, oy)
set i = i + 1
endloop
elseif octant == 3 then
loop
exitwhen i > xyc
set ox = -xs[i] * 32.0
set oy = ys[i] * 32.0
call RegionAddCell(this.reg, ox, oy)
set i = i + 1
endloop
elseif octant == 4 then
loop
exitwhen i > xyc
set ox = -xs[i] * 32.0
set oy = -ys[i] * 32.0
call RegionAddCell(this.reg, ox, oy)
set i = i + 1
endloop
elseif octant == 5 then
loop
exitwhen i > xyc
set ox = -ys[i] * 32.0
set oy = -xs[i] * 32.0
call RegionAddCell(this.reg, ox, oy)
set i = i + 1
endloop
elseif octant == 6 then
loop
exitwhen i > xyc
set ox = ys[i] * 32.0
set oy = -xs[i] * 32.0
call RegionAddCell(this.reg, ox, oy)
set i = i + 1
endloop
else // if octant == 7 then
loop
exitwhen i > xyc
set ox = xs[i] * 32.0
set oy = -ys[i] * 32.0
call RegionAddCell(this.reg, ox, oy)
set i = i + 1
endloop
endif
set wall_width_x48 = R2I(wall_width / 48.0)
set wall_height_x48 = R2I(firewall_height_for(this.level) / 48.0)
// we are the sentinel node
set this.e_prev = this
set this.e_next = this
set i = 0
loop
exitwhen i >= wall_height_x48
set j = 0
loop
exitwhen j >= wall_width_x48
set x = cx + Cos(ang) * (j * 48.0)
set y = cy + Sin(ang) * (j * 48.0)
call MoveLocation(loc, x, y)
set z = GetLocationZ(loc) + (i * 48.0)
set e_node = allocate()
// add special effect with z
set platform = CreateDestructableZ('OTip', x, y, z, 0.0, 1.0, 0)
set e_node.e = AddSpecialEffect(FIRE_MODEL, x, y)
call RemoveDestructable(platform)
set e_node.e_prev = this.e_prev
set e_node.e_next = this.e_next
set this.e_prev = e_node
set e_node.e_prev.e_next = e_node
set e_node.e_next.e_prev = e_node
set j = j + 1
endloop
set i = i + 1
endloop
set this.trg = CreateTrigger()
call TriggerRegisterEnterRegion(this.trg, this.reg, null)
call TriggerRegisterLeaveRegion(this.trg, this.reg, null)
call TriggerAddAction(this.trg, on_enter_leave_region_handler)
if this.g == null then
set this.g = CreateGroup()
endif
set interval = damage_interval_for(this.level)
set duration = duration_for(this.level)
set damage = damage_per_second_for(this.level)
set this.interval_max_ticks = R2I(interval / DT)
set this.interval_ticks = 0
set this.dmg_per_interval = damage / (1.0 / interval)
set this.dur_max_ticks = R2I(duration * (1.0 / DT))
set this.dur_ticks = 0
set ht[ht.h2i(this.trg)].int = this
set ulc = ulc + 1
set ul[ulc] = this
if ulc == 1 then
call TimerStart(tmr, DT, true, update_loop_handler)
endif
set platform = null
endmethod
static method on_enter_leave_region takes nothing returns nothing
local thistype this = ht.select()[ht.h2i(GetTriggeringTrigger())].int
local unit uu = GetTriggerUnit()
local eventid ee = GetTriggerEventId()
if ee == EVENT_GAME_ENTER_REGION then
if targets_allowed(this, uu) then
call GroupAddUnit(this.g, uu)
call UnitDamageTarget(this.caster, uu, this.dmg_per_interval, MELEE_ATTACK, RANGE_ATTACK, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
endif
elseif ee == EVENT_GAME_LEAVE_REGION then
call GroupRemoveUnit(this.g, uu)
endif
set uu = null
endmethod
method destroy2 takes integer i returns nothing
local thistype e_node
local thistype next
set ul[i] = ul[ulc]
set ulc = ulc - 1
if ulc <= 0 then
call PauseTimer(tmr)
endif
set this.caster = null
call RemoveRegion(this.reg)
set this.reg = null
call DestroyTrigger(this.trg)
set this.trg = null
call GroupClear(this.g)
set e_node = this.e_next
if e_node != this then
loop
call DestroyEffect(e_node.e)
set e_node.e = null
set next = e_node.e_next
call e_node.deallocate()
set e_node = next
exitwhen e_node == this.e_next
endloop
endif
call this.deallocate()
endmethod
static method update_loop takes nothing returns nothing
local thistype this
local unit uu
local integer i
set i = 1
loop
exitwhen i > ulc
set this = ul[i]
set this.interval_ticks = this.interval_ticks + 1
if this.interval_ticks >= this.interval_max_ticks then
set this.interval_ticks = 0
loop
set uu = FirstOfGroup(this.g)
exitwhen uu == null
call GroupRemoveUnit(this.g, uu)
call GroupAddUnit(tmp_group, uu)
if targets_allowed(this, uu) then
call UnitDamageTarget(this.caster, uu, this.dmg_per_interval, MELEE_ATTACK, RANGE_ATTACK, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
endif
endloop
set swap_group = this.g
set this.g = tmp_group
set tmp_group = swap_group
endif
set this.dur_ticks = this.dur_ticks + 1
if this.dur_ticks >= this.dur_max_ticks then
call this.destroy2(i)
set i = i - 1
endif
set i = i + 1
endloop
endmethod
static method set_handlers takes nothing returns nothing
set on_spell_effect_handler = function thistype.on_spell_effect
set on_enter_leave_region_handler = function thistype.on_enter_leave_region
set update_loop_handler = function thistype.update_loop
endmethod
endstruct
endlibrary