library ChillingGale requires T32, TerrainPathability
// v 1.0.3 by 3yeballz
// Timer32 by Jesus4Lyf | http://www.thehelper.net/forums/showthread.php/132538
// TerrainPathability by RisingDusk | http://www.wc3c.net/showthread.php?t=103862
/////////////////////////////////////
/* User Configuration */
/////////////////////////////////////
globals
// rawcode of spell
private constant integer SPELLID = 'A000'
// rawcode of tornado dummy unit
private constant integer TORNADO = 'h000'
// number of callbacks to build up
private constant integer BUILDUP = 340
// number of callbacks until end
private constant integer MAXCOUNT = 640
// initial rotation speed of the tornado
private constant real INITSPEED = 3 * bj_DEGTORAD
// acceleration of the rotation speed
private constant real ACCELERATION = 0.94 // must be lower than 1
private constant real ACCEL = 0.15 * bj_DEGTORAD
// amount of damage dealt. damage is equal to rotation speed multiplied by this constant
private constant real DMGFACTOR = 3.4
// pull strength of the tornado
private constant real MOVEFACTOR = 40
// number of callbacks until more dummy units spawn
private constant integer PHASE1 = 50
private constant integer PHASE2 = 120
// distance factor for additionally spawning dummy units
private constant real DISTFACTOR1 = 0.6
private constant real DISTFACTOR2 = 0.3
// radius offset that it looks more realistic
private constant real OFFSET = 40.
// pull direction. must be between 90 and 180
private constant real DEGREE = 110. * bj_DEGTORAD
private constant attacktype atktype = ATTACK_TYPE_NORMAL
private constant damagetype dmgtype = DAMAGE_TYPE_COLD
private constant weapontype wpntype = null
endglobals
private function Damage takes real r, integer level returns real
return r + r * level
endfunction
/////////////////////////////////////
/* End of User Configuration */
/////////////////////////////////////
private module ChillingGaleInit
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Filter(function thistype.SpellCast))
set filter = Filter(function thistype.SpellEffect)
endmethod
endmodule
private struct Tornado
real x
real y
real facing
unit t
static method create takes real setx, real sety, real degree returns thistype
local thistype this = thistype.allocate()
set this.x = setx
set this.y = sety
set this.facing = degree
set this.t = CreateUnit(Player(15), TORNADO, this.x, this.y, 0.)
return this
endmethod
method destroy takes nothing returns nothing
call RemoveUnit(this.t)
call this.deallocate()
endmethod
endstruct
private struct ChillingGale
private static thistype temp
private static boolexpr filter
private static group g = CreateGroup()
private unit caster
private player owner
private real midx
private real midy
private integer count
private integer level
private real speed
private real dist
private real damage
private Tornado array tornado [12]
private static method SpellEffect takes nothing returns boolean
local unit u = GetFilterUnit()
local real facing
local real locx
local real locy
local real dist
local real x
local real y
if not (IsUnitEnemy(u, temp.owner) and GetWidgetLife(u) > 0.405 and GetUnitTypeId(u) != 0) then
set u = null
return false
endif
set locx = GetUnitX(u)
set locy = GetUnitY(u)
set x = locx - temp.midx
set y = locy - temp.midy
set dist = SquareRoot(x*x+y*y)
call UnitDamageTarget(temp.caster, u, temp.damage * (temp.dist - dist) / temp.dist, false, false, atktype, dmgtype, wpntype)
set facing = Atan2(locy - temp.midy, locx - temp.midx) + DEGREE
call IsTerrainWalkable(locx + temp.speed * Cos(facing) * MOVEFACTOR, locy + temp.speed * Sin(facing) * MOVEFACTOR)
call SetUnitX(u, TerrainPathability_X)
call SetUnitY(u, TerrainPathability_Y)
set u = null
return false
endmethod
private method periodic takes nothing returns nothing
local integer i = 0
set this.count = this.count + 1
if this.count < BUILDUP then
set this.dist = this.dist + 1. + Pow(0.994, this.count) * 1.2
set this.speed = this.speed + Pow(ACCELERATION, this.count) * ACCEL
set this.damage = Damage(this.speed, this.level) * DMGFACTOR
loop
exitwhen i == 4
set this.tornado[i].facing = this.tornado[i].facing + this.speed
set this.tornado[i].x = this.midx + Cos(this.tornado[i].facing) * this.dist
set this.tornado[i].y = this.midy + Sin(this.tornado[i].facing) * this.dist
call SetUnitX(this.tornado[i].t, this.tornado[i].x)
call SetUnitY(this.tornado[i].t, this.tornado[i].y)
set i = i + 1
endloop
if this.count > PHASE1 then
loop
exitwhen i == 8
set this.tornado[i].facing = this.tornado[i].facing + this.speed
set this.tornado[i].x = this.midx + Cos(this.tornado[i].facing) * this.dist * DISTFACTOR1
set this.tornado[i].y = this.midy + Sin(this.tornado[i].facing) * this.dist * DISTFACTOR1
call SetUnitX(this.tornado[i].t, this.tornado[i].x)
call SetUnitY(this.tornado[i].t, this.tornado[i].y)
set i = i + 1
endloop
if this.count > PHASE2 then
loop
exitwhen i == 12
set this.tornado[i].facing = this.tornado[i].facing + this.speed
set this.tornado[i].x = this.midx + Cos(this.tornado[i].facing) * this.dist * DISTFACTOR2
set this.tornado[i].y = this.midy + Sin(this.tornado[i].facing) * this.dist * DISTFACTOR2
call SetUnitX(this.tornado[i].t, this.tornado[i].x)
call SetUnitY(this.tornado[i].t, this.tornado[i].y)
set i = i + 1
endloop
elseif this.count == PHASE2 then
loop
exitwhen i == 12
set this.tornado[i] = Tornado.create(this.midx+Cos(this.tornado[i-8].facing)*this.dist*DISTFACTOR2, this.midy+Sin(this.tornado[i-8].facing)*this.dist*DISTFACTOR2, this.tornado[i-8].facing)
set i = i + 1
endloop
endif
elseif this.count == PHASE1 then
loop
exitwhen i == 8
set this.tornado[i] = Tornado.create(this.midx+Cos(this.tornado[i-4].facing+bj_PI/4)*this.dist*DISTFACTOR1, this.midy+Sin(this.tornado[i-4].facing+bj_PI/4)*this.dist*DISTFACTOR1, this.tornado[i-4].facing+bj_PI/4)
set i = i + 1
endloop
endif
else
loop
exitwhen i == 4
set this.tornado[i].facing = this.tornado[i].facing + this.speed
set this.tornado[i].x = this.midx + Cos(this.tornado[i].facing) * this.dist
set this.tornado[i].y = this.midy + Sin(this.tornado[i].facing) * this.dist
call SetUnitX(this.tornado[i].t, this.tornado[i].x)
call SetUnitY(this.tornado[i].t, this.tornado[i].y)
set i = i + 1
endloop
loop
exitwhen i == 8
set this.tornado[i].facing = this.tornado[i].facing + this.speed
set this.tornado[i].x = this.midx + Cos(this.tornado[i].facing) * this.dist * DISTFACTOR1
set this.tornado[i].y = this.midy + Sin(this.tornado[i].facing) * this.dist * DISTFACTOR1
call SetUnitX(this.tornado[i].t, this.tornado[i].x)
call SetUnitY(this.tornado[i].t, this.tornado[i].y)
set i = i + 1
endloop
loop
exitwhen i == 12
set this.tornado[i].facing = this.tornado[i].facing + this.speed
set this.tornado[i].x = this.midx + Cos(this.tornado[i].facing) * this.dist * DISTFACTOR2
set this.tornado[i].y = this.midy + Sin(this.tornado[i].facing) * this.dist * DISTFACTOR2
call SetUnitX(this.tornado[i].t, this.tornado[i].x)
call SetUnitY(this.tornado[i].t, this.tornado[i].y)
set i = i + 1
endloop
endif
set temp = this
call GroupEnumUnitsInRange(g, this.midx, this.midy, this.dist + OFFSET, filter)
if this.count == MAXCOUNT then
call this.stopPeriodic()
call this.destroy()
endif
endmethod
implement T32x
private static method create takes nothing returns thistype
local thistype this = thistype.allocate()
local integer i = 0
set this.caster = GetTriggerUnit()
set this.owner = GetOwningPlayer(this.caster)
set this.midx = GetSpellTargetX()
set this.midy = GetSpellTargetY()
set this.count = 0
set this.level = GetUnitAbilityLevel(this.caster, SPELLID)
set this.speed = INITSPEED
set this.dist = 30.
loop
exitwhen i == 4
set this.tornado[i] = Tornado.create(this.midx+Cos(i*bj_PI/2)*this.dist, this.midy+Sin(i*bj_PI/2), i*bj_PI/2)
set i = i + 1
endloop
return this
endmethod
private method destroy takes nothing returns nothing
local integer i = 0
loop
exitwhen i == 12
call this.tornado[i].destroy()
set i = i + 1
endloop
call this.deallocate()
endmethod
private static method SpellCast takes nothing returns boolean
if GetSpellAbilityId() == SPELLID then
call thistype.create().startPeriodic()
endif
return false
endmethod
implement ChillingGaleInit
endstruct
endlibrary