Moderator
M
Moderator
08:15, 14th Apr 2010
TriggerHappy:
The only thing I would suggest is dummy recycling.
TriggerHappy:
The only thing I would suggest is dummy recycling.
WhirlwindConjures several small tornadoes that ravages the target area, dealing damage to nearby enemy units and slowing their movement speed. Enemy units that move too slowly will be tossed into the air, rendering them helpless.
Tornadoes accelerate every second, increasing their speed, damage, and slow.
//==========================================================================================
// Whirlwind v2.00 by watermelon_1234
//******************************************************************************************
// Libraries required: (Libraries with * are optional)
// - T32 http://www.thehelper.net/threads/timer32.118245/
// - xe system http://www.wc3c.net/showthread.php?t=101150
// * BoundSentinel http://www.wc3c.net/showthread.php?t=102576
//##########################################################################################
// Importing:
// 1. Copy the abilities: Whirlwind and Slow Aura(Whirlwind). Copy the buff Whirlwind (Slow Aura)
// 2. Implement the required libraries
// 3. Copy this trigger
// 4. Configure the spell, including the abilities
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Notes:
// - Casting time has no effect on the actual ability.
// - If a targeting spell, such as Blizzard, is targeted over the tornado, it will highlight
// it in green because it's a unit.
//==========================================================================================
scope Whirlwind initializer Init
//==========================================================================================
// CONSTANTS
//==========================================================================================
globals
//------------------------------------------------------------------------------------------
// General spell settings
//------------------------------------------------------------------------------------------
private constant integer SPELL_ID = 'A000' // The raw id of the Whirlwind ability
private constant integer SLOW_ID = 'A001' // The raw id of the Slow Aura (Whirlwind) ability
private constant integer CYCLONE_ID = 'S000' // The raw id of the Cyclone (Whirlwind) ability
private constant integer CYCLONE_BUFF = 'Bcy2' // The buff of the Cyclone (Whirlwind) ability
private constant integer CYCLONE_OID = 852144 // The order id of the Cyclone ability
private constant boolean PRELOAD = true // Determines whether or not to preload abilities and SFX
//------------------------------------------------------------------------------------------
// Tornado settings
//------------------------------------------------------------------------------------------
private constant real TORNADO_AREA = 100. // The area that each tornado deals damage
private constant string TORNADO_SFX = "Abilities\\Spells\\Other\\Tornado\\TornadoElementalSmall.mdl" // The sfx used for the tornado
private constant integer MAX_TORNADOES = 5 // The max number of tornadoes that can be spawned
private constant real ROTATION_SPEED = bj_PI * 3 / 4 // The number of radians the tornado will move per second
//------------------------------------------------------------------------------------------
// Damage settings
//------------------------------------------------------------------------------------------
private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WPN_TYPE = null
endglobals
//==========================================================================================
// OTHER CONFIGURATION
//==========================================================================================
// The target area of the spell
private constant function Area takes integer lvl returns real
return 250. + 100*lvl
endfunction
// Determines which units will be affected by the spell
private constant function AffectedTargets takes unit target, player owner returns boolean
return not IsUnitType(target, UNIT_TYPE_DEAD) and /*
*/ IsUnitEnemy(target, owner) and /*
*/ not IsUnitType(target, UNIT_TYPE_STRUCTURE) and /*
*/ not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
// Determines which units will be checked for cycloning if moving too slowly
private function AffectedCycloneTargets takes unit target, player owner returns boolean
return not IsUnitType(target, UNIT_TYPE_DEAD) and /*
*/ IsUnitEnemy(target, owner) and /*
*/ not IsUnitType(target, UNIT_TYPE_STRUCTURE) and /*
*/ not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ not IsUnitType(target, UNIT_TYPE_FLYING) and /*
*/ GetUnitAbilityLevel(target, CYCLONE_BUFF) < 1
endfunction
// How long will the tornadoes last
private constant function Duration takes integer lvl returns real
return 6. + 2*lvl
endfunction
// The number of tornadoes spawned per spell level. Change MAX_TORNADOES accordingly.
private constant function Number takes integer lvl returns integer
return 2 + 1*lvl
endfunction
// The damage dealt by a tornado per second
private constant function Damage takes integer lvl returns real
return 7. + 3*lvl
endfunction
// The number of radians the tornado accelerates by per second
// Damage is increased by the tornado's speed compared to ROTATION_SPEED
private constant function Acceleration takes integer lvl returns real
return bj_PI / 10 + bj_PI / 14 * (lvl - 1)
endfunction
// The number of seconds it takes to increase the slow ability of the Whirlwind
// Be sure to configure this with Duration accordingly
private constant function UpdateSlowInterval takes integer lvl returns real
return 3.0
endfunction
// The move speed at which units will be cycloned for moving too slowly
private constant function MoveSpeedThreshold takes integer lvl returns real
return 175.0
endfunction
//==========================================================================================
// END OF CONFIGURATION
//==========================================================================================
private struct Data
unit cast // The caster unit
player owner // Owner of the caster
integer lvl // The level of the spell when the spell was used
real area // The spell target area
real accel // The tornado's acceleration
real x // The x coordinate of the spell target
real y // The y coordinate of the spell target
real count = 0 // Counts how long the spell has been running
real update = 0 // Determines when to update the slow ability level for each tornado
xefx array tornado[MAX_TORNADOES] // Holds the tornado spawns
real array ang[MAX_TORNADOES] // Used to make the tornado cycle
real array dist[MAX_TORNADOES] // The distance the tornado is from the spell target's coordinates
real array speed[MAX_TORNADOES] // Determines the speed of the tornado based on its distance and ROTATION_SPEED
integer array turn[MAX_TORNADOES] // The direction of the tornado. 1 is counterclockwise. -1 is clockwise
method periodic takes nothing returns nothing
local integer i = 0
local unit u
local real dmg
local boolean increaseSlow = false
local xecast xc
local real life
if .count < Duration(.lvl) then
set .count = .count + T32_PERIOD
set .update = .update + T32_PERIOD
if .update > UpdateSlowInterval(.lvl) then
set increaseSlow = true
set .update = .update - UpdateSlowInterval(.lvl)
endif
loop
set .speed[i] = .speed[i] + .accel * T32_PERIOD
set .ang[i] = .ang[i] + .turn[i]*.speed[i] * T32_PERIOD
set .tornado[i].x = .x + .dist[i]*Cos(.ang[i])
set .tornado[i].y = .y + .dist[i]*Sin(.ang[i])
if increaseSlow then
set .tornado[i].abilityLevel = .tornado[i].abilityLevel + 1
endif
call GroupEnumUnitsInRange(bj_lastCreatedGroup, .tornado[i].x, .tornado[i].y, TORNADO_AREA, null)
set dmg = Damage(.lvl) * T32_PERIOD * .speed[i] / ROTATION_SPEED
loop
set u = FirstOfGroup(bj_lastCreatedGroup)
exitwhen u == null
call GroupRemoveUnit(bj_lastCreatedGroup, u)
if AffectedTargets(u, .owner) then
// still deal damage to cycloned units by decreasing life
// If life is too low, removes the buff and attempts to kill target
if GetUnitAbilityLevel(u, CYCLONE_BUFF) > 0 then
set life = GetWidgetLife(u)
if life - dmg > 0.405 then
call SetWidgetLife(u, life - dmg)
else
call UnitRemoveAbility(u, CYCLONE_BUFF)
call UnitDamageTarget(.cast, u, 9999, false, true, ATK_TYPE, DMG_TYPE, WPN_TYPE)
endif
else
call UnitDamageTarget(.cast, u, dmg, false, true, ATK_TYPE, DMG_TYPE, WPN_TYPE)
endif
endif
if AffectedCycloneTargets(u, .owner) and /*
*/ GetUnitMoveSpeed(u) < MoveSpeedThreshold(.lvl) then
set xc = xecast.createBasicA(CYCLONE_ID, CYCLONE_OID, .owner)
set xc.level = .lvl
call xc.castOnTarget(u)
endif
endloop
set i = i + 1
exitwhen i == Number(.lvl)
endloop
else // Clean up the spell
loop
call .tornado[i].destroy() // Destroys the tornao sfx
set i = i + 1
exitwhen i == Number(.lvl)
endloop
call .stopPeriodic()
call .destroy() // The spell is done, destroy the struct
endif
endmethod
implement T32x
static method create takes unit c, real x, real y returns thistype
local thistype this = thistype.allocate()
local integer i = 0
local integer mod
local real randx
local real randy
local real distx
local real disty
set .cast = c
set .owner = GetOwningPlayer(c)
set .x = x
set .y = y
set .lvl = GetUnitAbilityLevel(c, SPELL_ID)
set .area = Area(.lvl)
set .accel = Acceleration(.lvl)
// Spawning the tornadoes
loop
// randomly spawn the tornado
set randx = .x + GetRandomReal(-.area/2,.area/2)
set randy = .y + GetRandomReal(-.area/2,.area/2)
set .tornado[i] = xefx.create(randx,randy,0)
set .tornado[i].fxpath = TORNADO_SFX
set .tornado[i].abilityid = SLOW_ID
set .tornado[i].abilityLevel = .lvl
set .tornado[i].owner = .owner
set distx = .x - randx
set disty = .y - randy
set .ang[i] = Atan2(disty, distx)
set .dist[i] = SquareRoot(distx*distx+disty*disty)
// farther tornadoes from the cast point move slower
set .speed[i] = ROTATION_SPEED * (.area-.dist[i]) /.area
// changes the rotation direction depending on what number they were spawned
set mod = i - (i / 2) * 2
if mod == 0 then
set .turn[i] = 1
else
set .turn[i] = -1
endif
set i = i + 1
exitwhen i == Number(.lvl)
endloop
call .startPeriodic()
return this
endmethod
endstruct
// Creates a Data struct when the spell is cast
private function Actions takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call Data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
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 Actions)) // Used a condition since it gets executed faster
set t = null // Just to null handles
static if PRELOAD then
call XE_PreloadAbility(SLOW_ID)
call XE_PreloadAbility(CYCLONE_ID)
call Preload(TORNADO_SFX)
endif
endfunction
endscope