Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
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
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//* > function IsTerrainDeepWater takes real x, real y returns boolean
//* > function IsTerrainShallowWater takes real x, real y returns boolean
//* > function IsTerrainLand takes real x, real y returns boolean
//* > function IsTerrainPlatform takes real x, real y returns boolean
//* > function IsTerrainWalkable takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//* [real] TerrainPathability_X
//* [real] TerrainPathability_Y
//*
globals
private constant real MAX_RANGE = 10.
private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals
globals
private item Item = null
private rect Find = null
private item array Hid
private integer HidMax = 0
public real X = 0.
public real Y = 0.
endglobals
function IsTerrainDeepWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
private function HideItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hid[HidMax] = GetEnumItem()
call SetItemVisible(Hid[HidMax], false)
set HidMax = HidMax + 1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
//Hide any items in the area to avoid conflicts with our item
call MoveRectTo(Find, x, y)
call EnumItemsInRect(Find ,null, function HideItem)
//Try to move the test item and get its coords
call SetItemPosition(Item, x, y) //Unhides the item
set X = GetItemX(Item)
set Y = GetItemY(Item)
static if LIBRARY_IsTerrainWalkable then
//This is for compatibility with the IsTerrainWalkable library
set IsTerrainWalkable_X = X
set IsTerrainWalkable_Y = Y
endif
call SetItemVisible(Item, false)//Hide it again
//Unhide any items hidden at the start
loop
exitwhen HidMax <= 0
set HidMax = HidMax - 1
call SetItemVisible(Hid[HidMax], true)
set Hid[HidMax] = null
endloop
//Return walkability
return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
private function Init takes nothing returns nothing
set Find = Rect(0., 0., 128., 128.)
set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary