scope Eruption
struct Dummy
private static constant integer UNIT_CODE = 'h00J'
private static constant real AREA = 700
private static constant real SPEED = 1.7
private static constant real HEIGHT = 600
private static constant string LANDING_EFFECT = "Abilities\\Spells\\Orc\\LiquidFire\\Liquidfire.mdl"
private thistype next
private thistype prev
private static integer count = 0
//dummy unit
private unit dummy
//the distance the dummy unit is about to travel
private real dist
//already travelled distance
private real travelled
private real dx
private real dy
private unit caster
private real damage
private method destroy takes nothing returns nothing
call this.deallocate()
set this.next.prev = this.prev
set this.prev.next = this.next
set count = count - 1
endmethod
static method move takes nothing returns boolean
local thistype this = thistype(0).next
local real x
local real y
local group g
local unit u
loop
exitwhen this == 0
//move dummy units
call SetUnitX(this.dummy, GetUnitX(this.dummy) + this.dx)
call SetUnitY(this.dummy, GetUnitY(this.dummy) + this.dy)
call SetUnitFlyHeight(this.dummy, Parabola(this.dist, HEIGHT, this.travelled), 0)
set this.travelled = this.travelled + SquareRoot(this.dx*this.dx + this.dy*this.dy)
set x = GetUnitX(this.dummy)
set y = GetUnitY(this.dummy)
//if dummy unit has reached its destination, destroy it and damage area
if this.travelled > this.dist then
call DestroyEffect(AddSpecialEffect(LANDING_EFFECT, x, y))
set g = CreateGroup()
call GroupEnumUnitsInRange(g, x, y, 100, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if IsUnitEnemy(u, GetOwningPlayer(this.caster)) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and GetUnitState(u, UNIT_STATE_LIFE) > 0 then
call UnitDamageTarget(this.caster, u, this.damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
endif
call GroupRemoveUnit(g, u)
endloop
call DestroyGroup(g)
call KillUnit(this.dummy)
call this.destroy()
endif
set this = this.next
endloop
set u = null
set g = null
//return true if number of dummy units is 0
return count == 0
endmethod
static method new takes unit caster, real x, real y, real damage returns nothing
local thistype this = thistype.allocate()
local real angle
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
set count = count + 1
set angle = GetRandomReal(0, 2*bj_PI)
set this.dist = GetRandomReal(10, AREA)
set this.dummy = CreateUnit(GetOwningPlayer(caster), UNIT_CODE, x, y, angle)
set this.travelled = 0
set this.damage = damage
set this.caster = caster
set this.dx = Cos(angle) * this.dist * SPEED * 0.01
set this.dy = Sin(angle) * this.dist * SPEED * 0.01
endmethod
endstruct
struct Spell
private static constant integer ABIL_CODE = 'A02E'
private static constant real TIMEOUT = 0.04
//dummy units will be created every Nth iteration
private static constant integer DUMMY_INTERVAL = 20
//number of dummy units created at time
private static constant integer DUMMY_NUMBER = 10
private thistype next
private thistype prev
private static timer iterator = CreateTimer()
private static integer count = 0
private unit caster
private real damage
private boolean channelling = true
private integer dummyInterval = 0
private real x
private real y
private method destroy takes nothing returns nothing
call this.deallocate()
set this.next.prev = this.prev
set this.prev.next = this.next
set count = count - 1
set this.caster = null
endmethod
private static method periodic takes nothing returns nothing
local thistype this = thistype(0).next
local boolean spellEnd = false
local integer i
loop
exitwhen this == 0
//Once every (DUMMY_INTERVAL*TIMEOUT), create DUMMY_NUMBER dummy units
if this.dummyInterval == DUMMY_INTERVAL then
set i = 0
loop
exitwhen i == DUMMY_NUMBER
call Dummy.new(this.caster, this.x, this.y, this.damage)
set i = i + 1
endloop
set this.dummyInterval = 0
else
set this.dummyInterval = this.dummyInterval + 1
endif
if not this.channelling then
call this.destroy()
endif
set this = this.next
endloop
//move all dummy units
set spellEnd = Dummy.move()
//if there are no dummy units and no units casting the spell, stop timer
if spellEnd and count == 0 then
call PauseTimer(iterator)
endif
endmethod
private static method run takes nothing returns nothing
local thistype this = thistype.allocate()
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
set count = count + 1
if count == 1 then
call TimerStart(iterator, TIMEOUT, true, function thistype.periodic)
endif
set this.caster = GetTriggerUnit()
set this.damage = (1000 + GetUnitState(this.caster, UNIT_STATE_LIFE)) * (2 + GetUnitAbilityLevel(GetTriggerUnit(), ABIL_CODE)) / 100
set this.x = GetUnitX(this.caster)
set this.y = GetUnitY(this.caster)
endmethod
private static method stop takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen GetTriggerUnit() == this.caster
set this = this.next
endloop
set this.channelling = false
endmethod
private static method conditions takes nothing returns boolean
if GetSpellAbilityId() == ABIL_CODE then
return true
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local trigger t2 = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.conditions))
call TriggerAddAction(t, function thistype.run)
call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_SPELL_FINISH)
call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t2, Condition(function thistype.conditions))
call TriggerAddAction(t2, function thistype.stop)
set t = null
set t2 = null
endmethod
endstruct
endscope