// CONFIGURATION
// Constants
// Main spell raw code
constant function SS_GetSpellID takes nothing returns integer
return 'A005'
endfunction
// Main projectile dummy raw code
constant function SS_DummyID takes nothing returns integer
return 'h002'
endfunction
// Main projectile model effect
constant function SS_Sfx takes nothing returns string
return "Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl"
endfunction
// Blood effect
constant function SS_Sfx1 takes nothing returns string
return "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
endfunction
// Attachment point
constant function SS_AttachPoint takes nothing returns string
return "origin"
endfunction
// Movement interval
constant function SS_Interval takes nothing returns real
return 0.03125
endfunction
// Should a unit be hit only once or not?
// True = yes, a unit will be hit only once
// False = no, a unit can be hit multiple times
constant function SS_HitOnce takes nothing returns boolean
return false
endfunction
// Should smart homing be enabled?
// True = enable smart homing, the shuriken will change target if the current homed unit is dead
// False = disable smart homing
constant function SS_SmartHoming takes nothing returns boolean
return true
endfunction
// Filter valid units
function SS_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 SS_SpellLvl takes unit caster returns integer
return GetUnitAbilityLevel (caster, SS_GetSpellID()) // Get the spell level of the caster
endfunction
// Level dependable values
// Main target damage
function SS_Damage takes integer level returns real
return 100 + level*50.
endfunction
// The damage change (%) for each target after the last
// Set to positive if you want the spell to deals lesser damage with each bounce
// Set to negative if you want the spell to deals greater damage with each bounce
function SS_DamageRate takes integer level returns real
return 11 - level*1.
endfunction
// Area of Effect
function SS_AoE takes integer level returns real
return 500 + level*100.
endfunction
// Number of bounces
function SS_Bounce takes integer level returns integer
return 5 + level
endfunction
// Projectile Speed
function SS_Speed takes integer level returns real
return (800. + level*150.)/(1/SS_Interval())
endfunction
// Attack - Damage - Weapon type
// Attack type
function SS_AttackType takes nothing returns attacktype
return ATTACK_TYPE_NORMAL
endfunction
// Damage type
function SS_DamageType takes nothing returns damagetype
return DAMAGE_TYPE_MAGIC
endfunction
// Weapon type
function SS_WeaponType takes nothing returns weapontype
return WEAPON_TYPE_METAL_MEDIUM_SLICE
endfunction
// END CONFIGURATION
// SPELL FUNCTIONS
// Here we create a list of units that can be picked from
function SS_MakeList takes nothing returns boolean
local unit u = GetFilterUnit()
local integer i = LoadInteger(udg_SS_Hashtable, -4, 0)
local integer ID = LoadInteger(udg_SS_Hashtable, -4, -3)
local boolean b = SS_FilterUnit(LoadUnitHandle(udg_SS_Hashtable, -4, -2), u) and u != LoadUnitHandle(udg_SS_Hashtable, -4, -1)
if not SS_HitOnce() then
if b then
set i = i + 1
call SaveUnitHandle(udg_SS_Hashtable, -4, i, u)
endif
else
if b and not IsUnitInGroup(u, LoadGroupHandle(udg_SS_Hashtable, ID, 6)) then
set i = i + 1
call SaveUnitHandle(udg_SS_Hashtable, -4, i, u)
endif
endif
call SaveInteger(udg_SS_Hashtable, -4, 0, i)
set u = null
return false
endfunction
// The loop where we index, deindex, and recycle everything
// This is also the main function of the spell
function SS_TimerLoop takes nothing returns nothing
local integer ID
local integer listmax = LoadInteger(udg_SS_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 angle
local integer i1
local group g
local group g1
local integer i2
local integer i3
// 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_SS_Hashtable, -1, i)
set caster = LoadUnitHandle(udg_SS_Hashtable, ID, 1)
set target = LoadUnitHandle(udg_SS_Hashtable, ID, 2)
set dummy = LoadUnitHandle(udg_SS_Hashtable, ID, 3)
set g1 = LoadGroupHandle(udg_SS_Hashtable, ID, 6)
set x1 = GetUnitX(dummy)
set y1 = GetUnitY(dummy)
set x2 = GetUnitX(target)
set y2 = GetUnitY(target)
set i1 = SS_SpellLvl(caster)
set i2 = LoadInteger (udg_SS_Hashtable, ID, 5)
if (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) < 900 then
if caster != target then
// The shuriken collided with an enemy so we will deal damage
call DestroyEffect (AddSpecialEffectTarget(SS_Sfx1(), target, SS_AttachPoint()))
call DestroyEffect (AddSpecialEffectTarget(SS_Sfx(), target, SS_AttachPoint()))
call UnitDamageTarget(caster, target, SS_Damage(i1)*Pow(1 - (SS_DamageRate(i1)/100), SS_Bounce(i1)-i2), false, false, SS_AttackType(), SS_DamageType(), SS_WeaponType())
if SS_HitOnce() then
call GroupAddUnit(g1,target)
endif
else
// The shuriken returned to the caster and thus we will destroy the dummy and begin deindexing and recycling
call KillUnit (dummy)
call DestroyEffect (AddSpecialEffect(SS_Sfx(), x1, y1))
call DestroyGroup (g1)
if listmax != i then
call SaveInteger(udg_SS_Hashtable, LoadInteger(udg_SS_Hashtable, -1 , listmax), 0, i)
call SaveInteger(udg_SS_Hashtable, -1, i, LoadInteger(udg_SS_Hashtable, -1, listmax))
call RemoveSavedInteger(udg_SS_Hashtable, -1, listmax)
set i = i -1
endif
set listmax = listmax -1
call SaveInteger(udg_SS_Hashtable, -1, 0, listmax )
if LoadInteger(udg_SS_Hashtable, 0, 0) + 1 == LoadInteger(udg_SS_Hashtable, 0, -1) then
call FlushChildHashtable(udg_SS_Hashtable, 0)
call PauseTimer(LoadTimerHandle(udg_SS_Hashtable, -2, 0))
else
call SaveInteger (udg_SS_Hashtable, 0, 0, LoadInteger(udg_SS_Hashtable, 0, 0) + 1)
call SaveInteger(udg_SS_Hashtable, 0, LoadInteger(udg_SS_Hashtable, 0, 0), ID)
endif
call FlushChildHashtable(udg_SS_Hashtable, ID)
endif
// Now we check how many bounces are left and choose the next target
if i2 > 1 then
set g = CreateGroup()
call SaveUnitHandle(udg_SS_Hashtable, -4, -1, target)
call SaveUnitHandle(udg_SS_Hashtable, -4, -2, caster)
call SaveInteger(udg_SS_Hashtable, -4, -3, ID)
call GroupEnumUnitsInRange(g, x1, y1, SS_AoE(i1), Condition(function SS_MakeList))
call DestroyGroup(g)
set g = null
set i3 = LoadInteger(udg_SS_Hashtable, -4, 0)
if i3 == 0 then
call SaveUnitHandle(udg_SS_Hashtable, ID, 2, caster)
else
call SaveUnitHandle(udg_SS_Hashtable, ID, 2, LoadUnitHandle(udg_SS_Hashtable, -4, GetRandomInt(1, i3)))
call SaveInteger(udg_SS_Hashtable, ID, 5, i2 - 1)
call FlushChildHashtable(udg_SS_Hashtable, -4)
endif
else
call SaveUnitHandle(udg_SS_Hashtable, ID, 2, caster)
endif
else
if IsUnitType(caster, UNIT_TYPE_DEAD) then
call KillUnit (dummy)
call DestroyEffect (AddSpecialEffect(SS_Sfx(), x1, y1))
call DestroyGroup (g1)
if listmax != i then
call SaveInteger(udg_SS_Hashtable, LoadInteger(udg_SS_Hashtable, -1 , listmax), 0, i)
call SaveInteger(udg_SS_Hashtable, -1, i, LoadInteger(udg_SS_Hashtable, -1, listmax))
call RemoveSavedInteger(udg_SS_Hashtable, -1, listmax)
set i = i -1
endif
set listmax = listmax -1
call SaveInteger(udg_SS_Hashtable, -1, 0, listmax)
if LoadInteger(udg_SS_Hashtable, 0, 0) + 1 == LoadInteger(udg_SS_Hashtable, 0, -1) then
call FlushChildHashtable(udg_SS_Hashtable, 0)
call PauseTimer(LoadTimerHandle(udg_SS_Hashtable, -2, 0))
else
call SaveInteger (udg_SS_Hashtable, 0, 0, LoadInteger(udg_SS_Hashtable, 0, 0) + 1)
call SaveInteger(udg_SS_Hashtable, 0, LoadInteger(udg_SS_Hashtable, 0, 0), ID)
endif
call FlushChildHashtable(udg_SS_Hashtable, ID)
endif
if IsUnitType(target, UNIT_TYPE_DEAD) then
if SS_SmartHoming() then
set g = CreateGroup()
call SaveUnitHandle(udg_SS_Hashtable, -4, -1, target)
call SaveUnitHandle(udg_SS_Hashtable, -4, -2, caster)
call SaveInteger(udg_SS_Hashtable, -4, -3, ID)
call GroupEnumUnitsInRange(g, x1, y1, SS_AoE(i1), Condition(function SS_MakeList))
call DestroyGroup(g)
set g = null
set i3 = LoadInteger(udg_SS_Hashtable, -4, 0)
if i3 == 0 then
call SaveUnitHandle(udg_SS_Hashtable, ID, 2, caster)
else
call SaveUnitHandle(udg_SS_Hashtable, ID, 2, LoadUnitHandle(udg_SS_Hashtable, -4, GetRandomInt(1, i3)))
call SaveInteger(udg_SS_Hashtable, ID, 5, i2 - 1)
call FlushChildHashtable(udg_SS_Hashtable, -4)
endif
else
call SaveUnitHandle(udg_SS_Hashtable, ID, 2, caster)
endif
else
// Move all the projectiles!
set angle = Atan2(y2-y1, x2-x1)
call SetUnitX(dummy, x1 + (SS_Speed(i1))*Cos(angle))
call SetUnitY(dummy, y1 + (SS_Speed(i1))*Sin(angle))
call SetUnitFacing(dummy, angle*bj_RADTODEG)
endif
endif
set caster = null
set target = null
set dummy = null
set g1 = null
endloop
endfunction
// Initiate function
function Trig_SS_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() == SS_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_SS_Hashtable, 0, 0)
// Now we start indexing the spell
if ID>0 then
call SaveInteger(udg_SS_Hashtable, 0, 0, ID - 1)
set ID = LoadInteger (udg_SS_Hashtable, 0, ID)
else
set ID = LoadInteger(udg_SS_Hashtable, 0, -1) + 1
call SaveInteger(udg_SS_Hashtable, 0,-1, ID)
if ID == 1 then
call TimerStart (LoadTimerHandle(udg_SS_Hashtable, -2, 0), SS_Interval(), true, function SS_TimerLoop)
endif
endif
// Save the values related to this spell instance
call SaveUnitHandle(udg_SS_Hashtable, ID, 1, caster)
call SaveUnitHandle(udg_SS_Hashtable, ID, 2, target)
call SaveUnitHandle (udg_SS_Hashtable, ID, 3, CreateUnit(GetTriggerPlayer(), SS_DummyID(), x1, y1, Atan2(y2-y1, x2-x1)*bj_RADTODEG))
call SaveInteger(udg_SS_Hashtable, ID, 5, SS_Bounce(SS_SpellLvl(caster)))
call SaveGroupHandle(udg_SS_Hashtable, ID, 6, CreateGroup())
set listmax = LoadInteger (udg_SS_Hashtable, -1, 0) + 1
call SaveInteger( udg_SS_Hashtable, -1, 0, listmax )
call SaveInteger (udg_SS_Hashtable, -1, listmax, ID)
call SaveInteger (udg_SS_Hashtable, ID, 0, listmax)
set caster = null
set target = null
endif
return false
endfunction
//===========================================================================
function InitTrig_Shuriken_Strike takes nothing returns nothing
local trigger SS = CreateTrigger()
set udg_SS_Hashtable = InitHashtable ()
call SaveTimerHandle (udg_SS_Hashtable, -2, 0, CreateTimer())
call TriggerRegisterAnyUnitEventBJ(SS, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(SS, Condition( function Trig_SS_Conditions ) )
set SS = null
endfunction