Oh, firing arrows in a cone-shape degree ?
Let's say fires 5 arrows in 90 degree angle, each arrow is 18 degrees to one another, is that it ?
// 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 "origin"
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
// Offset Angle
constant function V_OffsetAngle takes nothing returns real
return 15.*bj_DEGTORAD
endfunction
// Filter valid units
function V_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 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
// 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), 'h000', 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
// 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, 3)
set level = V_SpellLvl(caster)
set j = 0
loop
exitwhen j == V_ProjectileAmount(level)
set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 9)
set tempGroup[j] = LoadGroupHandle(udg_V_Hashtable, ID, j + 16)
set j = j + 1
endloop
set dummyCaster = LoadUnitHandle(udg_V_Hashtable, ID, 7)
set t = LoadReal(udg_V_Hashtable, ID, 5)
if t >= V_Range(level) then
set j = 0
loop
exitwhen j == V_ProjectileAmount(level)
call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
call KillUnit(dummyProjectile[j])
call DestroyGroup(tempGroup[j])
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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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 == V_ProjectileAmount(level)
call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
call KillUnit(dummyProjectile[j])
call DestroyGroup(tempGroup[j])
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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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()
set j = 0
loop
exitwhen j == V_ProjectileAmount(level)
set r = angle + (j - 3)*V_OffsetAngle()
call SetUnitX(dummyProjectile[j], GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r))
call SetUnitY(dummyProjectile[j], GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r))
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
set j = j + 1
endloop
// Move the projectiles, dealing damage and slowing enemies around
call DestroyGroup(enumGroup)
set enumGroup = null
set t = t + V_Speed(level)
call SaveReal(udg_V_Hashtable, ID, 5, t)
endif
endif
set caster = null
set j = 0
loop
exitwhen j == V_ProjectileAmount(level)
set dummyProjectile[j] = null
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
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)
// 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 == V_ProjectileAmount(V_SpellLvl(caster))
call SaveUnitHandle(udg_V_Hashtable, ID, i + 9, CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i - 3)*V_OffsetAngle())*bj_RADTODEG))
call SaveGroupHandle(udg_V_Hashtable, ID, i + 16, CreateGroup())
set i = i + 1
endloop
call SaveReal(udg_V_Hashtable, ID, 3, angle)
call SaveReal(udg_V_Hashtable, ID, 5, 0)
call SaveUnitHandle(udg_V_Hashtable, ID, 7, CreateUnit(owner, V_DummycasterID(), x1, y1, angle))
call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 7), 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
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
It is not exactly 90 degree but more like the center arrow takes the Atan2() angle and the other ones offset by an integer multiply of OffsetAngle() that goes from -2 to 2 I am terrible at explaning but I think you can understand me
EDIT: Spell made and attached. I made it a bit different from LoL's skill so that you can learn some JASS.
Also here is the code for reference.
JASS:// 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 "origin" 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 // Offset Angle constant function V_OffsetAngle takes nothing returns real return 15.*bj_DEGTORAD endfunction // Filter valid units function V_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 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 // 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), 'h000', 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 // 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, 3) set level = V_SpellLvl(caster) set j = 0 loop exitwhen j == V_ProjectileAmount(level) set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 9) set tempGroup[j] = LoadGroupHandle(udg_V_Hashtable, ID, j + 16) set j = j + 1 endloop set dummyCaster = LoadUnitHandle(udg_V_Hashtable, ID, 7) set t = LoadReal(udg_V_Hashtable, ID, 5) if t >= V_Range(level) then set j = 0 loop exitwhen j == V_ProjectileAmount(level) call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j]))) call KillUnit(dummyProjectile[j]) call DestroyGroup(tempGroup[j]) 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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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 == V_ProjectileAmount(level) call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j]))) call KillUnit(dummyProjectile[j]) call DestroyGroup(tempGroup[j]) 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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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() set j = 0 loop exitwhen j == V_ProjectileAmount(level) set r = angle + (j - 3)*V_OffsetAngle() call SetUnitX(dummyProjectile[j], GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r)) call SetUnitY(dummyProjectile[j], GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r)) 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 set j = j + 1 endloop // Move the projectiles, dealing damage and slowing enemies around call DestroyGroup(enumGroup) set enumGroup = null set t = t + V_Speed(level) call SaveReal(udg_V_Hashtable, ID, 5, t) endif endif set caster = null set j = 0 loop exitwhen j == V_ProjectileAmount(level) set dummyProjectile[j] = null 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 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) // 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 == V_ProjectileAmount(V_SpellLvl(caster)) call SaveUnitHandle(udg_V_Hashtable, ID, i + 9, CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i - 3)*V_OffsetAngle())*bj_RADTODEG)) call SaveGroupHandle(udg_V_Hashtable, ID, i + 16, CreateGroup()) set i = i + 1 endloop call SaveReal(udg_V_Hashtable, ID, 3, angle) call SaveReal(udg_V_Hashtable, ID, 5, 0) call SaveUnitHandle(udg_V_Hashtable, ID, 7, CreateUnit(owner, V_DummycasterID(), x1, y1, angle)) call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 7), 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 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
It would be nice if the slow turns the affected unit blue (like in Halls of the Dead's Slow), and destroy the arrow if it reaches an enemy unit.
// 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 "origin"
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
// Offset Angle
constant function V_OffsetAngle takes nothing returns real
return 15.*bj_DEGTORAD
endfunction
// Filter valid units
function V_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 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
// 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), 'h000', 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 real t
local integer j
local real r
local group tempGroup
// 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 tempGroup = LoadGroupHandle(udg_V_Hashtable, ID, 12)
set j = 0
loop
exitwhen j == V_ProjectileAmount(level)
set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 5)
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 == V_ProjectileAmount(level)
if dummyProjectile[j] != null then
call DestroyEffect(AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
call KillUnit(dummyProjectile[j])
endif
set j = j + 1
endloop
call KillUnit (dummyCaster)
call DestroyGroup (tempGroup)
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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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 == V_ProjectileAmount(level)
if dummyProjectile[j] != null then
call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
call KillUnit(dummyProjectile[j])
endif
set j = j + 1
endloop
call KillUnit (dummyCaster)
call DestroyGroup (tempGroup)
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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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 == V_ProjectileAmount(level)
set r = angle + (j - 3)*V_OffsetAngle()
call SetUnitX(dummyProjectile[j], GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r))
call SetUnitY(dummyProjectile[j], GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r))
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) 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 DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
call KillUnit(dummyProjectile[j])
call GroupAddUnit(tempGroup, u)
endif
call GroupRemoveUnit (enumGroup, u)
endloop
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 dummyCaster = null
set tempGroup = null
set j = 0
loop
exitwhen j == V_ProjectileAmount(level)
set dummyProjectile[j] = null
set j = j + 1
endloop
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
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)
// 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 == V_ProjectileAmount(V_SpellLvl(caster))
call SaveUnitHandle(udg_V_Hashtable, ID, i + 5, CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i - 3)*V_OffsetAngle())*bj_RADTODEG))
set i = i + 1
endloop
call SaveReal(udg_V_Hashtable, ID, 2, angle)
call SaveReal(udg_V_Hashtable, ID, 3, 0)
call SaveGroupHandle(udg_V_Hashtable, ID, 12, CreateGroup())
call SaveUnitHandle(udg_V_Hashtable, ID, 4, CreateUnit(owner, V_DummycasterID(), x1, y1, angle))
call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 7), 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
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