library Windblast requires Knockback3D
globals
public integer SPELL_ID = 'Wbst'
public integer DUMMY_ID = 'dmmy'
private real TIMER_TICK = 0.03
private real MAX_COLLISION = 128.
private real VELOCITY = 900.
private real DISTANCE = 600.
private real KB_VEL = 500.
private real KB_ALPHA = 0. //elevation angle to knock back at
private real EFFECT_H = 10.
private string EFFECT_1A = "Doodads\\Cinematic\\GlowingRunes\\GlowingRunes3.mdl"
private real E1A_SCALE = 2.2
private real E1A_TSCALE = .1
private real E1A_TSKIP = 1.0 //skip this far ahead in the stand animation
private integer E1A_COL_R = 255
private integer E1A_COL_G = 255
private integer E1A_COL_B = 255
endglobals
native UnitAlive takes unit u returns boolean
globals
public timer SpellTimer = CreateTimer()
public trigger CastTrigger = CreateTrigger()
public keyword SpellData
private SpellData array GSD
private integer COUNT = 0
private integer CURR = 0
endglobals
private module spellBehavior
// these are defined in the struct below for you to use:
// they do not need to be destroyed or removed, that is handled already
//
// static group SEARCH_GROUP <-- a group you can use to search for targets via GroupEnumUnitsIn...
// unit DUMMY <-- can cast instantly and is moved to (.cx, .cy) every timer step
// add abilities to it with .prepDummy(abilCode) (level is set automatically)
// group HIT_GROUP <-- units already hit by this instance of the spell, you are free to clear this as necessary, but you must add units manually
// integer l <-- level of the spell when it was cast
// unit c <-- caster
// player p <-- owner of caster
// real cx, cy, cz <-- coordinates of the center of the spell
// you may end an instance early by calling .endInstance()
// you may add your own instance or static members here:
static integer SECONDARY_ID = 'Wbls'
static string SECONDARY_ORDER = "slow"
static real RADIUS = 100.
method dmgIntFactor takes nothing returns real
return this.l * 1.0
endmethod
static method onInitEx takes nothing returns nothing
//called on map init, so you can set up anything you need to here (most likely arrays or some global dummies)
endmethod
method onStart takes nothing returns nothing
//called when the spell is cast
call this.prepDummy(SECONDARY_ID)
endmethod
method onPeriodic takes nothing returns nothing
local unit u
local real x
local real y
local real a
local real dmg = this.dmgIntFactor()*GetHeroInt(this.c, true)
call GroupEnumUnitsInRange(SEARCH_GROUP, this.cx, this.cy, RADIUS+MAX_COLLISION, null)
loop
set u = FirstOfGroup(SEARCH_GROUP)
exitwhen u == null
call GroupRemoveUnit(SEARCH_GROUP, u)
if IsUnitInRangeXY(u, this.cx, this.cy, RADIUS) and /*
*/ IsUnitEnemy(u, this.p) and UnitAlive(u) and /*
*/ not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ not BlzIsUnitInvulnerable(u) and /*
*/ not IsUnitInGroup(u, this.HIT_GROUP) then
set x = GetUnitX(u)
set y = GetUnitY(u)
set a = Atan2(y-this.cx, x-this.cx)
if a > 0. then
set a = this.kbaL
else
set a = this.kbaR
endif
call UnitDamageTarget(this.c, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS)
call Knockback3D.add(u, KB_VEL, a, KB_ALPHA)
call IssueTargetOrder(this.DUMMY, SECONDARY_ORDER, u)
call GroupAddUnit(this.HIT_GROUP, u)
endif
endloop
set u = null
endmethod
method onEnd takes nothing returns nothing
//called when the spell instance ends
endmethod
endmodule
public struct SpellData
static group SEARCH_GROUP = CreateGroup()
group HIT_GROUP = null
unit DUMMY = null
unit c = null
player p = null
integer l = 0
integer d = 0
effect e = null
real cx = 0.
real cy = 0.
real dx = 0.
real dy = 0.
real kbaL = 0.
real kbaR = 0.
method prepDummy takes integer abil returns nothing
call UnitAddAbility(this.DUMMY, abil)
call SetUnitAbilityLevel(this.DUMMY, abil, this.l)
endmethod
implement spellBehavior
method endInstance takes nothing returns nothing
call this.onEnd()
call RemoveUnit(this.DUMMY)
call DestroyGroup(this.HIT_GROUP)
//call DestroyEffect(this.e)
call RemoveEffect(this.e)
set GSD[CURR] = GSD[COUNT-1]
set COUNT = COUNT-1
set CURR = CURR-1
if COUNT == 0 then
call PauseTimer(SpellTimer)
endif
call this.destroy()
endmethod
static method periodic takes nothing returns nothing
local SpellData sd
set CURR = 0
loop
exitwhen CURR >= COUNT
set sd = GSD[CURR]
set sd.cx = sd.cx + sd.dx
set sd.cy = sd.cy + sd.dy
call BlzSetSpecialEffectX(sd.e, sd.cx)
call BlzSetSpecialEffectY(sd.e, sd.cy)
call BlzSetSpecialEffectHeight(sd.e, EFFECT_H)
call SetUnitX(sd.DUMMY, sd.cx)
call SetUnitY(sd.DUMMY, sd.cy)
call sd.onPeriodic()
set sd.d = sd.d-1
if sd.d <= 0 then
call sd.endInstance()
endif
set CURR = CURR+1
endloop
endmethod
static method create takes unit c, real x, real y returns thistype
local thistype sd = thistype.allocate()
local real a
set sd.c = c
set sd.p = GetOwningPlayer(sd.c)
set sd.l = GetUnitAbilityLevel(sd.c, SPELL_ID)
set sd.cx = GetUnitX(sd.c)
set sd.cy = GetUnitY(sd.c)
set a = Atan2(y-sd.cy, x-sd.cx)
set sd.kbaL = a + bj_PI/2.
set sd.kbaR = a - bj_PI/2.
set sd.dx = VELOCITY*Cos(a)*TIMER_TICK
set sd.dy = VELOCITY*Sin(a)*TIMER_TICK
set sd.d = R2I(DISTANCE/VELOCITY/TIMER_TICK)
set sd.e = AddSpecialEffect(EFFECT_1A, sd.cx, sd.cy)
call BlzSetSpecialEffectColor(sd.e, E1A_COL_R, E1A_COL_G, E1A_COL_B)
call BlzSetSpecialEffectHeight(sd.e, EFFECT_H)
call BlzSetSpecialEffectScale(sd.e, E1A_SCALE)
call BlzSetSpecialEffectTimeScale(sd.e, E1A_TSCALE)
call BlzSetSpecialEffectTime(sd.e, E1A_TSKIP)
set sd.DUMMY = CreateUnit(sd.p, DUMMY_ID, sd.cx, sd.cy, 0.)
set sd.HIT_GROUP = CreateGroup()
if COUNT == 0 then
call TimerStart(SpellTimer, TIMER_TICK, true, function thistype.periodic)
endif
set GSD[COUNT] = sd
set COUNT = COUNT+1
return sd
endmethod
static method onCast takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call SpellData.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
call GSD[COUNT-1].onStart()
endif
return false
endmethod
static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(CastTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(CastTrigger, Condition(function thistype.onCast))
call thistype.onInitEx()
endmethod
endstruct
endlibrary