scope DarkWave initializer init
native UnitAlive takes unit uid returns boolean
/*
[vJASS] Dark Wave v1.0.2
by jondrean
Date: 9.11.2014
Updated: 10.11.2014
The caster shoots a dark wave that
pushes everything out of the way
and drains their magic. Their
movement speed is decreased by
10% for 30 seconds
Credits: Moyackx, Vexorian and Malhorne
This spell requires TimerUtils, MalhorneAlloc
and TerrainPathability
*/
globals
private constant integer A_SPELL = 'A002' // id of your spell
//private constant integer A_SLOW = 'A000' // if of the slow
private constant integer U_DUMMY = 'h000' // id of your dummy
private constant integer A_FLY = 'Arav' // id of crow ability
private constant real DUMMY_HEIGHT = 50. // height of the wave dummy
private constant player P_EFFECT = Player(PLAYER_NEUTRAL_PASSIVE) // owner of dummy
private constant string EF_WAVE = "Abilities\\Spells\\Undead\\CarrionSwarm\\CarrionSwarmMissile.mdl" // look of the dark wave itself
private constant string EF_HIT = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl" // look of hit event
private constant string EF_HIT_ATTACH= "chest" // attach point of EF_HIT
private constant real ACC_DURATION = 1.166 // ACCURATE duration of the spell
private constant real INTERVAL = 0.03125000 // FPS, currently set to 32
private constant real KNOCK_FORCE = 28.126 // staring speed of kb
private constant real KNOCK_LOSS = 2 // loss of kb, per interval
private constant real TT_Z_OFFSET = 50 // z offset from ground to floating text
private constant string TT_C = "|c003366ff" // color of floating text
private constant real TT_HEIGHT = 0.023 // height of the floating text
private constant real TT_LIFESPAN) = 2.5 // complete lifespan of the floating text
private constant real TT_FADEPOINT = 1.5 // the point where floating text starts to fade
private constant real TT_X_VEL = 0 // the x speed of the floating text
private constant real TT_Y_VEL = 0.1 // the y speed of the floating text
endglobals
// target filter
// caster - caster of spell
// owner - owner of caster
// U - target
//! textmacro Filter
return IsUnitEnemy(U, owner) and UnitAlive(U) and not IsUnitType(U, UNIT_TYPE_MAGIC_IMMUNE) and GetUnitAbilityLevel(U,'Bvul')==0
//! endtextmacro
// getters
private constant function GetMissileSpeed takes integer level returns real
return 28.125 // if FPS is 32, this is 900 speed
endfunction
private constant function GetDamage takes integer level returns real
return 25.+50*(level) // damage per target, 75,125,175
endfunction
private constant function GetAoE takes integer level returns real
return 100.
endfunction
// endgetters
// useful
private function Atan3 takes real x1, real y1, real x2, real y2 returns real
local real a = Atan2(y2 - y1, x2 - x1)
if a < 0 then
return 2 * bj_PI + a
endif
return a
endfunction
// enduseful
private struct Push extends array
unit me
real xOff
real yOff
real x
real y
real f
readonly static group g = CreateGroup()
//! runtextmacro MA()
private static method onExpire takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen this == 0
if UnitAlive(me) then
set x = x+xOff*f
set y = y+yOff*f
if IsTerrainWalkable(x,y) then
call SetUnitX(me, x)
call SetUnitY(me, y)
set f = f - KNOCK_LOSS
if f <= 0 then
call GroupRemoveUnit(g, me)
set me = null
call deallocate()
endif
else
call GroupRemoveUnit(g, me)
set me = null
call deallocate()
endif
else
call GroupRemoveUnit(g, me)
set me = null
call deallocate()
endif
set this = this.next
endloop
endmethod
//! runtextmacro MA_alloc("onExpire","INTERVAL")
static method create takes real x1, real y1, unit knocked returns thistype
local thistype this = thistype.allocate()
local real a
set me = knocked
set x = GetUnitX(me)
set y = GetUnitY(me)
set a = Atan3(x1,y1,x,y)
set xOff = Cos(a)
set yOff = Sin(a)
set f = KNOCK_FORCE
call GroupAddUnit(g, me)
return this
endmethod
endstruct
private struct Struct extends array
real dmg
unit caster
player owner
real xOff
real range
real yOff
real dmgDealt
real x
real y
timer lifespan
unit dummy
effect ef
//! runtextmacro MA()
private method destroy takes nothing returns nothing
local texttag tt = CreateTextTag()
call SetTextTagPos(tt, GetUnitX(caster), GetUnitY(caster), TT_Z_OFFSET)
call SetTextTagText(tt, TT_C+"+"+I2S(R2I(dmgDealt)), TT_HEIGHT)
//call SetTextTagAge(tt, TT_AGE)
call SetTextTagLifespan(tt, TT_LIFESPAN)
call SetTextTagPermanent(tt, false)
call SetTextTagFadepoint(tt, TT_FADEPOINT)
call SetTextTagVisibility(tt, owner==GetLocalPlayer())
call SetTextTagVelocity(tt, TT_X_VEL, TT_Y_VEL)
set tt = null
set caster = null
call DestroyEffect(ef)
set ef = null
call RemoveUnit(dummy)
set dummy = null
set owner = null
call ReleaseTimer(lifespan)
set lifespan = null
call deallocate()
endmethod
static method endOfLifespan takes nothing returns nothing
call thistype(GetTimerData(GetExpiredTimer())).destroy()
endmethod
private static group g = CreateGroup()
private static unit U
private static thistype S
private static method filter takes nothing returns boolean
local thistype this = S
set U = GetFilterUnit()
//! runtextmacro Filter()
endmethod
private static method onExpire takes nothing returns nothing
local thistype this = thistype(0).next
local unit FoG
local real r = 0
local real r2
//local unit slower
loop
exitwhen this == 0
if UnitAlive(caster) then
set x = x + xOff
set y = y + yOff
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
set S = this
call GroupEnumUnitsInRange(g, x, y, range, Filter(function thistype.filter))
loop
set FoG = FirstOfGroup(g)
exitwhen FoG == null
if not IsUnitInGroup(FoG, Push.g) then
call Push.create(x,y,FoG)
set r2 = GetUnitState(FoG, UNIT_STATE_MANA) - dmg
if r2 < 0 then
call SetUnitState(FoG, UNIT_STATE_MANA, 0)
set r = r + dmg + r2
else
call SetUnitState(FoG, UNIT_STATE_MANA, r2)
set r = r + dmg
endif
//set slower = CreateUnit(owner, U_DUMMY, x, y, 9)
//call IssueTargetOrder(slower, "slow", FoG)
//call UnitApplyTimedLife(slower, 'BTLF', 3)
call DestroyEffect(AddSpecialEffectTarget(EF_HIT,FoG,EF_HIT_ATTACH))
call GroupRemoveUnit(g, FoG)
endif
endloop
call SetUnitState(caster, UNIT_STATE_MANA, RMinBJ(GetUnitState(caster, UNIT_STATE_MANA)+r,GetUnitState(caster,UNIT_STATE_MAX_MANA)))
set dmgDealt = dmgDealt + r
//set slower = null
else
call destroy()
endif
set this = this.next
endloop
endmethod
//! runtextmacro MA_alloc("onExpire","INTERVAL")
endstruct
private function OnCast takes nothing returns boolean
local Struct s
local integer level
local real spd
local real a
if GetSpellAbilityId() == A_SPELL then
set s = Struct.allocate()
set s.caster = GetTriggerUnit()
set level = GetUnitAbilityLevel(s.caster, A_SPELL)
set s.dmg = GetDamage(level)
set spd = GetMissileSpeed(level)
set s.x = GetUnitX(s.caster)
set s.y = GetUnitY(s.caster)
set a = Atan3(s.x,s.y,GetSpellTargetX(),GetSpellTargetY())
set s.xOff = spd * Cos(a)
set s.yOff = spd * Sin(a)
set s.dmgDealt = 0
set s.owner = GetOwningPlayer(s.caster)
set s.dummy = CreateUnit(P_EFFECT, U_DUMMY, s.x, s.y, a*bj_RADTODEG)
if UnitAddAbility(s.dummy, A_FLY) then
call UnitRemoveAbility(s.dummy, A_FLY)
endif
call SetUnitFlyHeight(s.dummy, DUMMY_HEIGHT, 0)
set s.range = GetAoE(level)
set s.ef = AddSpecialEffectTarget(EF_WAVE, s.dummy, "origin")
set s.lifespan = NewTimer()
call TimerStart(s.lifespan, ACC_DURATION, false, function Struct.endOfLifespan)
call SetTimerData(s.lifespan, s)
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function OnCast))
set t = null
endfunction
endscope