// CONFIGURATION
// Constants
// Main spell raw code
constant function FS_GetSpellID takes nothing returns integer
return 'A005'
endfunction
// Main projectile dummy raw code
constant function FS_DummyID takes nothing returns integer
return 'h002'
endfunction
// Secondary projectile dummy raw code
constant function FS_Dummy2ID takes nothing returns integer
return 'h003'
endfunction
// Main projectile effect
constant function FS_Sfx takes nothing returns string
return "Abilities\\Weapons\\LichMissile\\LichMissile.mdl"
endfunction
// Secondary projectile effect
constant function FS_Sfx1 takes nothing returns string
return "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
endfunction
// Attachment point
constant function FS_AttachPoint takes nothing returns string
return "origin"
endfunction
// Movement interval
constant function FS_Interval takes nothing returns real
return 0.03125
endfunction
// Booleans
// True = check if a unit's HP falls below a certain percentage
// False = check if a unit's HP falls below a certain amount
constant function FS_HPPercBool takes nothing returns boolean
return true
endfunction
// Filter valid units
function FS_FilterUnit takes unit caster, unit u returns boolean
return IsUnitEnemy(u, GetOwningPlayer(caster)) and GetWidgetLife(u) > 0.405 and GetUnitAbilityLevel(u, 'Avul') == 0 and not IsUnitType (u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType (u, UNIT_TYPE_STRUCTURE)
endfunction
// Get Abilities Level
function FS_SpellLvl takes unit caster returns integer
return GetUnitAbilityLevel (caster, FS_GetSpellID()) // Get the spell level of the caster
endfunction
// Level dependable values
// Main target damage
function FS_Damage takes integer level returns real
return 100 + level*50.
endfunction
// The damage rate (%) for secondary targets
function FS_DamageRate takes integer level returns real
return (40 + level*10.)/100
endfunction
// Area of Effect
function FS_AoE takes integer level returns real
return 400 + level*100.
endfunction
function FS_HPCheck takes integer level returns real
if FS_HPPercBool() then
// If the target's HP Percentage falls below this rate the spread will be triggered
return 40. + level*10.
endif
// If the target's HP falls below this rate the spread will be triggered
return 400. + level*100.
endfunction
// Projectile Speed
// The speed of the two type of projectiles
function FS_Speed takes integer level returns real
return (800. + level*100.)/(1/FS_Interval())
endfunction
// Attack - Damage - Weapon type
// Attack type
function FS_AttackType takes nothing returns attacktype
return ATTACK_TYPE_NORMAL
endfunction
// Damage type
function FS_DamageType takes nothing returns damagetype
return DAMAGE_TYPE_FIRE
endfunction
// Weapon type
function FS_WeaponType takes nothing returns weapontype
return WEAPON_TYPE_WHOKNOWS
endfunction
// END CONFIGURATION
// SPELL FUNCTIONS
// The loop where we index, deindex, and recycle everything
function FS_TimerLoop takes nothing returns nothing
local integer ID
local integer listmax = LoadInteger(udg_FS_Hashtable, -1, 0 )
local unit caster
local unit target
local unit dummy
local integer i = 0
local real x1
local real y1
local real x2
local real y2
local unit u
local real r
local real angle
local integer i1
local boolean b
local group g = CreateGroup()
local integer ID2
// Now we start looping through all the spell instances
loop
exitwhen i >= listmax
// First we load the saved values of a spell instance
set i = i + 1
set ID = LoadInteger (udg_FS_Hashtable, -1, i)
set caster = LoadUnitHandle (udg_FS_Hashtable, ID, 1)
set target = LoadUnitHandle (udg_FS_Hashtable, ID, 2)
set dummy = LoadUnitHandle (udg_FS_Hashtable, ID, 3)
set x1 = GetUnitX (dummy)
set y1 = GetUnitY (dummy)
set x2 = GetUnitX (target)
set y2 = GetUnitY (target)
set i1 = FS_SpellLvl (caster)
set b = LoadBoolean (udg_FS_Hashtable, ID, 4)
if (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) < 900 then
call KillUnit (dummy)
// We will check if this projectile is the main one or the secondary one through a saved boolean in order to deal the correct damage
if not b then
call DestroyEffect (AddSpecialEffectTarget(FS_Sfx(), target, FS_AttachPoint()))
call UnitDamageTarget(caster, target, FS_Damage(i1), false, false, FS_AttackType(), FS_DamageType(), FS_WeaponType())
else
call DestroyEffect (AddSpecialEffectTarget(FS_Sfx1(), target, FS_AttachPoint()))
call UnitDamageTarget(caster, target, FS_Damage(i1)*FS_DamageRate(i1), false, false, FS_AttackType(), FS_DamageType(), FS_WeaponType())
endif
// Now we check if the projectile that just collided is the main one or not
if not LoadBoolean (udg_FS_Hashtable, ID, 4) then
// Branching here: Depends on the spread condition, we will check if the target is fit for spreading
if FS_HPPercBool() then
// Here we go with the HP percentage
set r = (GetUnitState (target, UNIT_STATE_LIFE)/GetUnitState (target, UNIT_STATE_MAX_LIFE))*100
if r < FS_HPCheck(i1) then
call GroupEnumUnitsInRange(g, x2, y2, FS_AoE(i1), null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if FS_FilterUnit (caster, u) and IsUnitVisible(u, GetOwningPlayer(caster)) and u != target then
// Index and save secondary projectiles
set ID2 = LoadInteger (udg_FS_Hashtable, 0, 0)
if ID2 > 0 then
call SaveInteger (udg_FS_Hashtable, 0, 0, ID2 - 1)
set ID2 = LoadInteger (udg_FS_Hashtable, 0, ID2)
else
set ID2 = LoadInteger(udg_FS_Hashtable, 0, -1) + 1
call SaveInteger(udg_FS_Hashtable, 0, -1, ID2)
endif
call SaveUnitHandle (udg_FS_Hashtable, ID2, 1, caster)
call SaveUnitHandle (udg_FS_Hashtable, ID2, 2, u)
call SaveUnitHandle (udg_FS_Hashtable, ID2, 3, CreateUnit(GetOwningPlayer(caster), FS_Dummy2ID(), x2, y2, Atan2(GetUnitY(u)-y2, GetUnitX(u)-x2)*bj_RADTODEG))
call SaveBoolean (udg_FS_Hashtable, ID2, 4, true)
set listmax = listmax + 1
call SaveInteger (udg_FS_Hashtable , -1, 0, listmax)
call SaveInteger (udg_FS_Hashtable, -1, listmax, ID2)
call SaveInteger (udg_FS_Hashtable, ID2, 0, listmax)
endif
call GroupRemoveUnit(g, u)
endloop
endif
else
// Here we go with the HP amount
set r = GetUnitState (target, UNIT_STATE_LIFE)
if r < FS_HPCheck(i1) then
call GroupEnumUnitsInRange(g, x2, y2, FS_AoE(i1), null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if FS_FilterUnit (caster, u) and IsUnitVisible(u, GetOwningPlayer(caster)) and u != target then
// Index and save secondary projectiles, same as above
set ID2 = LoadInteger (udg_FS_Hashtable, 0, 0)
if ID2 > 0 then
call SaveInteger (udg_FS_Hashtable, 0, 0, ID2 - 1)
set ID2 = LoadInteger (udg_FS_Hashtable, 0, ID2)
else
set ID2 = LoadInteger (udg_FS_Hashtable, 0, -1) + 1
call SaveInteger (udg_FS_Hashtable, 0, -1, ID2)
endif
call SaveUnitHandle (udg_FS_Hashtable, ID2, 1, caster)
call SaveUnitHandle (udg_FS_Hashtable, ID2, 2, u)
call SaveUnitHandle (udg_FS_Hashtable, ID2, 3, CreateUnit(GetOwningPlayer(caster), FS_Dummy2ID(), x2, y2, Atan2(GetUnitY(u)-y2, GetUnitX(u)-x2)*bj_RADTODEG))
call SaveBoolean (udg_FS_Hashtable, ID2, 4, true)
set listmax = listmax + 1
call SaveInteger (udg_FS_Hashtable, -1, 0, listmax)
call SaveInteger (udg_FS_Hashtable, -1, listmax, ID2)
call SaveInteger (udg_FS_Hashtable, ID2, 0, listmax)
endif
call GroupRemoveUnit(g, u)
endloop
endif
endif
endif
// We are now recycling, clearing the used spell instance's ID and flushing
if listmax != i then
call SaveInteger (udg_FS_Hashtable, LoadInteger (udg_FS_Hashtable, -1, listmax), 0, i)
call SaveInteger (udg_FS_Hashtable, -1, i, LoadInteger (udg_FS_Hashtable, -1, listmax))
call RemoveSavedInteger (udg_FS_Hashtable, -1, listmax)
set i = i - 1
endif
set listmax = listmax - 1
call SaveInteger (udg_FS_Hashtable, -1, 0, listmax)
if LoadInteger (udg_FS_Hashtable, 0, 0) + 1 == LoadInteger (udg_FS_Hashtable, 0, -1) then
call FlushChildHashtable (udg_FS_Hashtable, 0)
call PauseTimer (LoadTimerHandle (udg_FS_Hashtable, -2, 0))
else
call SaveInteger (udg_FS_Hashtable, 0, 0, LoadInteger (udg_FS_Hashtable, 0, 0) + 1)
call SaveInteger (udg_FS_Hashtable, 0, LoadInteger (udg_FS_Hashtable, 0, 0), ID)
endif
call FlushChildHashtable (udg_FS_Hashtable, ID)
else
if IsUnitType(target, UNIT_TYPE_DEAD) then
// We are now recycling because the target is dead, also clearing the used spell instance's ID and flushing
call KillUnit (dummy)
if not b then
call DestroyEffect(AddSpecialEffect (FS_Sfx(), x1, y1))
else
call DestroyEffect(AddSpecialEffect (FS_Sfx1(), x1, y1))
endif
if listmax != i then
call SaveInteger (udg_FS_Hashtable, LoadInteger (udg_FS_Hashtable, -1, listmax), 0, i)
call SaveInteger (udg_FS_Hashtable, -1, i, LoadInteger (udg_FS_Hashtable,-1, listmax))
call RemoveSavedInteger (udg_FS_Hashtable, -1, listmax)
set i = i - 1
endif
set listmax = listmax - 1
call SaveInteger (udg_FS_Hashtable, -1, 0, listmax)
if LoadInteger (udg_FS_Hashtable, 0, 0) + 1 == LoadInteger (udg_FS_Hashtable, 0, -1) then
call FlushChildHashtable (udg_FS_Hashtable, 0)
call PauseTimer (LoadTimerHandle (udg_FS_Hashtable, -2, 0))
else
call SaveInteger (udg_FS_Hashtable, 0, 0, LoadInteger(udg_FS_Hashtable, 0, 0) + 1)
call SaveInteger (udg_FS_Hashtable, 0, LoadInteger(udg_FS_Hashtable, 0, 0), ID)
endif
call FlushChildHashtable (udg_FS_Hashtable, ID)
else
// Move all the projectiles!
set angle = Atan2(y2-y1, x2-x1)
call SetUnitX (dummy, x1 + (FS_Speed(i1))*Cos(angle))
call SetUnitY (dummy, y1 + (FS_Speed(i1))*Sin(angle))
call SetUnitFacing (dummy, angle*bj_RADTODEG)
endif
endif
set caster = null
set target = null
set dummy = null
endloop
call DestroyGroup(g)
set g = null
endfunction
// Initiate function
function Trig_FS_Conditions takes nothing returns boolean
local unit caster
local unit target
local integer ID
local integer listmax
local real x1
local real y1
local real x2
local real y2
if GetSpellAbilityId() == FS_GetSpellID() then
set caster = GetTriggerUnit()
set target = GetSpellTargetUnit()
set x1 = GetUnitX (caster)
set y1 = GetUnitY (caster)
set x2 = GetUnitX (target)
set y2 = GetUnitY (target)
set ID = LoadInteger (udg_FS_Hashtable, 0, 0)
// Now we start indexing the main projectiles
if ID>0 then
call SaveInteger (udg_FS_Hashtable, 0, 0, ID - 1)
set ID = LoadInteger (udg_FS_Hashtable, 0, ID)
else
set ID = LoadInteger (udg_FS_Hashtable, 0, -1) + 1
call SaveInteger (udg_FS_Hashtable, 0, -1, ID)
if ID == 1 then
call TimerStart (LoadTimerHandle (udg_FS_Hashtable, -2, 0), FS_Interval(), true, function FS_TimerLoop)
endif
endif
// Save the values related to this spell instance
call SaveUnitHandle (udg_FS_Hashtable, ID, 1, caster)
call SaveUnitHandle (udg_FS_Hashtable, ID, 2, target)
call SaveUnitHandle (udg_FS_Hashtable, ID, 3, CreateUnit(GetTriggerPlayer(), FS_DummyID(), x1, y1, Atan2(y2-y1, x2-x1)*bj_RADTODEG))
set listmax = LoadInteger (udg_FS_Hashtable, -1, 0) + 1
call SaveInteger(udg_FS_Hashtable, -1, 0, listmax)
call SaveInteger (udg_FS_Hashtable, -1, listmax, ID)
call SaveInteger (udg_FS_Hashtable, ID, 0, listmax)
set caster = null
set target = null
endif
return false
endfunction
//===========================================================================
function InitTrig_Frost_Shard takes nothing returns nothing
local trigger FS = CreateTrigger()
set udg_FS_Hashtable = InitHashtable ()
call SaveTimerHandle (udg_FS_Hashtable, -2, 0, CreateTimer())
call TriggerRegisterAnyUnitEventBJ(FS, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(FS, Condition( function Trig_FS_Conditions ) )
set FS = null
endfunction