/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* F R O S T A R R O W S V 1.01
*
* Written by DualShock (IBornForThis100)
* [17.02.2012]
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Spell Description:
* Fires a frost arrow into the sky which explodes and scatters
* at the center of the point target, showering the target area.
* Deals amount of damage each.
*
* CREDITS:
* -------
*
* Vexorian XE libraries:
* [URL]http://www.wc3c.net/showthread.php?t=101150[URL]
*
* - xebasic
* - xemissile
* - xecast
* - xefx
* - xedamage
*
* RegisterPlayerUnitEvent by Magtheridon96
* [URL]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/[URL]
* TimerUtils by Vexorian
* [URL]http://www.wc3c.net/showthread.php?t=101322[URL]
* BoundSentinel by Vexorian
* [URL]http://www.wc3c.net/showthread.php?t=102576[URL]
*
* HOW TO IMPORT:
* -------------
*
* - copy spells (Object Editor)
* - copy dummy unit (Object Editor)
* - copy dummy.mdx model
* - copy libraries and spell trigger
* - edit raw-codes in spell settings, xebasic
*
* UPDATE HISTORY:
* --------------
* ~ v1.00 First Release.
* ~ v1.01 optimized code, added comments and import documentation, removed xepreload
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
library FrostArrows uses RegisterPlayerUnitEvent, TimerUtils, SimError, xemissile, xecast, xedamage optional BoundSentinel
//*================================================================================
//* C O N F I G U R A T I O N M E N U
//*================================================================================
globals
private constant integer SPELL_ID = 'A000' //spell raw code
private constant integer FROST_NOVA_ID = 'A001' //dummy spell id
private constant string DUMMY_ORDER = "frostnova" //dummy order
private constant boolean PRELOAD = true // preload dummy ability?
private constant string ARROW_MODEL = "Abilities\\Spells\\Other\\FrostArrows\\NagaColdArrowMissile.mdl" //missile fx path
private constant string EFFECT = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl" //explode sfx
private constant real ARROW_SCALE = 1. //arrow scale
private constant real MAX_HEIGHT = 500. // explode height
private constant real SPEED = 800.
private constant real ARC = 0.15
private constant real MIN_DIST = 300. // min cast range
private constant string ERROR_MSG = "Inside Minimum Range."
endglobals
private function GetAoE takes integer lvl returns real
return 400. // spell aoe, must match the object editor field
endfunction
private function GetDamage takes integer lvl returns real
return 25. * lvl // deals damage each arrow
endfunction
private function GetArrowCount takes integer lvl returns integer
return 12 //how many arrows
endfunction
private function GetArrowAoE takes integer lvl returns real
return 125. // collision size each arrow
endfunction
//xedamage options:
private function DamageOptions takes xedamage spellDamage returns nothing
set spellDamage.dtype = DAMAGE_TYPE_MAGIC
set spellDamage.atype = ATTACK_TYPE_NORMAL
set spellDamage.exception = UNIT_TYPE_STRUCTURE //don't damage building
set spellDamage.visibleOnly = false //only damage visible unit
set spellDamage.damageAllies = false //damage allies if true
set spellDamage.damageTrees = true //damage destructables?
endfunction
//*================================================================================
//* E N D O F C O N F I G U R A T I O N M E N U
//*================================================================================
globals
xedamage xed
xecast xec
endglobals
struct Arrow extends xemissile
unit caster
real tx
real ty
integer lvl
private method onHit takes nothing returns nothing
call xed.damageAOE(.caster,.tx, .ty, GetArrowAoE(.lvl), GetDamage(.lvl))
set xec.owningplayer = GetOwningPlayer(.caster)
call xec.castOnAOE(.tx,.ty, GetArrowAoE(.lvl))
endmethod
static method start takes unit c, real x, real y, real tx, real ty, integer lvl returns nothing
local thistype this = thistype.allocate(x,y,MAX_HEIGHT,tx,ty,45.)
local real dist = SquareRoot((x - tx) * (x - tx) + (y - ty) * (y - ty))
local real speed = dist * (SPEED / GetAoE(.lvl))
set .fxpath = ARROW_MODEL
set .scale = ARROW_SCALE
set .lvl = lvl
set .caster = c
set .tx = tx
set .ty = ty
call .launch(speed,ARC)
endmethod
endstruct
private struct MainArrow extends xemissile
unit caster
integer lvl
real sx
real sy
real tx
real ty
xefx sfx
private method onHit takes nothing returns nothing
local integer i
local real angle
local real dis
local real tx
local real ty
//sfx:
set .sfx = xefx.create(.sx, .sy, 0.)
set .sfx.z = MAX_HEIGHT
set .sfx.fxpath = EFFECT
set .sfx.alpha = 0
call .sfx.destroy()
set i = GetArrowCount(.lvl)
loop
exitwhen i == 0
set angle = GetRandomReal(0., 2. * bj_PI)
set dis = GetRandomReal(1., GetAoE(.lvl))
set tx = .tx + dis * Cos (angle)
set ty = .ty + dis * Sin (angle)
call Arrow.start(.caster, .sx, .sy, tx, ty, .lvl)
set i = i - 1
endloop
endmethod
private static method start takes unit u, real x, real y, integer lvl returns nothing
local real unitX = GetUnitX(u)
local real unitY = GetUnitY(u)
local real dx = x - unitX
local real dy = y - unitY
local real a = Atan2(dy, dx)
local real dist = SquareRoot(dx*dx + dy*dy)/2
local real tx = unitX + dist * Cos(a)
local real ty = unitY + dist * Sin(a)
local thistype this = thistype.allocate(unitX, unitY, 45, tx, ty, MAX_HEIGHT)
set this.fxpath = ARROW_MODEL
set this.scale = ARROW_SCALE
set this.lvl = lvl
set this.caster = u
set this.sx = tx
set this.sy = ty
set this.tx = x
set this.ty = y
call this.launch(SPEED, ARC)
endmethod
private static method SpellEffect takes nothing returns nothing
if GetSpellAbilityId() == SPELL_ID then
call thistype.start(GetTriggerUnit(),GetSpellTargetX(), GetSpellTargetY(), GetUnitAbilityLevel(GetTriggerUnit(),SPELL_ID))
endif
endmethod
private static method Check takes nothing returns nothing
local unit u = GetTriggerUnit()
local real dx = GetUnitX(u) - GetSpellTargetX()
local real dy = GetUnitY(u) - GetSpellTargetY()
if GetSpellAbilityId() == SPELL_ID and dx * dx + dy * dy < MIN_DIST * MIN_DIST then
call SimError(GetOwningPlayer(u), ERROR_MSG)
call PauseUnit(u, true)
call IssueImmediateOrder(u, "stop")
call PauseUnit(u, false)
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent( EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.SpellEffect )
call RegisterPlayerUnitEvent( EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.Check )
//xecast options:
set xec = xecast.create()
set xec.abilityid = FROST_NOVA_ID
set xec.orderstring = DUMMY_ORDER
//xe damage init:
set xed = xedamage.create()
call DamageOptions(xed)
//preload sfx and dummy ability:
static if PRELOAD then //preload enabled?
set bj_lastCreatedUnit = CreateUnit(Player(15), XE_DUMMY_UNITID, 0, 0, 0)
call UnitAddAbility(bj_lastCreatedUnit, FROST_NOVA_ID)
call RemoveUnit(bj_lastCreatedUnit)
call Preload(ARROW_MODEL)
call Preload(EFFECT)
endif
endmethod
endstruct
endlibrary