library AquaField uses AIDS, GT, GroupUtils, T32 optional Damage
constant native UnitAlive takes unit id returns boolean
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 'ABAF' // raw code of ability "Aqua Field"
private constant integer DUMABILID = 'AAF0' // raw code of ability "Aqua Field (Stun)"
private constant integer BUFFID = 'BAF0' // raw code of buff "Aqua Field"
private constant integer DUMMYID = 'duAF' // raw code of unit "Aqua Field Dummy"
private constant integer CASTERID = 'cAST' // raw code of unit "Caster Dummy"
private constant real PERIOD = 0.1 // interval in delay time is updated
private constant real DINTERVAL = 50.0 // every distance interval where an effect will be created
// smaller value is more accurate, but might cause framerate to drop
private constant string FX = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // effect used for explosion
private constant boolean USE_DAMAGE = true // true if you want to this spell to use the Damage library
private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage dealt
private constant damagetype DMG = DAMAGE_TYPE_NORMAL // damage type of damage dealt
endglobals
// filter to determine which target types are allowed
private constant function TargetFilter takes unit u, unit target returns boolean
return /*
*/ UnitAlive(target) /* // target is alive
*/ and IsUnitEnemy(target, GetOwningPlayer(u)) /* // target is an enemy
*/ and not IsUnitType(target, UNIT_TYPE_STRUCTURE) /* // target is not a structure
*/ and not IsUnitType(target, UNIT_TYPE_MECHANICAL) /* // taget is not mechanical
*/
endfunction
// radius of each field
private constant function GetArea takes integer level returns real
return 200.
endfunction
// damage dealt per second
private constant function GetDamage takes integer level returns real
return 125.
endfunction
// delay time before the field explodes
private constant function GetDelay takes integer level returns real
return 1.
endfunction
// distance of the field
private constant function GetDistance takes integer level returns real
return 1000.
endfunction
// duration of stun
private constant function GetDuration takes integer level returns real
return level * 0.25 + 1.
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
private struct Data
thistype next
thistype prev
unit caster
group dummyg
group g
real area
real damage
real delay
real dur
static thistype tempData
static integer array store
static unit castDummy
static integer groupCount
static timer delayTimer = CreateTimer()
static integer delayCount = 0
private static method flushEnumFunc takes nothing returns nothing
local unit u = GetEnumUnit()
call UnitRemoveAbility(u, BUFFID)
set store[GetUnitId(u)] = 0
set u = null
endmethod
private static method stunEnumFunc takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
if UnitAlive(u) and GetUnitAbilityLevel(u, BUFFID) > 0 then
static if USE_DAMAGE then
call UnitDamageTargetEx(this.caster, u, this.damage, true, false, ATK, DMG, null)
else
call UnitDamageTarget(this.caster, u, this.damage, true, false, ATK, DMG, null)
endif
set groupCount = groupCount + 1
endif
set u = null
endmethod
private method periodic takes nothing returns nothing
if this.dur <= 0 then
call ForGroup(this.g, function thistype.flushEnumFunc)
call ReleaseGroup(this.dummyg)
call ReleaseGroup(this.g)
call this.stopPeriodic()
call this.deallocate()
else
set tempData = this
set groupCount = 0
call ForGroup(this.g, function thistype.stunEnumFunc)
if groupCount == 0 then
set this.dur = 0
else
set this.dur = this.dur - T32_PERIOD
endif
endif
endmethod
implement T32x
private static method filterFunc takes nothing returns boolean
local thistype this = tempData
local unit u = GetFilterUnit()
local integer id
if not IsUnitInGroup(u, this.g) and TargetFilter(this.caster, u) then
set id = GetUnitId(u)
if store[id] > 0 then
call GroupRemoveUnit(thistype(store[id]).g, u)
endif
set store[id] = this
call IssueTargetOrder(castDummy, "firebolt", u)
call GroupAddUnit(this.g, u)
endif
set u = null
return false
endmethod
private static method enumFunc takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
call DestroyEffect(AddSpecialEffect(FX, x, y))
call GroupEnumUnitsInArea(ENUM_GROUP, x, y, this.area, Filter(function thistype.filterFunc))
call GroupRemoveUnit(this.dummyg, u)
call RemoveUnit(u)
set u = null
endmethod
private static method delayIterate takes nothing returns nothing
local thistype this = thistype(0)
loop
set this = this.next
exitwhen this == 0
if this.delay <= 0 then
set this.next.prev = this.prev
set this.prev.next = this.next
set delayCount = delayCount - 1
if delayCount == 0 then
call PauseTimer(delayTimer)
endif
set tempData = this
call ForGroup(this.dummyg, function thistype.enumFunc)
call this.startPeriodic()
else
set this.delay = this.delay - PERIOD
endif
endloop
endmethod
private static method onCast takes nothing returns boolean
local thistype this = thistype.allocate()
local player p
local integer level
local integer i
local real a
local real x
local real y
set thistype(0).next.prev = this
set this.next = thistype(0).next
set thistype(0).next = this
set this.prev = thistype(0)
if delayCount == 0 then
call TimerStart(delayTimer, PERIOD, true, function thistype.delayIterate)
endif
set delayCount = delayCount + 1
set this.caster = GetTriggerUnit()
set this.dummyg = NewGroup()
set this.g = NewGroup()
set level = GetUnitAbilityLevel(this.caster, ABILID)
set this.area = GetArea(level)
set this.damage = GetDamage(level) * T32_PERIOD
set this.delay = GetDelay(level)
set this.dur = GetDuration(level)
set p = GetOwningPlayer(this.caster)
set x = GetUnitX(this.caster)
set y = GetUnitY(this.caster)
set a = Atan2(GetSpellTargetY() - y, GetSpellTargetX() - x)
set i = R2I(GetDistance(level) / DINTERVAL)
loop
exitwhen i == 0
call GroupAddUnit(this.dummyg, CreateUnit(p, DUMMYID, x + DINTERVAL * i * Cos(a), y + DINTERVAL * i * Sin(a), 0))
set i = i - 1
endloop
set p = null
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call GT_RegisterStartsEffectEvent(t, ABILID)
call TriggerAddCondition(t, Condition(function thistype.onCast))
set t = null
set castDummy = CreateUnit(Player(13), CASTERID, 0, 0, 0)
call UnitAddAbility(castDummy, DUMABILID)
endmethod
endstruct
endlibrary