////////////////////////////////////////////////////////////
// Forest Soul v1.06 by Meatmuffin
// Conjures the very essence of the forest and sends it
// to the target enemy. Once it reaches the target, it will
// circle around it, taking its life force and sending it
// to the caster. After that, returns to the caster and
// repeats the same action. Meanwhile the forest spirits
// guard the caster, pushing away enemies based on the time
// the soul was circling.
//
// The functions below are meant for configuration only.
// Anything below the configuration can be changed,
// although it does take some programming knowledge to do so.
//
////////////// HOW TO IMPORT ////////////////
// 1. Go to File -> Preferences and tick the box that says:
// "Automatically create unknown variables when pasting trigger
// data"
//
// 2. Copy the dummy unit (if you don't have one) and the
// ability and match their ID's with the configuration.
//
// 3. Copy the "Forest Soul" category into your map along with
// the variable creator. Variable creator creates variables for
// you so you don't have to manually create them yourself.
//
// 4. Configure the spell as it fits best for your map.
//
// 5. Enjoy!
//
////////////// CONFIGURATION ////////////////
//
//
// The looping speed, this indicates the time between
// effects of the spell, how many times per second does
// it loop. 0.031250000 is recommended because it is
// exactly 32 times per second. Other values should fit
// in between 0.03 and 0.04. Outside those, it becomes
// inefficient.
constant function FS_TimerSpeed takes nothing returns real
return 0.031250000
endfunction
// The Dummy ID, this indicates what type of dummy we are
// going to use in the spell. Current dummy is using an
// imported model which was made by Vexorian/Anitarf/Infrane,
// import this if you don't have it already in your map. Make
// sure to match the ID of the dummy with the ID below.
constant function FS_DummyID takes nothing returns integer
return 'h000'
endfunction
// The ability ID, this indicates what ability this code is
// based on. Once again, make sure to match this ID with
// the one your ability has.
constant function FS_AbilityID takes nothing returns integer
return 'A000'
endfunction
// The dummy player, this is for what player we are creating the
// dummy, it is currently set to "Neutral Passive" so that it
// wouldn't mess up any statistics ingame. This is okay because
// we damage target directly with the caster.
constant function FS_DummyPlayer takes nothing returns player
return Player(14)
endfunction
// The channel ID that is used when making comparisons if the
// caster is still chanelling.
constant function FS_ChannelID takes nothing returns integer
return 852600
endfunction
// The initial duration, change this to your wanted initial
// duration.
constant function FS_InitialDuration takes nothing returns real
return 5.00
endfunction
// Duration per level, change this to how much seconds you want
// to add to the duration per level.
constant function FS_DurationPerLevel takes nothing returns real
return 5.00
endfunction
// Initial damage per second, change this to your wanted initial
// damage per second.
constant function FS_DPSInitial takes nothing returns real
return 0.00 * FS_TimerSpeed()
endfunction
// Damage per second per level, change this to how much damage
// you want to add to the damage per level.
constant function FS_DPSPerLevel takes nothing returns real
return 25.00 * FS_TimerSpeed()
endfunction
// The damage type, what type of damage you want to use against
// your enemy.
constant function FS_DamageType takes nothing returns damagetype
return DAMAGE_TYPE_MAGIC
endfunction
// The attack type, what type of attack you want to use against
// your enemy.
constant function FS_AttackType takes nothing returns attacktype
return ATTACK_TYPE_NORMAL
endfunction
// The weapon type, what kind of weapon(?) you want to use against
// your enemy.
constant function FS_WeaponType takes nothing returns weapontype
return WEAPON_TYPE_WHOKNOWS
endfunction
// This indicates the initial speed of the soul in the first phase.
// The first phase is when the soul is being sent to the enemy.
constant function FS_FirstPhaseInitialSpeed takes nothing returns real
return 200.00 * FS_TimerSpeed()
endfunction
// This indicates the speed per level of the soul in the first phase.
constant function FS_FirstPhaseSpeedPerLevel takes nothing returns real
return 100.00 * FS_TimerSpeed()
endfunction
// This indicates the max height you want your soul to reach in the
// first phase. Note that this doesn't actually change the speed, just
// the aesthetic.
constant function FS_ParabolaHeight takes nothing returns real
return 300.00
endfunction
// This indicates the initial duration of the second phase. The second
// phase is when the soul is circuling around the target, draining life.
constant function FS_SecondPhaseInitialDuration takes nothing returns real
return 0.00
endfunction
// This indicates the duration per level of the second phase.
constant function FS_SecondPhaseDurationPerLevel takes nothing returns real
return 2.00
endfunction
// This indicates how fast is the soul circling around the target in the
// second phase. 360.00 - it makes a full circle in 1 second.
constant function FS_SecondPhaseAngleCircleRate takes nothing returns real
return 180.00 * FS_TimerSpeed()
endfunction
// This indicates how much height are we going to add in addition to the
// flying height of the target. At the calculated value, the missile
// hovers (phase 2).
constant function FS_SecondPhaseSoulHeight takes nothing returns real
return 50.00
endfunction
// When the soul transitions into phase 2, it has to drop(jump) to the
// height indicated above. This indicates that drop/jump rate. Default
// is set to 1 second (rate and height both match).
constant function FS_SecondPhaseTransitionRate takes nothing returns real
return FS_SecondPhaseSoulHeight() * 1.00
endfunction
// This indicates the distance between the soul and the target, it is also
// when the soul will transition from the first to the second phase.
constant function FS_CirclingRadius takes nothing returns real
return 200.00
endfunction
// This indicates the inital speed of the soul in the third phase. The
// third phase is when the soul is going back to the caster.
constant function FS_ThirdPhaseInitialSpeed takes nothing returns real
return 300.00 * FS_TimerSpeed()
endfunction
// This indicates the speed per level of the soul in the third phase.
constant function FS_ThirdPhaseSpeedPerLevel takes nothing returns real
return 175.00 * FS_TimerSpeed()
endfunction
// This indicates how small does the distance between the soul and the
// caster has to be in the third phase to transition into the first one.
constant function FS_ThirdPhaseReturnRadius takes nothing returns real
return 75.00
endfunction
// This indicates the model of the soul. It isn't set in the object
// editor because "Dummy.mdx" takes that spot. We just attach the
// effect which is much more simple.
constant function FS_MissileModel takes nothing returns string
return "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
endfunction
// This indicates the size of the soul. 1.00 - normal size.
constant function FS_MissileSize takes nothing returns real
return 2.00
endfunction
// This indicates the attachment point that we are going to use for our
// selected special effect for the soul.
constant function FS_MissileAttachmentPoint takes nothing returns string
return "origin"
endfunction
// This indicates the attachment point that we are going to use for our
// selected special effect for the indicator.
constant function FS_IndicatorAttachmentPoint takes nothing returns string
return "origin"
endfunction
// This indicates the primary protection AoE around the caster. Units
// that come within the protection AoE are being pushed back.
constant function FS_PrimaryProtectionAoe takes nothing returns real
return 200.00
endfunction
// This indicates how much of the duration is converted to protection
// AoE when the soul gets back to the caster. The value entered must
// match how much you want it to be converted over 1 second.
constant function FS_DurationToProtectionAoe takes nothing returns real
return 75.00
endfunction
// This indicates the initial pushback per second. The pushback happens
// throughout the spell, it pushes back enemies that come close to the
// caster.
constant function FS_InitialPushbackPerSecond takes nothing returns real
return 0.00
endfunction
// This indicates the pushback per second per level.
constant function FS_PushbackPerSecondPerLevel takes nothing returns real
return 100.00 * FS_TimerSpeed()
endfunction
// This indicates the model that is going to represent our pushback boundries.
constant function FS_PushbackIndicatorModel takes nothing returns string
return "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
endfunction
// This indicates the size of the pushback model.
constant function FS_PushbackIndicatorSize takes nothing returns real
return 0.25
endfunction
// This indicates how fast is the indicator turning. 360 - it makes a full
// circle over 1 second.
constant function FS_PushbackIndicatorTurnRate takes nothing returns real
return 720.00 * FS_TimerSpeed()
endfunction
// This indicates the draining special effect. Both the caster and the target
// have this effect.
constant function FS_DrainingEffect takes nothing returns string
return "Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl"
endfunction
// This indicates at which attachment point does the draining effect happen.
// "origin" happens at the feet of the affected unit.
constant function FS_DrainingEffectLoc takes nothing returns string
return "origin"
endfunction
// This indicates how fast is the effect made. I included this because it
// can become quite spammy. It is currently set to each 5 times the loop fires.
constant function FS_EffectInterval takes nothing returns real
return FS_TimerSpeed() * 5.00
endfunction
// This indicates how fast is the effect made when pushbacking the enemies.
// This was implemented for the save reason as above.
constant function FS_PushbackEffectInterval takes nothing returns real
return FS_TimerSpeed() * 5.00
endfunction
// This indicates the effect that is used on the units that are being
// pushed back.
constant function FS_PushbackEffect takes nothing returns string
return "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
endfunction
// This indicates at which attachment point does the pushback effect happen.
// Once again, "origin" happens at the feet of the affected unit.
constant function FS_PushbackEffectLoc takes nothing returns string
return "origin"
endfunction
// This indicates how high is the pushback boundry indicator hovering.
// It is recommended not to set it too high because it can screw up the
// aesthetic.
constant function FS_IndicatorHeight takes nothing returns real
return 50.00
endfunction
// This indicates the integer ID that we are going to use to apply 0.01
// timed life to units that no longer have a use.
constant function FS_DeathID takes nothing returns integer
return 'BTLF'
endfunction
// This indicates the breaking range of the spell. If the target moves
// out of this rangem the spell breaks. It can only break on phase 3.
constant function FS_MaxDistance takes nothing returns real
return 1500.00
endfunction
// This allows you to switch between if you want the spell to break when
// regarding distance or not.
constant function FS_EnableMaxDistance takes nothing returns boolean
return false
endfunction
////////////////////////// END OF CONFIGURATION /////////////////////////
// Pushback target filter. If a unit passes all the conditions, it gets
// pushed back.
function FS_TargetFilter takes unit u, player pl returns boolean
return not(IsUnitType(u, UNIT_TYPE_DEAD) == true) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE) == true) and IsUnitEnemy(u, pl) == true
endfunction
// Function to check if the unit is already channeling.
// This is used to prevent it from affecting multiple units at once.
function FS_CheckChannel takes unit u returns nothing
local integer Node = 0
loop
set Node = udg_FS_NextNode[Node]
exitwhen (udg_FS_Caster[Node] == u) or (Node == 0)
endloop
set udg_FS_Duration[Node] = 0.00
endfunction
// Function used to get the terrain height at the used point.
function FS_GetLocZ takes real x, real y returns real
call MoveLocation(udg_FS_Loc, x, y)
return GetLocationZ(udg_FS_Loc)
endfunction
// The main function of the spell (loop). Each (timer speed) seconds
// this function runs.
function FS_Loop takes nothing returns nothing
// Setting up locals
local real x
local real y
local real z
local real x2
local real y2
local real z2
local real dx
local real dy
local real totalD
local real angle
local integer Node = 0
local unit u
// Initializing the loop.
loop
// Cycling through each node.
set Node = udg_FS_NextNode[Node]
exitwhen Node == 0
// Subtracting the duration by the timer speed.
set udg_FS_Duration[Node] = udg_FS_Duration[Node] - FS_TimerSpeed()
// Checking if all the arguments are met (Both the target and the caster are alive,
// current order is channel (852600) and the duration hasn't expired yet.)
if udg_FS_Duration[Node] > 0.00 and GetUnitCurrentOrder(udg_FS_Caster[Node]) == FS_ChannelID() and not IsUnitType(udg_FS_Caster[Node], UNIT_TYPE_DEAD) and GetUnitTypeId(udg_FS_Caster[Node]) != 0 and not IsUnitType(udg_FS_Target[Node], UNIT_TYPE_DEAD) and GetUnitTypeId(udg_FS_Target[Node]) != 0 then
// Setting up all the coordinates
set x = GetUnitX(udg_FS_Caster[Node])
set y = GetUnitY(udg_FS_Caster[Node])
set x2 = GetUnitX(udg_FS_Target[Node])
set y2 = GetUnitY(udg_FS_Target[Node])
// Rotating the indicator to make it look more appealing.
set udg_FS_Angle[Node] = udg_FS_Angle[Node] - FS_PushbackIndicatorTurnRate() * bj_DEGTORAD
call SetUnitX(udg_FS_Indicator[Node], x + udg_FS_IndicatorAoE[Node] * Cos(udg_FS_Angle[Node]))
call SetUnitY(udg_FS_Indicator[Node], y + udg_FS_IndicatorAoE[Node] * Sin(udg_FS_Angle[Node]))
// Calculating the distances. We do not need to calculate them in the second phase
// so we use the "or" function to check if the current phase is either 1 or 3.
if udg_FS_CurrentPhase[Node] == 1 or udg_FS_CurrentPhase[Node] == 3 then
set z = FS_GetLocZ(x, y) + GetUnitFlyHeight(udg_FS_Caster[Node])
set z2 = FS_GetLocZ(x2, y2) + GetUnitFlyHeight(udg_FS_Target[Node])
set dx = x2 - x
set dy = y2 - y
set totalD = SquareRoot(dx * dx + dy * dy)
// Adding up to the partial distance.
if udg_FS_CurrentPhase[Node] == 1 then
set udg_FS_PartialD[Node] = udg_FS_PartialD[Node] + udg_FS_FirstPhaseSpeed[Node]
else
set udg_FS_PartialD[Node] = udg_FS_PartialD[Node] + udg_FS_ThirdPhaseSpeed[Node]
endif
// Current phase is 2, execute the second phase.
else
// Setting up the angle and rotating the soul accordingly.
set udg_FS_SoulAngle[Node] = udg_FS_SoulAngle[Node] + FS_SecondPhaseAngleCircleRate() * bj_DEGTORAD
call SetUnitX(udg_FS_Soul[Node], x2 + FS_CirclingRadius() * Cos(udg_FS_SoulAngle[Node]))
call SetUnitY(udg_FS_Soul[Node], y2 + FS_CirclingRadius() * Sin(udg_FS_SoulAngle[Node]))
// Damaging the target and healing the caster.
call UnitDamageTarget(udg_FS_Caster[Node], udg_FS_Target[Node], udg_FS_Damage[Node], true, false, FS_AttackType(), FS_DamageType(), FS_WeaponType())
call SetWidgetLife(udg_FS_Caster[Node], GetWidgetLife(udg_FS_Caster[Node]) + udg_FS_Damage[Node])
// Counting down the duration and using the effect at the end to make it less spammy.
set udg_FS_EffectDuration[Node] = udg_FS_EffectDuration[Node] - FS_TimerSpeed()
if udg_FS_EffectDuration[Node] < 0.00 then
call DestroyEffect(AddSpecialEffectTarget(FS_DrainingEffect(), udg_FS_Target[Node], FS_DrainingEffectLoc()))
call DestroyEffect(AddSpecialEffectTarget(FS_DrainingEffect(), udg_FS_Caster[Node], FS_DrainingEffectLoc()))
set udg_FS_EffectDuration[Node] = FS_EffectInterval()
endif
// Counting down the second phase duration.
set udg_FS_SecondPhase[Node] = udg_FS_SecondPhase[Node] - FS_TimerSpeed()
if udg_FS_SecondPhase[Node] < 0.00 then
// Second phase has ended, preparing for phase 3.
set udg_FS_SecondPhase[Node] = FS_SecondPhaseInitialDuration() + FS_SecondPhaseDurationPerLevel() * udg_FS_AbilityLevel[Node]
set udg_FS_CurrentPhase[Node] = 3
set udg_FS_PartialD[Node] = FS_CirclingRadius()
endif
endif
// Curent phase is 1, execute the first phase.
if udg_FS_CurrentPhase[Node] == 1 then
// Setting up the angle and moving the soul accordingly.
set udg_FS_SoulAngle[Node] = Atan2(y2 - y, x2 - x)
call SetUnitX(udg_FS_Soul[Node], x + udg_FS_PartialD[Node] * Cos(udg_FS_SoulAngle[Node]))
call SetUnitY(udg_FS_Soul[Node], y + udg_FS_PartialD[Node] * Sin(udg_FS_SoulAngle[Node]))
// Calculating the parabola and using it to make it look like the soul is being actually thrown.
call SetUnitFlyHeight(udg_FS_Soul[Node], 4.00 * FS_ParabolaHeight() / totalD * (totalD - udg_FS_PartialD[Node]) * (udg_FS_PartialD[Node] / totalD) + udg_FS_PartialD[Node] * (z2 - z) / (totalD + 1.00) + z, 0.00)
// Checking if the soul has reached the target.
if totalD - udg_FS_PartialD[Node] < FS_CirclingRadius() then
// The soul has reached the target, transition into phase 2.
call SetUnitFlyHeight(udg_FS_Soul[Node], GetUnitFlyHeight(udg_FS_Target[Node]) + FS_SecondPhaseSoulHeight(), FS_SecondPhaseTransitionRate())
set udg_FS_PartialD[Node] = 0.00
set udg_FS_CurrentPhase[Node] = 2
set udg_FS_SoulAngle[Node] = Atan2(y - y2, x - x2)
endif
// Current phase is 3, execute the third phase.
elseif udg_FS_CurrentPhase[Node] == 3 then
// Gradually setting the height to that of the caster's.
if z2 < z then
call SetUnitFlyHeight(udg_FS_Soul[Node], ((totalD - udg_FS_PartialD[Node]) * (z2 - z + 1.00) / totalD) + z, 0.00)
else
call SetUnitFlyHeight(udg_FS_Soul[Node], ((totalD - udg_FS_PartialD[Node]) * (z2 + 1.00) / totalD) + z + 50.00, 0.00)
endif
// Setting up the angle and moving the soul accordingly.
set udg_FS_SoulAngle[Node] = Atan2(y - y2, x - x2)
call SetUnitX(udg_FS_Soul[Node], x2 + udg_FS_PartialD[Node] * Cos(udg_FS_SoulAngle[Node]))
call SetUnitY(udg_FS_Soul[Node], y2 + udg_FS_PartialD[Node] * Sin(udg_FS_SoulAngle[Node]))
// Checking if the soul has reached the caster.
if totalD - udg_FS_PartialD[Node] < FS_ThirdPhaseReturnRadius() then
// The soul has reached the target, increase the pushback AoE and transition back to phase 1.
set udg_FS_IndicatorAoE[Node] = udg_FS_IndicatorAoE[Node] + udg_FS_SecondPhase[Node] * FS_DurationToProtectionAoe()
set udg_FS_CurrentPhase[Node] = 1
set udg_FS_PartialD[Node] = 0.00
// Checking if the max distance has been exceeded. If yes, break the spell.
if totalD > FS_MaxDistance() and FS_EnableMaxDistance() == true then
set udg_FS_Duration[Node] = 0.00
endif
endif
endif
// Reducing the duration for the pushback special effect.
set udg_FS_SpecialEfDuration[Node] = udg_FS_SpecialEfDuration[Node] - FS_TimerSpeed()
// Enumerating all units that come within AoE of the pushback.
call GroupEnumUnitsInRange(udg_FS_TempGroup, x, y, udg_FS_IndicatorAoE[Node], null)
loop
// Using the FirstOfGroup method
set u = FirstOfGroup(udg_FS_TempGroup)
exitwhen u == null
// Checking if the target is not dead, not a structure and is an enemy.
if FS_TargetFilter(u, udg_FS_Player[Node]) then
// Arguments are passed, setting up the angle and pushing back enemies.
set x2 = GetUnitX(u)
set y2 = GetUnitY(u)
set angle = Atan2(y2 - y, x2 - x)
call SetUnitX(u, x2 + udg_FS_Pushback[Node] * Cos(angle))
call SetUnitY(u, y2 + udg_FS_Pushback[Node] * Sin(angle))
if udg_FS_SpecialEfDuration[Node] < 0.00 then
call DestroyEffect(AddSpecialEffectTarget(FS_PushbackEffect(), u, FS_PushbackEffectLoc()))
endif
endif
call GroupRemoveUnit(udg_FS_TempGroup, u)
endloop
if udg_FS_SpecialEfDuration[Node] < 0.00 then
set udg_FS_SpecialEfDuration[Node] = FS_PushbackEffectInterval()
endif
else
// The spell has ended, clean up the effects.
if udg_FS_Duration[Node] > 0.00 then
call IssueImmediateOrder(udg_FS_Caster[Node], "stop")
endif
call DestroyEffect(udg_FS_SpecialEf[Node])
call UnitApplyTimedLife(udg_FS_Soul[Node], FS_DeathID(), 0.01)
call UnitApplyTimedLife(udg_FS_Indicator[Node], FS_DeathID(), 0.01)
call DestroyEffect(udg_FS_IndicatorEf[Node])
// Recycling the node.
set udg_FS_RecycleNodes[udg_FS_RecyclableNodes] = Node
set udg_FS_RecyclableNodes = udg_FS_RecyclableNodes + 1
set udg_FS_NextNode[udg_FS_PrevNode[Node]] = udg_FS_NextNode[Node]
set udg_FS_PrevNode[udg_FS_NextNode[Node]] = udg_FS_PrevNode[Node]
// Checking if this was the last instance running. If yes, pauses the timer.
if (udg_FS_NextNode[0] == 0) then
call PauseTimer(udg_FS_Timer)
endif
endif
endloop
endfunction
// Function that is run on cast.
function FS_OnCast takes nothing returns boolean
// Setting up the locals
local real x
local real y
local integer Node
local real angle
local unit u
// Checking if the casted ability was this one.
if (GetSpellAbilityId() == FS_AbilityID()) then
// Calling the function that checks if the unit is currently chanelling
// an instance.
set u = GetTriggerUnit()
call FS_CheckChannel(u)
// Setting up the node.
if (udg_FS_RecyclableNodes == 0) then
set udg_FS_NodeNumber = udg_FS_NodeNumber + 1
set Node = udg_FS_NodeNumber
else
set udg_FS_RecyclableNodes = udg_FS_RecyclableNodes - 1
set Node = udg_FS_RecycleNodes[udg_FS_RecyclableNodes]
endif
set udg_FS_NextNode[Node] = 0
set udg_FS_NextNode[udg_FS_PrevNode[0]] = Node
set udg_FS_PrevNode[Node] = udg_FS_PrevNode[0]
set udg_FS_PrevNode[0] = Node
// Initializing all necessary values for the spell.
set udg_FS_Caster[Node] = u
set udg_FS_Player[Node] = GetTriggerPlayer()
set udg_FS_AbilityLevel[Node] = GetUnitAbilityLevel(udg_FS_Caster[Node], FS_AbilityID())
set udg_FS_SpecialEfDuration[Node] = FS_PushbackEffectInterval()
set udg_FS_Duration[Node] = FS_InitialDuration() + FS_DurationPerLevel() * udg_FS_AbilityLevel[Node]
set udg_FS_Damage[Node] = FS_DPSInitial() + FS_DPSPerLevel() * udg_FS_AbilityLevel[Node]
set udg_FS_FirstPhaseSpeed[Node] = FS_FirstPhaseInitialSpeed() + FS_FirstPhaseSpeedPerLevel() * udg_FS_AbilityLevel[Node]
set udg_FS_ThirdPhaseSpeed[Node] = FS_ThirdPhaseInitialSpeed() + FS_ThirdPhaseSpeedPerLevel() * udg_FS_AbilityLevel[Node]
set udg_FS_Pushback[Node] = FS_InitialPushbackPerSecond() + FS_PushbackPerSecondPerLevel() * udg_FS_AbilityLevel[Node]
set udg_FS_CurrentPhase[Node] = 1
set udg_FS_SoulAngle[Node] = 0.00
set udg_FS_PartialD[Node] = 0.00
set udg_FS_EffectDuration[Node] = FS_EffectInterval()
set udg_FS_SecondPhase[Node] = FS_SecondPhaseInitialDuration() + FS_SecondPhaseDurationPerLevel() * udg_FS_AbilityLevel[Node]
set udg_FS_Target[Node] = GetSpellTargetUnit()
set x = GetUnitX(udg_FS_Caster[Node])
set y = GetUnitY(udg_FS_Caster[Node])
set udg_FS_Soul[Node] = CreateUnit(FS_DummyPlayer(), FS_DummyID(), x, y, GetRandomReal(0, 360))
set udg_FS_SpecialEf[Node] = AddSpecialEffectTarget(FS_MissileModel(), udg_FS_Soul[Node], FS_MissileAttachmentPoint())
call SetUnitScale(udg_FS_Soul[Node], FS_MissileSize(), 0.00, 0.00)
call UnitRemoveAbility(udg_FS_Soul[Node], 'Amov')
set udg_FS_IndicatorAoE[Node] = FS_PrimaryProtectionAoe()
// Creating the indicator of the pushback AoE.
set udg_FS_Angle[Node] = GetRandomReal(0, 360) * bj_DEGTORAD
set x = GetUnitX(udg_FS_Caster[Node])
set y = GetUnitY(udg_FS_Caster[Node])
set x = x + udg_FS_IndicatorAoE[Node] * Cos(udg_FS_Angle[Node])
set y = y + udg_FS_IndicatorAoE[Node] * Sin(udg_FS_Angle[Node])
set udg_FS_Indicator[Node] = CreateUnit(FS_DummyPlayer(), FS_DummyID(), x, y, udg_FS_Angle[Node])
set udg_FS_IndicatorEf[Node] = AddSpecialEffectTarget(FS_PushbackIndicatorModel(), udg_FS_Indicator[Node], FS_IndicatorAttachmentPoint())
call SetUnitFlyHeight(udg_FS_Indicator[Node], FS_IndicatorHeight(), 0.00)
// Checking if this instance was the first one. If yes, starts the timer.
if (udg_FS_PrevNode[Node] == 0) then
call TimerStart(udg_FS_Timer, FS_TimerSpeed(), true, function FS_Loop)
endif
set u = null
endif
return false
endfunction
// Initialization trigger
function InitTrig_Forest_Soul takes nothing returns nothing
local trigger FS = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(FS, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(FS, Condition(function FS_OnCast))
set udg_FS_Loc = Location(0, 0)
endfunction
/////////////////////// END OF THE SPELL //////////////////////