//* ****************************************************************************************************
//*
//* Installation:
//*
//* Copy all of the triggers in the "Required Systems" folder that you don't already have.
//* > If you copied xebasic, do the following:
//* - Import the included "Dummy.mdx" file.
//* - Copy the "Dummy" unit. Update its model field to the imported model.
//* - Open the "xebasic" trigger. Update XE_DUMMY_UNITID to the raw ID of the copied unit.
//* Copy this trigger.
//*
//* Copy the "Energy Field" hero ability. Update SPELL_ID below.
//*
//* ****************************************************************************************************
scope EnergyField
// -----
// Start of Configurables
// -----
globals
// Raw ID of the "Energy Field" hero ability:
private constant integer SPELL_ID = 'A059'
private constant integer BOUNDARIES = 15 // Number of boundary connections (shouldn't need to be changed - only if you change the AoE).
private constant real AREA_OF_EFFECT = 350.0 // Area of effect.
private constant real RING_RADIUS = 64.0 // How close enemies need to get to the ring to get shocked.
private constant real RING_HEIGHT = 50.0 // Ring height above the ground.
private constant real KNOCK_DISTANCE = 100.0 // How far enemies get knocked back.
// Code for the connecting lighting:
private constant string LIGHTNING_CODE = "CLPB"
// Boundary connection effect:
private constant string BOUNDARY_EFF = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
// Effect created on shocked units:
private constant string TARGET_EFF = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
endglobals
// Configure how the duration of the field is calculated:
private function GetDuration takes integer level returns real
return (2.0 * level) + 5.0
endfunction
// Configure how the damage of the shock is calculated:
private function GetDamage takes integer level returns real
return (20.0 * level) + 30.0
endfunction
// -----
// End of Configurables
// -----
globals
private constant real BOUNDARY_DIV = 360.0 / BOUNDARIES
private group enumG = CreateGroup()
private group knocked = CreateGroup()
private unit enumU
endglobals
private function IsUnitTargetable takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
private function GetDistance takes unit u, real x, real y returns real
local real dx = GetUnitX(u) - x
local real dy = GetUnitY(u) - y
return SquareRoot((dx * dx) + (dy * dy))
endfunction
private struct Pushback
unit target
private static method expire takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call ReleaseTimer(GetExpiredTimer())
call GroupRemoveUnit(knocked, target)
set target = null
call deallocate()
endmethod
static method start takes unit s, unit u, real dmg, real ang, real spd returns thistype
local thistype this
if (not IsUnitInGroup(u, knocked)) then
set this = allocate()
set target = u
call GroupAddUnit(knocked, target)
call Knockback.start(target, KNOCK_DISTANCE, ang, spd, "")
call UnitDamageTarget(s, target, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
call DestroyEffect(AddSpecialEffectTarget(TARGET_EFF, target, "origin"))
call TimerStart(NewTimerEx(this), KNOCK_DISTANCE / spd, false, function thistype.expire)
return this
endif
return 0
endmethod
endstruct
private struct SpellData
unit caster
lightning array bolt[BOUNDARIES]
xefx array boundary[BOUNDARIES]
player owner
real timeLeft
real damage
private method destroy takes nothing returns nothing
local integer i
for i = 0 to (BOUNDARIES - 1)
call boundary[i].destroy()
call DestroyLightning(bolt[i])
set bolt[i] = null
endfor
set caster = null
set owner = null
call deallocate()
endmethod
private method periodic takes nothing returns nothing
local integer i
local real cx = GetUnitX(caster)
local real cy = GetUnitY(caster)
local real x
local real y
local real dist
local real speed = GetUnitMoveSpeed(caster) * 1.3
// -----
// Update boundary
// -----
for i = 0 to (BOUNDARIES - 1)
set x = cx + AREA_OF_EFFECT * Cos(Deg2Rad(BOUNDARY_DIV * i))
set y = cy + AREA_OF_EFFECT * Sin(Deg2Rad(BOUNDARY_DIV * i))
set boundary[i].x = x
set boundary[i].y = y
endfor
for i = 0 to (BOUNDARIES - 1)
if (i == BOUNDARIES - 1) then
set x = boundary[0].x
set y = boundary[0].y
else
set x = boundary[i+1].x
set y = boundary[i+1].y
endif
call MoveLightningEx(bolt[i], true, boundary[i].x, boundary[i].y, RING_HEIGHT, x, y, RING_HEIGHT)
endfor
// -----
// Check units
// -----
call GroupEnumUnitsInRange(enumG, cx, cy, AREA_OF_EFFECT + RING_RADIUS, null)
for enumU in enumG
if (IsUnitTargetable(enumU) and IsUnitEnemy(enumU, owner)) then
set dist = GetDistance(enumU, cx, cy)
if (dist > AREA_OF_EFFECT) then
call Pushback.start(caster, enumU, damage, Atan2(GetUnitY(enumU) - cy, GetUnitX(enumU) - cx), speed)
elseif (dist > (AREA_OF_EFFECT - RING_RADIUS)) then
call Pushback.start(caster, enumU, damage, Atan2(cy - GetUnitY(enumU), cx - GetUnitX(enumU)), speed)
endif
endif
endfor
set timeLeft = timeLeft - T32_PERIOD
if (timeLeft <= 0.0) then
call stopPeriodic()
call destroy()
endif
endmethod
implement T32x
private static method create takes nothing returns thistype
local thistype this = allocate()
local integer i
local real cx
local real cy
local real x
local real y
set caster = GetTriggerUnit()
set owner = GetTriggerPlayer()
set i = GetUnitAbilityLevel(caster, SPELL_ID)
set timeLeft = GetDuration(i)
set damage = GetDamage(i)
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
// Create connection points:
for i = 0 to (BOUNDARIES - 1)
set x = cx + AREA_OF_EFFECT * Cos(Deg2Rad(BOUNDARY_DIV * i))
set y = cy + AREA_OF_EFFECT * Sin(Deg2Rad(BOUNDARY_DIV * i))
set boundary[i] = xefx.create(x, y, 0.0)
set boundary[i].fxpath = BOUNDARY_EFF
set boundary[i].z = RING_HEIGHT
endfor
// Create connecting lightning:
for i = 0 to (BOUNDARIES - 1)
if (i == BOUNDARIES - 1) then
set x = boundary[0].x
set y = boundary[0].y
else
set x = boundary[i+1].x
set y = boundary[i+1].y
endif
set bolt[i] = AddLightningEx(LIGHTNING_CODE, true, boundary[i].x, boundary[i].y, RING_HEIGHT, x, y, RING_HEIGHT)
endfor
call startPeriodic()
return this
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.create)
call Preload(BOUNDARY_EFF)
call Preload(TARGET_EFF)
endmethod
endstruct
endscope