Moderator
M
Moderator
16:07, 13th Apr 2014
PurgeandFire: Approved.
Review:
http://www.hiveworkshop.com/forums/2512151-post22.html
PurgeandFire: Approved.
Review:
http://www.hiveworkshop.com/forums/2512151-post22.html
(5 ratings)
Obligatory Requirements | Optional Requirements |
|
Spells included in the demo map |
|
Spell Index
JASS:
|
It's nothing particularly dazzling, decent coding.
In case you are creating you map in vJass, you probably can't avoid using Table, TimerUtils, WorldBounds, UnitIndexer, Event and a system that recycles dummies.whereas on other side using so much vJASS requirements is maybe not very simple coding
private function GetDamage takes integer level returns real
return 45. + (10*level)
endfunction
//Define the periodic damage. level is the ability level for the caster
private function GetDamageOverTime takes integer level returns real
return 20. + (10*level)
endfunction
//Define how often periodic damage is applied
private function GetWaves takes integer level returns integer
return 4 + 0*level
endfunction
Is there any benefit in doing so?You could use constant function for this :
implement optional Alloc
static constant real TIMEOUT = 0.031250000
should be in the globals block, or at least point to a constant global.GetDamageFactor
could probably be inlined, to better support all levels.Only true for Vexorians JassHelper. But I see no reason to use Cohadars in the first place.Just place the UnitAlive line in each spell, JassHelper only adds it once.
Yes I also figured that BoundSentinel is a really useful and lightweight resource.I suggest BoundSentinel over WorldBounds so you don't have to constantly check coordinates. I figure it's more efficient.
call TriggerRegisterEnterRegion(q,WorldBounds.worldRegion,bc2)
and I didn't want to add another library into the map. I also though about excluding the coordinate condition wtih static if BoundSentinel exists, but then I have to add a statif if around an endif and that looks so stupid. ^^It's just an old habit, however that way I never forget to null those who have to be nulled.You probably already know this but your triggers/timers don't need to be nulled.
The spell uses CTL and not TimerUtils. The timeout is not configurable.static constant real TIMEOUT = 0.031250000
I commented the condition that searches enemies to test the ForGroup behaviour and apperently forgot to uncomment it. Fixed, thank you.Does bone spirit actually do anything? I use it, it passes through the enemies, then blows up. Nothing happened. :/
Simple Spell Collection#1 v1.0.0.0 - by BPower
Good spell pack, and nice coding. I only can mention minor things.
Maybe these reals could be constants:
Maybe different scale values depending on ability-level would be cool. (fire ball)
JASS:call SetUnitPosition(c,2147483647,2147483647)
To exit loopsexitwhen(true)
would suffice. (fire ball)
^The real "f" is redundant. (Reaper's Scythe) Or maybe just -->
JASS:if (UnitAlive(.aim)) then call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, .aim, DAMAGE_EFFECT_ATTACH)) set f = GetUnitState(.aim, UNIT_STATE_MAX_LIFE) - GetWidgetLife(.aim) set .dmg = .dmg*f call UnitDamageTarget(.caster, .aim, .dmg, false, false, ATTACK_TYPE, DAMAGE_TYPE, null) endif
JASS:if (UnitAlive(.aim)) then call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, .aim, DAMAGE_EFFECT_ATTACH)) call UnitDamageTarget(.caster, .aim, .dmg*(GetUnitState(.aim, UNIT_STATE_MAX_LIFE) - GetWidgetLife(.aim)), false, false, ATTACK_TYPE, DAMAGE_TYPE, null) endif
set .t = 0 - SLEEP_TIME
. The 0 is not even needed. Also maybe you could mention that SPELL_TIME is not in secods, but in *TIMEOUT seconds. (Bone Spirit)
It looks a bit weird in game if fire ball can pass cliffs. Maybe it should get destroyed if it reaches a higher cliff level.
Execution is totaly fine. The spells can be useful and look cool in game. I rate this 4/5 and vote for approval.
Chaosflare |
|
Ice Siege |
|
Fireball |
|
Reaper's Scythe |
|
|
I like the idea, but the problem is, most user prefer to code simple spell by themselves :?Lately I was browsing through the Spells section with special focus on the old resources. I was wondering how spells were coded back then on 2009, since I have started wc3 modding in summer 2013.
Sadly, most of these spells have the following behaviour: Apply timed life and create a dummy in a certain angle and order a standart Warcraft 3 spell.
So I've decided to rebuild and enhance old spells and gather them in small Spell Collections. Currently each spell only exists in vJass.
EVENT_PLAYER_UNIT_ISSUE_ORDER
, in the previous version it was a periodic timer callback.GroupPickRandomUnitEnum
is accurate enough for your standards. )ubersplat
, which I never saw in any spell before .library Leap initializer Init uses SpellIndex, IsTerrainWalkable, CameraEQNoise/*v1.0
*************************************************************************************
*
* Leap into the air, dealing damage to all close enemies of your destination
* and slowing their movement speed by 20% for 3 seconds.
*
*************************************************************************************/
//**
//* User settings:
//* ==============
//* The slow duration is hardcoded in the leap buff. Change it there.
globals
private constant integer LEAP_ABILITY = 'A003'
//* Damage type options.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
//* Effect options
private constant string ON_LANDING_FX = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
private constant string WHILE_LEAPING_FX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
private constant string FX_ATTACH_POINT = "weapon"
//* Buff variables.
private constant integer BUFF_CAST_ID = 'A00B'//* Raw code of the Leap Apply buff ability - Object Editor (F6)
private constant integer ORDER_ID = 852075//* Order of the Leap Apply buff ability - Trigger Editor (OrderIds) --> slow
endglobals
//* Set the impact damage.
private constant function GetImpactDamage takes integer level returns real
return 0. + 70*level
endfunction
//* Set the arc of the unit. Play a bit with the values to find a good setup.
private function GetArc takes real distance returns real
return 45.*bj_DEGTORAD + RMinBJ(20., distance*.1)*bj_DEGTORAD
endfunction
//* Set the damage collision radius on leap finish.
private function GetDamageRadius takes integer level returns real
return 190. + 0.*level
endfunction
//* Set the total air time. Returning 0. is invalid!
//* The current setup is --> minimum + (distance/2)/movement speed.
private function GetAirTime takes real distance, integer level returns real
return .3 + (distance*.5)/500.
endfunction
//* Set the offset for the damage effects. It's an empirical value.
private constant function GetUnitWeaponOffset takes unit source returns real
return 75.
endfunction
//* Read the animation time out from the object editor field for your unit.
//* The current setup is --> slam animation time blademaster 1.133
private constant function GetUnitAnimationTime takes unit source returns real
return 1.133
endfunction
//* Filter valid targets.
private function FilterUnits takes unit target, player owner returns boolean
return UnitAlive(target) and IsUnitEnemy(target, owner) and not IsUnitType(target, UNIT_TYPE_FLYING)
endfunction
//* Code which should run on finish leap.
private function OnFinishLeap takes unit source returns nothing
if UnitAlive(source) then
call CameraSetEQNoise(GetOwningPlayer(source), 3., .33)
endif
endfunction
//========================================================================
//* Leap code. Make changes carefully.
//========================================================================
//* Quite useful, you may outsource it and make it a public function.
private function IsPointJumpable takes real x, real y returns boolean
if not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
return IsTerrainWalkable(x, y)
endif
return false
endfunction
globals
private sound error
endglobals
//* Uses Missile's API. For a destructable enum, add .onDestructable and .onDestructableFilter.
private struct Leap extends array
//* Runs on finish.
static method onFinish takes Missile missile returns boolean
local unit source = missile.dummy
local real unitX = GetUnitX(source)
local real unitY = GetUnitY(source)
local real posX = unitX + GetUnitWeaponOffset(source)*Cos(missile.angle)
local real posY = unitY + GetUnitWeaponOffset(source)*Sin(missile.angle)
local unit u
//* Restore pathing.
call SetUnitPathing(source, true)
if UnitAlive(source) then
//* Run effects.
call DestroyEffect(AddSpecialEffect(ON_LANDING_FX, posX, posY))
call GroupEnumUnitsInRange(SpellIndex.GLOBAL_GROUP, posX, posY, missile.collision, null)
loop
set u = FirstOfGroup(SpellIndex.GLOBAL_GROUP)
exitwhen u == null
call GroupRemoveUnit(SpellIndex.GLOBAL_GROUP, u)
if (FilterUnits(u, missile.owner)) then
//* Make pathing space for the leaping unit.
call SetUnitPosition(u, GetUnitX(u), GetUnitY(u))
//* Deal damage and apply the buff.
if (UnitDamageTarget(source, u, missile.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)) then
call DummyCaster[BUFF_CAST_ID].castTarget(missile.owner, 1, ORDER_ID, u)
endif
endif
endloop
endif
call SetUnitPosition(source, unitX, unitY)
call OnFinishLeap(source)
call SetUnitTimeScale(source, 1.)
call SpellIndex(missile.data).destroy()
set source = null
return true
endmethod
implement MissileStruct
endstruct
private function OnEffect takes nothing returns nothing
local string prefix = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00"
local unit source = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(source, LEAP_ABILITY)
local real time
local real distance
local Missile missile
//* Check for terrain pathability.
if IsPointJumpable(GetSpellTargetX(), GetSpellTargetY()) then
//* Make sure the caster can fly.
if UnitAddAbility(source, 'Amrf') and UnitRemoveAbility(source, 'Amrf') then
endif
//* Transform the source unit into a Missile instance.
set missile = Missile.createEx(source, GetSpellTargetX(), GetSpellTargetY(), GetUnitDefaultFlyHeight(source))
set distance = missile.origin.distance
set time = GetAirTime(distance, level)
set missile.arc = GetArc(distance)
set missile.speed = distance/time*Missile_TIMER_TIMEOUT
set missile.owner = GetTriggerPlayer()
set missile.damage = GetImpactDamage(level)
set missile.collision = GetDamageRadius(level)
call Leap.launch(missile)
//* Add effect.
set missile.data = SpellIndex.create()
set SpellIndex(missile.data).fx = AddSpecialEffectTarget(WHILE_LEAPING_FX, source, FX_ATTACH_POINT)
//*
call SetUnitTimeScale(source, GetUnitAnimationTime(source)*.5/time)
call SetUnitPathing(source, false)
else
//* Invalid jump location!
call PauseUnit(source, true)
call IssueImmediateOrderById(source, 851972)
call PauseUnit(source, false)
//* Simulate Warcraft III error message.
if GetLocalPlayer() == GetTriggerPlayer() then
call StartSound(error)
call ClearTextMessages()
endif
call DisplayTimedTextToPlayer(GetTriggerPlayer(), .52, .96, 2., prefix + GetUnitName(source) + " can't jump there!|r")
endif
set source = null
endfunction
private function Init takes nothing returns nothing
set error = CreateSoundFromLabel("InterfaceError", false, false, false, 10, 10)
call RegisterSpellEffectEvent(LEAP_ABILITY, function OnEffect)
endfunction
endlibrary
library FistOfThunder initializer Init uses SpellIndex /* v1.0
*************************************************************************************
*
* Slams the ground in front of you, dealing damage to close enemies.
* Emerging lightnings deal extra damage, when colliding with an enemy.
*
*************************************************************************************/
//**
//* User settings:
//* ==============
globals
private constant integer FIST_OF_THUNDER_ABILITY = 'A00C'
//* Damage options.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
//* Set the lightning end z.
private constant real MISSILE_END_Z = 175.
//* Effect options.
private constant string LIGHTNING_MODEL = "CLPB"
private constant string SLAM_CENTER_FX = "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl"
private constant real SLAM_CENTER_FX_DURATION = 2.
//* Concept.
private constant boolean DESTROY_MISSILE_ON_COLLIDE = true
endglobals
//* Set the slam collision size.
private constant function GetSlamCollisionSize takes integer level returns real
return 180.
endfunction
//* Set the damage dealt on slam.
private constant function GetSlamDamage takes integer level returns real
return 40.*level - 10.
endfunction
//* Set the travel distance for each missile.
private constant function GetMissileFlyDistance takes integer level returns real
return 600. + (50*level)
endfunction
//* Set the amount of Missiles based on the ability level.
private constant function GetMissileCount takes integer level returns integer
return 4 + 2*level
endfunction
//* Filter valid target units.
private function FilterUnits takes unit target, player owner returns boolean
return UnitAlive(target) and IsUnitEnemy(target, owner) and not IsUnitType(target, UNIT_TYPE_STRUCTURE)
endfunction
//* Customize all missile members to your needs.
private function CustomizeMissile takes Missile missile, integer level returns nothing
set missile.speed = 22. + 0.*level
set missile.damage = 10. + 10.*level
set missile.collision = 32.
set missile.model = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
set missile.scale = 1.
endfunction
//* These effects run on spell effect event. Customize them to your needs.
private function RunOnSlamEffects takes unit source, real slamX, real slamY returns nothing
//* Runs sounds.
local string file = "Abilities\\Spells\\Orc\\LightningBolt\\LightningBolt.wav"
local sound s
if (GetRandomInt(0, 1) == 0) then
set file = "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.wav"
endif
set s = CreateSound(file, false, false, false, 10, 10, "")
call AttachSoundToUnit(s, source)
call StartSound(s)
call KillSoundWhenDone(s)
//* Run special effects.
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl", slamX, slamY))
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl", source, "weapon,right"))
//*
set s = null
endfunction
//* These effects run when a missile ( source ) collides with a unit ( hit )
private function RunOnCollideEffects takes unit source, unit hit returns nothing
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl", hit, "origin"))
endfunction
//========================================================================
//* Fist of Thunder code. Make changes carefully.
//========================================================================
//* Uses Missile's API. For a destructable enum, add .onDestructable and .onDestructableFilter.
private struct FistOfThunder extends array
//* Releases the lightning model.
private static method onRemove takes Missile missile returns boolean
call SpellIndex(missile.data).destroy()
return true
endmethod
static method onCollide takes Missile missile, unit hit returns boolean
if FilterUnits(hit, missile.owner) then
call UnitDamageTarget(missile.source, hit, missile.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
call RunOnCollideEffects(missile.dummy, hit)
return DESTROY_MISSILE_ON_COLLIDE
endif
return false
endmethod
static method onPeriod takes Missile missile returns boolean
local MissilePosition pos = missile.origin
call MoveLightningEx(SpellIndex(missile.data).flash, true, pos.x, pos.y, pos.z, missile.x, missile.y, missile.z + missile.terrainZ)
return false
endmethod
implement MissileStruct
endstruct
private function Callback takes nothing returns nothing
static if LIBRARY_TimerUtilsEx then
call SpellIndex(ReleaseTimer(GetExpiredTimer())).destroy()
else
call SpellIndex(GetTimerData(GetExpiredTimer())).destroy()
call ReleaseTimer(GetExpiredTimer())
endif
endfunction
private function CreateMissiles takes unit source, player user, real x, real y, integer level returns nothing
local integer dex = 0
local integer end = GetMissileCount(level)
local real spacing = bj_PI*2/end
local real angle = GetRandomReal(-bj_PI, bj_PI)
local real distance = GetMissileFlyDistance(level)
local Missile missile
loop
exitwhen (dex == end)
//* Create a new missile and assign it's members.
set missile = Missile.create(x, y, 0., angle, distance, MISSILE_END_Z)
call CustomizeMissile(missile, level)
set missile.source = source
set missile.owner = user
set missile.data = SpellIndex.create()
set SpellIndex(missile.data).flash = AddLightningEx(LIGHTNING_MODEL, true, x, y, missile.origin.z, x, y, missile.z + missile.terrainZ)
call FistOfThunder.launch(missile)
//* Next missile.
set angle = angle + spacing
set dex = dex + 1
endloop
endfunction
private function OnEffect takes nothing returns nothing
local unit source = GetTriggerUnit()
local player user = GetTriggerPlayer()
local integer level = GetUnitAbilityLevel(source, FIST_OF_THUNDER_ABILITY)
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
local real damage = GetSlamDamage(level)
local SpellIndex dex = SpellIndex.create()
local unit u
//* Runs Effects.
call RunOnSlamEffects(source, x, y)
call GroupEnumUnitsInRange(SpellIndex.GLOBAL_GROUP, x, y, GetSlamCollisionSize(level), null)
loop
set u = FirstOfGroup(SpellIndex.GLOBAL_GROUP)
exitwhen u == null
call GroupRemoveUnit(SpellIndex.GLOBAL_GROUP, u)
if (FilterUnits(u, user)) then
call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
endloop
//* Create all missiles.
call CreateMissiles(source, user, x, y, level)
//* Create center model
set dex.fx = AddSpecialEffect(SLAM_CENTER_FX, x, y)
call TimerStart(NewTimerEx(dex), SLAM_CENTER_FX_DURATION, false, function Callback)
set user = null
set source = null
endfunction
private function Init takes nothing returns nothing
call RegisterSpellEffectEvent(FIST_OF_THUNDER_ABILITY, function OnEffect)
endfunction
endlibrary
library GuidedArrow initializer Init uses SpellIndex, Missile /* v2.0
*************************************************************************************
*
* The caster fires an arrow which tracks down it's target.
* On impact the arrow eventually pierces and hits the target again.
*
*************************************************************************************/
//**
//* User settings:
//* ==============
globals
private constant integer GUIDED_ARROW_ABILITY = 'A00E'
// Damage options.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// Effect options.
private constant string ON_TARGET_FX = "Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl"
private constant string FX_ATTACH_POINT = "overhead"
// Constant missile options.
private constant real ARROW_ACTION_DELAY = 0.3 // Timeout before the arrow starts to turn.
private constant real UNIT_DETECTION_AOE = 400.// Area in which the arrow searches for targets.
private constant real ARROW_FLY_RUN_OUT_DISTANCE = 350.// Distance traveled if the target dies.
private constant real ARROW_FLY_HEIGHT = 65.
private constant real ARROW_TURN_RATE = 8.*bj_DEGTORAD
private constant real ALLOW_HIT_AFTER = 1. // Minimum amount of seconds until the missile can damage again.
endglobals
// Filter valid target units.
private function FilterUnits takes unit target, player owner returns boolean
return UnitAlive(target) and IsUnitEnemy(target, owner)
endfunction
// Set the maximum travel distance. Does no longer count, when a traget is found.
private constant function GetFlyDistance takes integer level returns real
return 800. + (200*level)
endfunction
// Set the number of pierce events.
private function GetPiercingCount takes integer level returns integer
return GetRandomInt(0, 3) + 1*level
endfunction
//* Customize all missile members to your needs.
private function CustomizeMissile takes Missile missile, integer level returns nothing
set missile.speed = 20. + 0.*level
set missile.damage = 0. + 30.*level
set missile.collision = 32.
set missile.model = "Abilities\\Spells\\Other\\BlackArrow\\BlackArrowMissile.mdl"
set missile.scale = 1.
endfunction
//========================================================================
// Guided arrow code. Make changes carefully.
//========================================================================
globals
// "tempOwner" is always set to the owner of the arrow before "ConsiderUnitFiltered" is called.
private player tempOwner = null
endglobals
// Filters valid targets for the arrow.
private function ConsiderUnitsFiltered takes nothing returns boolean
return FilterUnits(GetFilterUnit(), tempOwner)
endfunction
private struct GuidedArrow extends array
static boolexpr filter
// Runs when the missile is deallocated.
private static method onRemove takes Missile missile returns boolean
call SpellIndex(missile.data).destroy()
return true
endmethod
// Runs when the maximum range is reached.
private static method onFinish takes Missile missile returns boolean
local SpellIndex dex = missile.data
return (dex.phase == 0) or (GetUnitTypeId(dex.target) == 0)
endmethod
// Runs when a missile collides with a unit.
private static method onCollide takes Missile missile, unit hit returns boolean
local SpellIndex dex = missile.data
// Only the target is valid.
if (hit == missile.target) then
// Allows the missile to hit the target again after 1 second.
call missile.enableHitAfter(hit, ALLOW_HIT_AFTER)
call UnitDamageTarget(missile.source, hit, missile.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
set missile.target = null
set missile.turn = 0
// Reduce the total amount of pierces.
set dex.count = dex.count - 1
endif
return (dex.count < 0)
endmethod
private static method searchTarget takes Missile missile returns nothing
local SpellIndex dex = missile.data
//
set tempOwner = missile.owner
call GroupEnumUnitsInRange(SpellIndex.GLOBAL_GROUP, missile.x, missile.y, UNIT_DETECTION_AOE , thistype.filter)
if (FirstOfGroup(SpellIndex.GLOBAL_GROUP) != null) then
set bj_groupRandomConsidered = 0
set bj_groupRandomCurrentPick = null
call ForGroup(SpellIndex.GLOBAL_GROUP, function GroupPickRandomUnitEnum)
call GroupClear(SpellIndex.GLOBAL_GROUP)
//
set missile.target = bj_groupRandomCurrentPick
set dex.target = bj_groupRandomCurrentPick
set dex.time = ARROW_ACTION_DELAY
set missile.turn = ARROW_TURN_RATE
// Just in case this unit was already hit, while missile.target was null.
// Remember: onCollide runs before onPeriod.
call missile.removeHitWidget(bj_groupRandomCurrentPick)
//
set bj_groupRandomCurrentPick = null
if (dex.fx != null) then
call DestroyEffect(dex.fx)
set dex.fx = null
endif
set dex.fx = AddSpecialEffectTarget(ON_TARGET_FX, dex.target, FX_ATTACH_POINT)
endif
endmethod
// Runs every timer interval. Let's break the mechanics down.
private static method onPeriod takes Missile missile returns boolean
local SpellIndex dex = missile.data
// In this phase no target was found yet. And no pierce event did take place.
if (dex.target == null) and (dex.count >= 0) then
// Check the action delay.
if (dex.time > 0.) then
set dex.time = dex.time - Missile_TIMER_TIMEOUT
return false
endif
call searchTarget(missile)
//
// In this phase we have a target stored on dex.target, but the missile doesn't know that.
elseif (missile.target == null) and (UnitAlive(dex.target)) then
// Check the action delay.
if (dex.time > 0.) then
set dex.time = dex.time - Missile_TIMER_TIMEOUT
return false
endif
set dex.time = ARROW_ACTION_DELAY
set missile.turn = ARROW_TURN_RATE
set missile.target = dex.target
//
// In this phase the target died. Let the missile run out.
elseif not UnitAlive(dex.target) then
set dex.count = -1
set dex.target = null
set missile.turn = 0.
set missile.target = null
set missile.collision = 0.
if (dex.phase == 1) then
set dex.phase = 0
call missile.origin.move(missile.x, missile.y, missile.z)
call missile.impact.move(missile.x + Cos(missile.angle)*ARROW_FLY_RUN_OUT_DISTANCE, missile.y + Sin(missile.angle)*ARROW_FLY_RUN_OUT_DISTANCE, missile.z)
endif
endif
return false
endmethod
implement MissileStruct
endstruct
private function OnEffect takes nothing returns nothing
local unit source = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(source, GUIDED_ARROW_ABILITY)
local real x = GetUnitX(source)
local real y = GetUnitY(source)
local real angle = Atan2(GetSpellTargetY() - y, GetSpellTargetX() - x)
local SpellIndex dex = SpellIndex.create()
// Create a new missile and assign its members.
local Missile missile = Missile.create(x, y, ARROW_FLY_HEIGHT, angle, GetFlyDistance(level), ARROW_FLY_HEIGHT)
// Allows configuration.
call CustomizeMissile(missile, level)
// Override user settings. These missile members are reserved.
set missile.source = source
set missile.owner = GetTriggerPlayer()
set missile.data = dex
set missile.target = null
call GuidedArrow.launch(missile)
// Assign data to the spell index.
set dex.count = GetPiercingCount(level)
set dex.time = ARROW_ACTION_DELAY
set dex.target = GetSpellTargetUnit()
if not FilterUnits(dex.target, missile.owner) then
set dex.target = null
endif
set dex.phase = 1
// Run effects.
if (dex.target != null) then
set dex.fx = AddSpecialEffectTarget(ON_TARGET_FX, dex.target, FX_ATTACH_POINT)
endif
set source = null
endfunction
private function Init takes nothing returns nothing
set GuidedArrow.filter = Filter(function ConsiderUnitsFiltered)
call RegisterSpellEffectEvent(GUIDED_ARROW_ABILITY, function OnEffect)
endfunction
endlibrary