// Chain Fire Version 1.00
scope ChainFire initializer Init_ChainFire
globals
private constant integer ABILTY_ID = 'A000'
private constant integer CHFIRE_ID = 'c000'
private constant timer cf_timer = CreateTimer()
private constant real CF_TIMER_INTERVAL = 0.01
private constant real STEP_DIST = 16.00
private constant real CHEK_DIST = 24.00
private constant real IB = 64.00
private constant real K = 40.00
private integer index = 0
private integer array temp_dat
private hashtable unit_flag = InitHashtable()
//====================================================================================================
// User-edit data //
//====================================================================================================
// Targets:
private constant boolean GROUND = TRUE // TRUE/FALSE to include/exclude ground units
private constant boolean FLYING = TRUE // TRUE/FALSE to include/exclude flying units
private constant boolean MECHANIC = FALSE // TRUE/FALSE to include/exclude mechanical units
private constant boolean STRUCTURE = FALSE // TRUE/FALSE to include/exclude structure units
// Damage Variables:
private constant boolean D_TABLE = FALSE // TRUE/FALSE to use Table/Formula
// Table:
private constant real array Damage // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real D_CF = 25.00 // changes 'ConstantFactor'
private constant real D_LF = 25.00 // changes 'AbilityLevelFactor'
private constant real D_PLF = 0 // changes 'PrevLevelFactor'
// Area of Effect Variables:
private constant boolean R_TABLE = FALSE // TRUE/FALSE to use Table/Formula
// Table:
private constant real array Area // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real R_CF = 500.0 // changes 'ConstantFactor'
private constant real R_LF = 0 // changes 'AbilityLevelFactor'
private constant real R_PLF = 0 // changes 'PrevLevelFactor'
// Damage Reduction per Jump Variables:
private constant boolean N_TABLE = TRUE // TRUE/FALSE to use Table/Formula
// Table:
private constant real array DmgRed // set the values in the function named "Spell_Data_Table"
// Formula:
private constant real N_CF = 0.2 // changes 'ConstantFactor'
private constant real N_LF = -0.05 // changes 'AbilityLevelFactor'
private constant real N_PLF = 0 // changes 'PrevLevelFactor'
// Jumps Variables:
private constant boolean J_TABLE = FALSE // TRUE/FALSE to use Table/Formula
// Table:
private constant integer array Jumps // set the values in the function named "Spell_Data_Table"
// Formula:
private constant integer J_CF = 2 // changes 'ConstantFactor'
private constant integer J_LF = 2 // changes 'AbilityLevelFactor'
private constant integer J_PLF = 0 // changes 'PrevLevelFactor'
private constant attacktype ATKTYPE = ATTACK_TYPE_NORMAL // Spell Attack Type
private constant damagetype DMGTYPE = DAMAGE_TYPE_FIRE // Fire Damage Type
endglobals
private function Spell_Data_Table takes nothing returns nothing
// If your spell is more than 3 levels just add Damage[]/Range[]/Jumps[] under its appropriate place
// Remember that if you don't add this line the Damage/Range/Jumps will be 0 for level 4+
// Damage Table
set Damage[1] = 50.0
set Damage[2] = 100.0
set Damage[3] = 200.0
// Range Table
set Area[1] = 500.0
set Area[2] = 500.0
set Area[3] = 500.0
// Jumps Table
set Jumps[1] = 4
set Jumps[2] = 6
set Jumps[3] = 8
// Damage Reduction Per Jump Table
set DmgRed[1] = 0.15
set DmgRed[2] = 0.10
set DmgRed[3] = 0.05
//====================================================================================================
// End of User-edit data //
//====================================================================================================
endfunction
private function CF_Damage takes integer al returns real
local real prevDmg
if D_TABLE then
return Damage[al]
else
if al == 1 then
set prevDmg = 0
else
set prevDmg = CF_Damage(al - 1)
endif
return D_CF + D_LF * al + D_PLF * prevDmg
endif
endfunction
private function CF_Area takes integer al returns real
local real prevArea
if R_TABLE then
return Area[al]
else
if al == 1 then
set prevArea = 0
else
set prevArea = CF_Area(al - 1)
endif
return R_CF + R_LF * al + R_PLF * prevArea
endif
endfunction
private function CF_Jump takes integer al returns integer
local integer prevJump
if J_TABLE then
return Jumps[al]
else
if al == 1 then
set prevJump = 0
else
set prevJump = CF_Jump(al - 1)
endif
return J_CF + J_LF * al + J_PLF * prevJump
endif
endfunction
private function CF_DamagReductionPerJump takes integer al returns real
local real prevDr
if N_TABLE then
return DmgRed[al]
else
if al == 1 then
set prevDr = 0
else
set prevDr = CF_DamagReductionPerJump(al - 1)
endif
return N_CF + N_LF * al + N_PLF * prevDr
endif
endfunction
private function IsTargetAllowed takes unit u returns boolean
if IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or/*
*/(not FLYING and IsUnitType(u,UNIT_TYPE_FLYING)) or/*
*/(not GROUND and IsUnitType(u,UNIT_TYPE_GROUND)) or/*
*/(not MECHANIC and IsUnitType(u,UNIT_TYPE_MECHANICAL)) or/*
*/(not STRUCTURE and IsUnitType(u,UNIT_TYPE_STRUCTURE)) then
return false
endif
return true
endfunction
private function GetNearestUnit takes player owner, unit source, real radius, integer dat returns unit
local group g = CreateGroup()
local group temp_g = CreateGroup()
local real locX = GetUnitX(source)
local real locY = GetUnitY(source)
local real dx
local real dy
local unit ref_u
local unit any_u
local unit temp_u
local real dist_ref_u
local real dist_any_u
call GroupEnumUnitsInRange(temp_g,locX,locY,radius,null)
//call GroupRemoveUnit(temp_g,source)
loop
set temp_u = FirstOfGroup(temp_g)
exitwhen temp_u == null
if temp_u != LoadUnitHandle(unit_flag,dat,GetHandleId(temp_u)) and/*
*/ not(IsUnitType(temp_u,UNIT_TYPE_DEAD)) and/*
*/ IsUnitEnemy(temp_u,owner) and/*
*/ IsTargetAllowed(temp_u) then
call GroupAddUnit(g,temp_u)
endif
call GroupRemoveUnit(temp_g,temp_u)
endloop
call DestroyGroup(temp_g)
set ref_u = FirstOfGroup(g)
call GroupRemoveUnit(g,ref_u)
loop
set any_u = FirstOfGroup(g)
exitwhen any_u == null
set dx = GetUnitX(ref_u) - locX
set dy = GetUnitY(ref_u) - locY
set dist_ref_u = SquareRoot(dx*dx + dy*dy)
set dx = GetUnitX(any_u) - locX
set dy = GetUnitY(any_u) - locY
set dist_any_u = SquareRoot(dx*dx + dy*dy)
if dist_ref_u > dist_any_u then
set ref_u = any_u
endif
call GroupRemoveUnit(g,any_u)
endloop
call DestroyGroup(g)
return ref_u
endfunction
private struct data extends array
player owner
unit caster
unit target
unit cfd // Chain Fire Dummy
real clx
real cly
real face
real slope
real stepK // Step Factor
//real MaxZ
real B
real damage
real areaeffect
real dr // Damage Reduction (Per Jump)
integer jump
implement Alloc
static method MoveCF takes nothing returns nothing
local thistype dat
local integer i = 0
local real x // Current X of Chain Fire Missile
local real y // Current Y of Chain Fire Missile
local real v
local real z
local real tx // Target X
local real ty // Target Y
local real tz // Target Z
local real dx
local real dy
local real dv
local real dz
local real rd
local real dist
local unit u
loop // cycle through instances
exitwhen i >= index
set dat = temp_dat[i]
set u = dat.target
set x = GetUnitX(dat.cfd)
set y = GetUnitY(dat.cfd)
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set tz = GetUnitFlyHeight(u) + K
set dx = tx - x
set dy = ty - y
set dat.face = Atan2(dy,dx)
set dist = SquareRoot(dx*dx + dy*dy)
if dist < CHEK_DIST then
call UnitDamageTarget(dat.caster, u,dat.damage, false, true, ATKTYPE, DMGTYPE, null)
call SaveUnitHandle(unit_flag,dat,GetHandleId(u),u)
set dat.jump = dat.jump - 1
set dat.damage = dat.damage * ( 1 - dat.dr)
if dat.jump <= 0 then // end the spell
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
call KillUnit(dat.cfd)
set dat.caster = null
set dat.target = null
set u = null
call FlushChildHashtable( unit_flag , dat )
if index == 0 then
call PauseTimer(cf_timer)
endif
call dat.deallocate()
else // seek the next target
set dat.target = GetNearestUnit(dat.owner,u,dat.areaeffect,dat)
if dat.target == null then
set dat.jump = 0
set index = index - 1
set temp_dat[i] = temp_dat[index]
set i = i - 1
call KillUnit(dat.cfd)
set dat.caster = null
set dat.target = null
set u = null
call FlushChildHashtable( unit_flag , dat )
if index == 0 then
call PauseTimer(cf_timer)
endif
call dat.deallocate()
else
set dx = GetUnitX(dat.target) - tx
set dy = GetUnitY(dat.target) - ty
set dv = SquareRoot( dx*dx + dy*dy )
set dz = GetUnitFlyHeight(dat.target) - tz
set rd = SquareRoot( dv*dv + dz*dz )
set dat.clx = tx
set dat.cly = ty
set dat.B = tz
set dat.slope = dz / dv
set dat.stepK = rd / dv
set dat.face = Atan2(dy,dx)
call SetUnitFacing(dat.cfd,dat.face * bj_RADTODEG)
endif
endif
else
//set x = x + STEP_DIST * Cos(dat.face)
//set y = y + STEP_DIST * Sin(dat.face)
set dx = x - dat.clx
set dy = y - dat.cly
set v = SquareRoot( dx*dx + dy*dy )
set z = (dat.slope * v) + dat.B
call SetUnitX(dat.cfd,x + STEP_DIST * Cos(dat.face) / dat.stepK)
call SetUnitY(dat.cfd,y + STEP_DIST * Sin(dat.face) / dat.stepK)
call SetUnitFlyHeight(dat.cfd,z,0.0)
endif
set i = i + 1
endloop
endmethod
static method StartCF takes unit caster, unit target returns nothing
local thistype dat = thistype.allocate()
local integer al = GetUnitAbilityLevel(caster , ABILTY_ID)
local real cx // Caster X
local real cy // Caster Y
local real cz // Caster Z
local real tx // Target X
local real ty // Target Y
local real tz // Target Z
local real dx
local real dy
local real dv
local real dz
local real rd // Real Distance
set temp_dat[index] = dat
set dat.caster = caster
set dat.target = target
set dat.owner = GetOwningPlayer(caster)
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set dat.clx = cx
set dat.cly = cy
set dat.B = IB
set cz = GetUnitFlyHeight(caster) + dat.B
set tx = GetUnitX(target)
set ty = GetUnitY(target)
set tz = GetUnitFlyHeight(target) + K
set dx = tx - cx
set dy = ty - cy
set dz = tz - cz
set dv = SquareRoot( dx*dx + dy*dy )
set rd = SquareRoot( dv*dv + dz*dz )
set dat.slope = dz / dv
set dat.stepK = rd / dv
set dat.face = Atan2(dy,dx)
set dat.cfd = CreateUnit(GetOwningPlayer(caster),CHFIRE_ID,cx,cy,dat.face*bj_RADTODEG)
call SetUnitFlyHeight(dat.cfd,cz,0.0)
call SetUnitPathing( dat.cfd, false )
set dat.damage = CF_Damage(al)
set dat.areaeffect = CF_Area(al)
set dat.jump = CF_Jump(al)
set dat.dr = CF_DamagReductionPerJump(al)
//call BJDebugMsg(R2S(dat.dr))
if index ==0 then
call TimerStart(cf_timer,CF_TIMER_INTERVAL,true,function thistype.MoveCF)
endif
set index = index + 1
endmethod
endstruct
private function CF_Actions takes nothing returns nothing
if GetSpellAbilityId() == ABILTY_ID then
call data.StartCF(GetTriggerUnit(),GetSpellTargetUnit())
endif
endfunction
private function Init_ChainFire takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t,function CF_Actions)
call Spell_Data_Table()
//Preload the dummy units to reduce lag on first cast
call RemoveUnit(CreateUnit(Player(12),CHFIRE_ID,0.,0.,0.))
call RemoveUnit(CreateUnit(Player(12),CHFIRE_ID,0.,0.,0.))
endfunction
endscope