// CONFIGURATION
// Constants
// Main spell raw code
constant function V_GetSpellID takes nothing returns integer
return 'A005'
endfunction
// Slow spell raw code
constant function V_GetSlowID takes nothing returns integer
return 'A000'
endfunction
// Main caster dummy raw code
constant function V_DummycasterID takes nothing returns integer
return 'h000'
endfunction
// Main projectile dummy raw code
constant function V_DummyID takes nothing returns integer
return 'h002'
endfunction
// Main projectile death effect
constant function V_Sfx takes nothing returns string
return "Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl"
endfunction
// Blood effect
constant function V_Sfx1 takes nothing returns string
return "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
endfunction
// Attachment point
constant function V_AttachPoint takes nothing returns string
return "chest"
endfunction
// Order for the custom ability
constant function V_DummyOrder takes nothing returns string
return "slow"
endfunction
// Movement interval
constant function V_Interval takes nothing returns real
return 0.03125
endfunction
// Filter valid units
function V_FilterUnit takes unit caster, unit u returns boolean
return IsUnitEnemy(u, GetOwningPlayer(caster)) and not IsUnitType(u, UNIT_TYPE_DEAD) 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 V_SpellLvl takes unit caster returns integer
return GetUnitAbilityLevel (caster, V_GetSpellID()) // Get the spell level of the caster
endfunction
// Level dependable values
// Main target damage
function V_Damage takes integer level returns real
return 30 + level*10.
endfunction
// Area of Effect
function V_AoE takes integer level returns real
return 50 + level*10.
endfunction
// Projectile Speed
function V_Speed takes integer level returns real
return (1200. + level*200.)/(1/V_Interval())
endfunction
// Projectile Amount
function V_ProjectileAmount takes integer level returns integer
return 7
endfunction
// Offset Angle
function V_OffsetAngle takes integer projectileAmount returns real
if projectileAmount == 1 then
return 0.
elseif projectileAmount < 4 then
return (30./(projectileAmount - 1))*bj_DEGTORAD
endif
return (60./(projectileAmount - 1))*bj_DEGTORAD
endfunction
// Max range allowed
function V_Range takes integer level returns real
return 1200.
endfunction
// Attack - Damage - Weapon type
// Attack type
function V_AttackType takes nothing returns attacktype
return ATTACK_TYPE_HERO
endfunction
// Damage type
function V_DamageType takes nothing returns damagetype
return DAMAGE_TYPE_NORMAL
endfunction
// Weapon type
function V_WeaponType takes nothing returns weapontype
return WEAPON_TYPE_METAL_MEDIUM_SLICE
endfunction
// Preload
function V_Preload takes nothing returns nothing
local unit dummy = CreateUnit(Player(0), V_DummycasterID(), 0, 0, 0.00)
call UnitAddAbility(dummy, V_GetSpellID())
call UnitAddAbility(dummy, V_GetSlowID())
call RemoveUnit (dummy)
set dummy = null
endfunction
// END CONFIGURATION
// SPELL FUNCTIONS
// This is the main function of the spell
function V_TimerLoop takes nothing returns nothing
local integer ID
local integer listmax = LoadInteger(udg_V_Hashtable, -1, 0 )
local unit caster
local unit dummyCaster
local unit array dummyProjectile
local integer i = 0
local unit u
local real angle
local integer level
local group enumGroup
local group array tempGroup
local real t
local integer j
local real r
local integer k
local real x
local real y
// 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_V_Hashtable, -1, i)
set caster = LoadUnitHandle(udg_V_Hashtable, ID, 1)
set angle = LoadReal(udg_V_Hashtable, ID, 2)
set level = V_SpellLvl(caster)
set k = V_ProjectileAmount(level)
set j = 0
loop
exitwhen j == k
set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 5)
set tempGroup[j] = LoadGroupHandle(udg_V_Hashtable, ID, j + (5 + k))
set j = j + 1
endloop
set t = LoadReal(udg_V_Hashtable, ID, 3)
set dummyCaster = LoadUnitHandle(udg_V_Hashtable, ID, 4)
if t >= V_Range(level) then
set j = 0
loop
exitwhen j == k
if dummyProjectile[j] != null then
call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
call KillUnit(dummyProjectile[j])
call DestroyGroup(tempGroup[j])
endif
set j = j + 1
endloop
call KillUnit (dummyCaster)
if listmax != i then
call SaveInteger(udg_V_Hashtable, LoadInteger(udg_V_Hashtable, -1, listmax), 0, i)
call SaveInteger(udg_V_Hashtable, -1, i, LoadInteger(udg_V_Hashtable, -1, listmax))
call RemoveSavedInteger(udg_V_Hashtable, -1, listmax)
set i = i - 1
endif
set listmax = listmax - 1
call SaveInteger(udg_V_Hashtable, -1, 0, listmax)
if LoadInteger(udg_V_Hashtable, 0, 0) + 1 == LoadInteger(udg_V_Hashtable, 0, -1) then
call FlushChildHashtable(udg_V_Hashtable, 0)
call PauseTimer(LoadTimerHandle(udg_V_Hashtable, -2, 0))
else
call SaveInteger (udg_V_Hashtable, 0, 0, LoadInteger(udg_V_Hashtable, 0, 0) + 1)
call SaveInteger(udg_V_Hashtable, 0, LoadInteger(udg_V_Hashtable, 0, 0), ID)
endif
call FlushChildHashtable(udg_V_Hashtable, ID)
else
if IsUnitType(caster, UNIT_TYPE_DEAD) then
set j = 0
loop
exitwhen j == k
if dummyProjectile[j] != null then
call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
call KillUnit(dummyProjectile[j])
call DestroyGroup(tempGroup[j])
set tempGroup[j] = null
endif
set j = j + 1
endloop
call KillUnit (dummyCaster)
if listmax != i then
call SaveInteger(udg_V_Hashtable, LoadInteger(udg_V_Hashtable, -1 ,listmax), 0, i)
call SaveInteger(udg_V_Hashtable, -1, i, LoadInteger(udg_V_Hashtable, -1, listmax))
call RemoveSavedInteger(udg_V_Hashtable, -1, listmax)
set i = i - 1
endif
set listmax = listmax - 1
call SaveInteger(udg_V_Hashtable, -1, 0, listmax)
if LoadInteger(udg_V_Hashtable, 0, 0) + 1 == LoadInteger(udg_V_Hashtable, 0, -1) then
call FlushChildHashtable(udg_V_Hashtable, 0)
call PauseTimer(LoadTimerHandle(udg_V_Hashtable, -2, 0))
else
call SaveInteger (udg_V_Hashtable, 0, 0, LoadInteger(udg_V_Hashtable, 0, 0) + 1)
call SaveInteger(udg_V_Hashtable, 0, LoadInteger(udg_V_Hashtable, 0, 0), ID)
endif
call FlushChildHashtable(udg_V_Hashtable, ID)
else
set enumGroup = CreateGroup()
// Move the projectiles, dealing damage and slowing enemies
set j = 0
loop
exitwhen j == k
set r = angle + (j*1. - (k/2. - 0.5))*V_OffsetAngle(k)
set x = GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r)
set y = GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r)
if IsTerrainPathable (x, y, PATHING_TYPE_WALKABILITY) and dummyProjectile[j] != null then
call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
call KillUnit(dummyProjectile[j])
call DestroyGroup(tempGroup[j])
set tempGroup[j] = null
else
call SetUnitX(dummyProjectile[j], x)
call SetUnitY(dummyProjectile[j], y)
call GroupEnumUnitsInRange(enumGroup, GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j]), V_AoE(level), null)
loop
set u = FirstOfGroup(enumGroup)
exitwhen u == null
if V_FilterUnit(caster, u) and not IsUnitInGroup(u, tempGroup[j]) then
call UnitDamageTarget(caster, u, V_Damage(level), false, false, V_AttackType(), V_DamageType(), V_WeaponType())
call DestroyEffect (AddSpecialEffectTarget(V_Sfx(), u, V_AttachPoint()))
call DestroyEffect (AddSpecialEffectTarget(V_Sfx1(), u, V_AttachPoint()))
call SetUnitX(dummyCaster, GetUnitX(u))
call SetUnitY(dummyCaster, GetUnitY(u))
call SetUnitAbilityLevel(dummyCaster, V_GetSlowID(), level)
call IssueTargetOrder(dummyCaster, V_DummyOrder(), u)
call GroupAddUnit(tempGroup[j], u)
endif
call GroupRemoveUnit (enumGroup, u)
endloop
endif
set j = j + 1
endloop
call DestroyGroup(enumGroup)
set enumGroup = null
set t = t + V_Speed(level)
call SaveReal(udg_V_Hashtable, ID, 3, t)
endif
endif
set caster = null
set j = 0
loop
exitwhen j == k
set tempGroup[j] = null
set j = j + 1
endloop
set dummyCaster = null
endloop
endfunction
// Initiate function
function Trig_V_Conditions takes nothing returns boolean
local unit caster
local integer ID
local integer listmax
local real x1
local real y1
local real x2
local real y2
local real angle
local player owner
local integer i
local integer j
local unit dummy
if GetSpellAbilityId() == V_GetSpellID() then
set caster = GetTriggerUnit()
set x1 = GetUnitX(caster)
set y1 = GetUnitY(caster)
set x2 = GetSpellTargetX()
set y2 = GetSpellTargetY()
set angle = Atan2(y2-y1, x2-x1)
set owner = GetTriggerPlayer()
set ID = LoadInteger(udg_V_Hashtable, 0, 0)
set j = V_ProjectileAmount(V_SpellLvl(caster))
// Now we start indexing the spell
if ID>0 then
call SaveInteger(udg_V_Hashtable, 0, 0, ID - 1)
set ID = LoadInteger (udg_V_Hashtable, 0, ID)
else
set ID = LoadInteger(udg_V_Hashtable, 0, -1) + 1
call SaveInteger(udg_V_Hashtable,0 , -1, ID)
if ID == 1 then
call TimerStart (LoadTimerHandle(udg_V_Hashtable, -2, 0), V_Interval(), true, function V_TimerLoop)
endif
endif
// Save the values related to this spell instance
call SaveUnitHandle(udg_V_Hashtable, ID, 1, caster)
set i = 0
loop
exitwhen i == j
set dummy = CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i*1. - (j/2. - 0.5))*V_OffsetAngle(j))*bj_RADTODEG)
call UnitAddAbility (dummy, 'Arav')
call UnitRemoveAbility (dummy, 'Arav')
call SetUnitFlyHeight (dummy, 100, 0.00)
call SaveUnitHandle(udg_V_Hashtable, ID, i + 5, dummy)
call SaveGroupHandle(udg_V_Hashtable, ID, i + (5 + j), CreateGroup())
set i = i + 1
endloop
call SaveReal(udg_V_Hashtable, ID, 2, angle)
call SaveReal(udg_V_Hashtable, ID, 3, 0)
call SaveUnitHandle(udg_V_Hashtable, ID, 4, CreateUnit(owner, V_DummycasterID(), x1, y1, angle))
call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 4), V_GetSlowID())
set listmax = LoadInteger (udg_V_Hashtable, -1, 0) + 1
call SaveInteger (udg_V_Hashtable, -1, 0, listmax )
call SaveInteger (udg_V_Hashtable, -1, listmax, ID)
call SaveInteger (udg_V_Hashtable, ID, 0, listmax)
set caster = null
set owner = null
set dummy = null
endif
return false
endfunction
//===========================================================================
function InitTrig_Volley takes nothing returns nothing
local trigger t = CreateTrigger()
set udg_V_Hashtable = InitHashtable ()
call V_Preload()
call SaveTimerHandle (udg_V_Hashtable, -2, 0, CreateTimer())
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition( function Trig_V_Conditions ) )
set t = null
endfunction