//Credits:
// - Blaxor for the inspiration
// - cohadar for his PUI library
// - Rising_Dusk for his GroupUtils library
// - Vexorian for JassHelper and TimerUtils
// - PipeDream for Grimoire and all his help with spirals
// - PitzerMike for JassNewGenPack
// - SFilip for TESH
// How to import:
// - Copy the code into a trigger on your map, adjust the constants below (especially AID) and youre done.
// - Note that you have to copy over the required libraries as well
library FireSpiral uses TimerUtils, GroupUtils, PUI, xefx
globals
private constant integer AID = 'A000' // rawcode of the triggering abiltiy (press Ctrl+D to display the rawcode of objects, in the object editor)
private constant real FX_INTERVAL = 0.15 // effects are spawned in this interval
private constant string FIRE_FX = "Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeEmbers.mdl" // this is the effect the spiral consits of
private constant real FIRE_FX_RADIUS = 24. // collision size of the beforementioned effect
private constant real FIRE_FX_SCALE = 1. // scale of beforementioned effect
private real array FIRE_DMG // Units receive this much damage per second
private constant real FIRE_DMG_RADIUS = 64 // this is the radius in which units around the effects get receive damage
private constant real FIRE_DMG_TICK = 1./32 // this is the granulation of damage
private constant string FIRE_DMG_FX = "Environment\\LargeBuildingFire\\LargeBuildingFire2.mdl" // this is the effect spawned on units receiving damage by the spiral
private constant string FIRE_DMG_FX_ATTPT = "head" // this is the attachmentpoint where the beforementioned effect gets attached to
private constant real FIRE_DMG_FX_STAY = 2. // the beforementioned effect stays on units this long after they receive no more damage
private constant attacktype FIRE_DMG_ATTACKTYPE = ATTACK_TYPE_MAGIC // attacktype of the damage units receive
private constant damagetype FIRE_DMG_DAMAGETYPE = DAMAGE_TYPE_MAGIC // damagetype of the damage units receive
private real array SPIRAL_INITIAL_RADIUS // initial radius of the spiral
private real array SPIRAL_END_RADIUS // radius of the spiral
private real array SPIRAL_CIRCLES // how often does a spiral reach ((angle) modulo (2*PI))==0
private integer array SPIRAL_ENDINGS // number of spirals spawned
private real array DURATION // Duration of the spiral after all effects have been spawned
private constant integer MAX_FIRE_FX_PER_INSTANCE = 255
endglobals
private function SetUpFIRE_DMG takes nothing returns nothing
set FIRE_DMG[1]=50.
set FIRE_DMG[2]=75.
set FIRE_DMG[3]=100.
endfunction
private function SetUpSPIRAL_INITIAL_RADIUS takes nothing returns nothing
set SPIRAL_INITIAL_RADIUS[1]=200
set SPIRAL_INITIAL_RADIUS[2]=200
set SPIRAL_INITIAL_RADIUS[3]=200
endfunction
private function SetUpSPIRAL_END_RADIUS takes nothing returns nothing
set SPIRAL_END_RADIUS[1]=400.
set SPIRAL_END_RADIUS[2]=500.
set SPIRAL_END_RADIUS[3]=600.
endfunction
private function SetUpSPIRAL_CIRCLES takes nothing returns nothing
set SPIRAL_CIRCLES[1]=0.6
set SPIRAL_CIRCLES[2]=0.8
set SPIRAL_CIRCLES[3]=1.0
endfunction
private function SetUpSPIRAL_ENDINGS takes nothing returns nothing
set SPIRAL_ENDINGS[1]=3
set SPIRAL_ENDINGS[2]=3
set SPIRAL_ENDINGS[3]=3
endfunction
private function SetUpDURATION takes nothing returns nothing
set DURATION[1]=5.
set DURATION[2]=7.
set DURATION[3]=9.
endfunction
// proxy functions
// if you want to use formulae, edit these
private function Fire_Dmg takes integer level returns real
return FIRE_DMG[level]
endfunction
private function Spiral_Initial_Radius takes integer level returns real
return SPIRAL_INITIAL_RADIUS[level]
endfunction
private function Spiral_End_Radius takes integer level returns real
return SPIRAL_END_RADIUS[level]
endfunction
private function Spiral_Circles takes integer level returns real
return SPIRAL_CIRCLES[level]
endfunction
private function Spiral_Endings takes integer level returns integer
return SPIRAL_ENDINGS[level]
endfunction
private function Duration takes integer level returns real
return DURATION[level]
endfunction
// I wouldnt edit anything below this line, if i were you
private struct Data
unit c
integer level
integer i
timer t
real x
real y
real a
real b
real f
real d
integer fxcount
xefx array FX[MAX_FIRE_FX_PER_INSTANCE]
real array FXX[MAX_FIRE_FX_PER_INSTANCE]
real array FXY[MAX_FIRE_FX_PER_INSTANCE]
static Data array Structs
static integer Count=0
static timer T=CreateTimer()
static Data tmps
static boolexpr FireDmg
static timer array ImmoT
static effect array ImmoFX
static method ImmoCallback takes nothing returns nothing
local timer t=GetExpiredTimer()
local integer i=GetTimerData(t)
call DestroyEffect(Data.ImmoFX[i])
set Data.ImmoFX[i]=null
call ReleaseTimer(t)
endmethod
static method FireDmgFunc takes nothing returns boolean
local unit u=GetFilterUnit()
local integer i
local timer t
if IsUnitEnemy(u, GetOwningPlayer(Data.tmps.c)) and (not IsUnitInGroup(u, ENUM_GROUP)) and IsUnitType(u, UNIT_TYPE_DEAD)==false and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)==false and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false then
call UnitDamageTarget(Data.tmps.c, u, Fire_Dmg(Data.tmps.level)*FIRE_DMG_TICK, false, false, FIRE_DMG_ATTACKTYPE, FIRE_DMG_DAMAGETYPE, WEAPON_TYPE_WHOKNOWS)
set i=GetUnitIndex(u)
if Data.ImmoFX[i]==null then
set Data.ImmoFX[i]=AddSpecialEffectTarget(FIRE_DMG_FX, u, FIRE_DMG_FX_ATTPT)
set t=NewTimer()
call SetTimerData(t, i)
call TimerStart(t, FIRE_DMG_FX_STAY, false, function Data.ImmoCallback)
set Data.ImmoT[i]=t
else
call TimerStart(Data.ImmoT[i], FIRE_DMG_FX_STAY, false, function Data.ImmoCallback)
endif
set u=null
return true
endif
set u=null
return false
endmethod
method onDestroy takes nothing returns nothing
set .c=null
call ReleaseTimer(.t)
set Data.Count=Data.Count-1
set Data.Structs[.i]=Data.Structs[Data.Count]
set Data.Structs[.i].i=.i
if Data.Count==0 then
call PauseTimer(Data.T)
endif
endmethod
static method EndingCB takes nothing returns nothing
local Data s=GetTimerData(GetExpiredTimer())
local integer i=0
loop
exitwhen i>=Spiral_Endings(s.level)
set s.fxcount=s.fxcount-1
call s.FX[s.fxcount].destroy()
set i=i+1
endloop
if s.fxcount<=0 then
call s.destroy()
endif
endmethod
static method Ending takes nothing returns nothing
call TimerStart(GetExpiredTimer(), FX_INTERVAL, true, function Data.EndingCB)
endmethod
static method SpawnCB takes nothing returns nothing
local Data s=GetTimerData(GetExpiredTimer())
local integer i=0
local real d=2*bj_PI/Spiral_Endings(s.level)
local real f=s.a+(bj_PI/2)
loop
exitwhen i>=Spiral_Endings(s.level)
debug if s.fxcount>=MAX_FIRE_FX_PER_INSTANCE then
debug call BJDebugMsg("FireSpiral: Array overflow! Try increasing MAX_FIRE_FX_PER_INSTANCE!")
debug exitwhen true
debug endif
set s.FXX[s.fxcount]=s.x+(s.d*Cos(s.f+s.a+(i*d)))
set s.FXY[s.fxcount]=s.y+(s.d*Sin(s.f+s.a+(i*d)))
set s.FX[s.fxcount]=xefx.create(s.FXX[s.fxcount], s.FXY[s.fxcount], f)
set s.FX[s.fxcount].fxpath=FIRE_FX
set s.FX[s.fxcount].scale=FIRE_FX_SCALE
set s.fxcount=s.fxcount+1
set i=i+1
endloop
set s.a=2*Asin((2*FIRE_FX_RADIUS)/(2*s.d))+s.a
set s.d=s.b*s.a
if s.d>Spiral_End_Radius(s.level) then
call TimerStart(s.t, Duration(s.level), false, function Data.Ending)
endif
endmethod
static method Callback takes nothing returns nothing
local Data s
local integer i=0
local integer j=0
loop
exitwhen i>=Data.Count
set s=Data.Structs[i]
loop
exitwhen j>=s.fxcount
set Data.tmps=s
call GroupEnumUnitsInRange(ENUM_GROUP, s.FXX[j], s.FXY[j], FIRE_DMG_RADIUS, Data.FireDmg)
set j=j+1
endloop
call GroupClear(ENUM_GROUP)
set i=i+1
endloop
endmethod
static method Cond takes nothing returns boolean
return GetSpellAbilityId()==AID
endmethod
static method Create takes nothing returns nothing
local Data s=Data.allocate()
local real d
local integer i=0
local real f
set s.c=GetTriggerUnit()
set s.level=GetUnitAbilityLevel(s.c, AID)
set s.t=NewTimer()
set s.f=GetUnitFacing(s.c)*bj_DEGTORAD
set s.x=GetUnitX(s.c)
set s.y=GetUnitY(s.c)
set s.b=(Spiral_End_Radius(s.level)-Spiral_Initial_Radius(s.level))/(2*bj_PI*Spiral_Circles(s.level))
set s.d=Spiral_Initial_Radius(s.level)
set s.a=s.d/s.b
set d=2*bj_PI/Spiral_Endings(s.level)
set f=s.a+(bj_PI/2)
loop
exitwhen i>=Spiral_Endings(s.level)
set s.FXX[s.fxcount]=s.x+(s.d*Cos(s.f+s.a+(i*d)))
set s.FXY[s.fxcount]=s.y+(s.d*Sin(s.f+s.a+(i*d)))
set s.FX[s.fxcount]=xefx.create(s.FXX[s.fxcount], s.FXY[s.fxcount], f)
set s.FX[s.fxcount].fxpath=FIRE_FX
set s.FX[s.fxcount].scale=FIRE_FX_SCALE
set s.fxcount=s.fxcount+1
set i=i+1
endloop
if s.d==0 then
set s.a=2*FIRE_FX_RADIUS/s.b
else
set s.a=2*Asin((2*FIRE_FX_RADIUS)/(2*s.d))+s.a
endif
set s.d=s.b*s.a
call SetTimerData(s.t, s)
call TimerStart(s.t, FX_INTERVAL, true, function Data.SpawnCB)
set Data.Structs[Data.Count]=s
set s.i=Data.Count
if Data.Count==0 then
call TimerStart(Data.T, FIRE_DMG_TICK, true, function Data.Callback)
endif
set Data.Count=Data.Count+1
endmethod
static method onInit takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t, function Data.Create)
call TriggerAddCondition(t, Condition(function Data.Cond))
set Data.FireDmg=Condition(function Data.FireDmgFunc)
call SetUpFIRE_DMG()
call SetUpSPIRAL_INITIAL_RADIUS()
call SetUpSPIRAL_END_RADIUS()
call SetUpSPIRAL_CIRCLES()
call SetUpSPIRAL_ENDINGS()
call SetUpDURATION()
endmethod
endstruct
endlibrary