Name | Type | is_array | initial_value |
UDex | integer | No | |
UDexGen | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No |
//TESH.scrollpos=150
//TESH.alwaysfold=0
library MissileRecycler initializer PreInit requires optional UnitIndexer, optional UnitDex, optional UnitIndexerGUI /*
MissileRecycler v 1.4.5.2
=========================================================================
Credits:
-------------------------------------------------------------------------
Written by Bribe
Vexorian, Anitarf and iNfraNe for the dummy.mdx model file
Nestharus for the Queue data structure and for finding that paused units
consume very few CPU resources.
=========================================================================
Introduction:
-------------------------------------------------------------------------
Recycling dummy units is important because the CreateUnit call is one of,
if not the, most processor-intensive native in the entire game. Creating
just a couple dozen dummy units in a single thread causes a visible frame
glitch for that instant. The overhead is even higher if you are using a
Unit Indexing library in the map which causes some extra evaluations per
new unit.
There are also reports of removed units leaving a little trail of RAM
surplus in their wake. I have not been able to reproduce this so I don't
know how serious it is.
I was motivated to create this system because removed units might be un-
safe in very large numbers and CreateUnit is a very heavy process.
The thing that makes this system different than others is the fact that
it considers the facing angle of the dummies being recycled, which I have
never seen another system even attempt before this. Since then,
MissileRecycler has inspired Anitarf to update XE with the same angle-retaining
capability and Nestharus has created Dummy. Considering the facing angle is
important because it takes 0.73 seconds for the unit to turn around,
which - when overlooked - looks especially weird if you are creating a unit-trail
or if you are shooting arrow-shaped objects as projectiles. For fireball effects or
effects that generally don't depend on facing angle, this system would be
a bit wasteful.
With default settings and the worst-case-scenario, it will take 0.09 seconds for
the projectile to turn to the angle you need. This is 1/8 of the normal worst case
scenario if you weren't recycling dummies considering facing. On average, it takes
roughly 0.045 seconds to turn to the angle you need (which is not noticable).
However, I have made this completely configurable and you are
able to change the values to whatever needs you have.
=========================================================================
Calibration Guide:
-------------------------------------------------------------------------
The thing that surprised me the most about this system was, no matter how
complex it turned out, it became very configurable. So I should let you
know what the constants do so you know if/how much you want to modify.
constant real DEATH_TIME = 2.0 //seconds
- Should not be less than the maximum time a death animation needs to play.
Should not be lower than .73 to ensure enough time to turn.
Should not be too high otherwise the dummies will take too long to recycle.
constant integer ANG_N = 8
- How many different angles are recognized by the system. Don't do
360 different angles because then you're going to have thousands of dummy
units stored and that's ridiculous, the game lags enough at 1000 units.
Increasing ANG_N increases realism but decreases the chance that a dummy
unit will be available to be recycled. I don't recommend making this any
lower, and the max I'd recommend would be 16.
constant integer ANG_STORAGE_MAX = 12
- How many dummy units are stored per angle. This limit is important
because you might have a spike at one point in the game where many units
are created, which could result in too high of a dummy population.
In general, I advise that the product of ANG_N x ANG_STORAGE_MAX does
not exceed 100 or 200. More than that is excessive, but you can
hypothetically have it up to 8190 if Warcraft 3's memory management
were better.
Preloads ANG_N x ANG_STORAGE_MAX dummy units. Preloading dummies is
useful as it dumps a lot of CreateUnit calls in initialization where you
won't see a frame glitch. In the 1.4 update, preloading is done 0 seconds
into the game to ensure any Indexers have already initialized.
private function ToggleIndexer takes boolean flag returns nothing
- Put what you need in here to disable/enable any indexer in your
map. if flag is true, enable indexer. If false, disable.
=========================================================================
API Guide:
-------------------------------------------------------------------------
You obviously need some functions so you can get a recycled dummy unit or
recycle it. Therefore I provide these:
function GetRecycledMissile
takes real x, real y, real z, real facing
returns unit
Returns a new dummy unit that acts as a projectile missile. The args
are simply the last three arguments you'd use for a CreateUnit call,
with the addition of a z parameter to represent the flying height -
it isn't the absolute z but relative to the ground because it uses
SetUnitFlyHeight on that value directly.
function RecycleMissile
takes unit u
returns nothing
When you are done with that dummy unit, recycle it via this function.
This function is pretty intelligent and resets that unit's animation
and its facing angle so you don't have to.
*/
//=======================================================================
// Save the map, then delete the exclaimation mark in the following line.
// Make sure that you don't have an object in your map with the rawcode
// 'dumi' and also configure the model path (war3mapImported\dummy.mdl)
// to the dummy.mdx model created by Vexorian.
///! external ObjectMerger w3u ewsp dumi unam "Missile Dummy" ufoo 0 utyp "" ubui "" uhom 1 ucol 0.01 umvt "None" umvr 1.00 utar "" uspa "" umdl "war3mapImported\dummy.mdl" umxr 0.00 umxp 0.00 ushr 0 uerd 0.00 udtm 0.00 ucbs 0.00 uble 0.00 uabi "Aloc,Amrf"
//Thanks to Vexorian that Optimizer 5.0 no longer kills natives
native UnitAlive takes unit id returns boolean
globals
//-------------------------------------------------------------------
// You must configure the dummy unit with the one created from the
// ObjectMerger statement above.
//
private constant integer DUMMY_ID = 'dumi' //The rawcode of the dummy unit.
private player OWNER = Player(14) //The owner of the dummy unit.
private constant integer ANG_N = 8 //# of indexed angles. Higher value increases realism but decreases recycle frequency.
private constant integer ANG_STORAGE_MAX = 12 //Max dummies per indexed angle. I recommend lowering this if you increase ANG_N.
private constant real DEATH_TIME = 2. //Allow the special effect on
//the unit to complete its "death" animation in this timeframe. Must
//be higher than 0.74 seconds to allow the unit time to turn. This
//number should not be lower than the maximum death-animation time of
//your missile-units' effect attachments, just to be safe.
endglobals
private function ToggleIndexer takes boolean flag returns nothing
static if LIBRARY_UnitIndexer then
set UnitIndexer.enabled = flag
elseif LIBRARY_UnitIndexerGUI then
set udg_UnitIndexerEnabled = flag
elseif LIBRARY_UnitDex then
set UnitDex.Enabled = flag
endif
endfunction
globals
private constant integer ANG_VAL = 360 / ANG_N //Generate angle value from ANG_N.
private constant integer ANG_MID = ANG_VAL / 2 //The middle value of angle value.
//Misc vars
private unit array stack //Recycled dummy units.
private real array timeStamp //Prevents early recycling of units.
private integer array queueNext
private integer array queueLast
private integer recycle = 0
private timer gameTime = CreateTimer() //Used for visual continuity.
private integer array queueStack
private integer queueStackN = 0 //Used to avoid searching the queues.
endglobals
static if DEBUG_MODE then
private function Print takes string s returns nothing
//Un-comment this next line if you want to know how the system works:
//call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 999, "[MissileRecycler] " + s)
endfunction
endif
//=======================================================================
// Get a recycled dummy missile unit. If there are no recycled dummies
// that are already facing the angle you need, it creates a new dummy for
// you.
//
function GetRecycledMissile takes real x, real y, real z, real facing returns unit
local integer i = ModuloInteger(R2I(facing), 360) / ANG_VAL
local integer this = queueNext[i]
local unit u
if this != 0 and TimerGetElapsed(gameTime) >= timeStamp[this] then
//Dequeue this
set queueNext[i] = queueNext[this]
if queueNext[i] == 0 then
set queueLast[i] = i
endif
//Recycle this index
set queueLast[this] = recycle
set recycle = this
//Add queue index to available stack
set queueStack[queueStackN] = i
set queueStackN = queueStackN + 1
//Old unit will return as new
set u = stack[this]
call SetUnitFacing(u, facing)
call SetUnitUserData(u, 0)
//Reset the dummy's properties.
call SetUnitVertexColor(u, 255, 255, 255, 255)
call SetUnitAnimationByIndex(u, 90)
call SetUnitScale(u, 1, 0, 0)
//call PauseUnit(u, false) -- you can disable "resets" that you don't need to worry about.
debug call Print("Recycling")
else
debug call Print("Creating new")
call ToggleIndexer(false)
set u = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
call ToggleIndexer(true)
call PauseUnit(u, true)
endif
call SetUnitX(u, x)
call SetUnitY(u, y)
call SetUnitFlyHeight(u, z, 0)
set bj_lastCreatedUnit = u
set u = null
return bj_lastCreatedUnit
endfunction
//=======================================================================
// You should recycle the dummy missile unit when its job is done.
//
function RecycleMissile takes unit u returns nothing
local integer i
local integer this = recycle
if GetUnitTypeId(u) == DUMMY_ID and UnitAlive(u) and GetUnitUserData(u) != -1 then
if queueStackN == 0 then
debug call Print("Stack is full - removing surplus unit")
call UnitApplyTimedLife(u, 'BTLF', DEATH_TIME)
return
endif
//Recycle this
set recycle = queueLast[this]
//Index the dummy unit to an available facing angle.
//Get the last vacant angle index.
set queueStackN = queueStackN - 1
set i = queueStack[queueStackN]
//Enqueue this
set queueNext[queueLast[i]] = this
set queueLast[i] = this
set queueNext[this] = 0
//Allow a time barrier for the effect to destroy/turn to complete.
set timeStamp[this] = TimerGetElapsed(gameTime) + DEATH_TIME
set stack[this] = u
call SetUnitFacing(u, i * ANG_VAL + ANG_MID)
call SetUnitOwner(u, OWNER, false)
//Prevent double-free of this unit.
call SetUnitUserData(u, -1)
debug else
debug call BJDebugMsg("[MissileRecycler] Error: Attempt to recycle invalid unit.")
endif
endfunction
//=======================================================================
// I didn't need this function after all
//
function RecycleMissileDelayed takes unit u, real r returns nothing
call RecycleMissile(u)
endfunction
//=======================================================================
// Map the dummy units to their facing angles (map below is if ANG_N is
// 4 and ANG_STORAGE_MAX is 3).
//
// angle[0] (0) - [4] [5] [6]
// angle[1] (90) - [7] [8] [9]
// angle[2] (180) - [10][11][12]
// angle[3] (270) - [13][14][15]
//
private function Init takes nothing returns nothing
local integer end
local integer i = ANG_N
local integer n = i
local integer angle
local real x = GetRectMaxX(bj_mapInitialPlayableArea)
local real y = GetRectMaxY(bj_mapInitialPlayableArea)
local unit u
call ToggleIndexer(false)
loop
set i = i - 1
set queueNext[i] = n
set angle = i * ANG_VAL + ANG_MID
set end = n + ANG_STORAGE_MAX
set queueLast[i] = end - 1
loop
set queueNext[n] = n + 1
set u = CreateUnit(OWNER, DUMMY_ID, x, y, angle)
set stack[n] = u
call PauseUnit(u, true)
call SetUnitUserData(u, -1)
set n = n + 1
exitwhen n == end
endloop
set queueNext[n - 1] = 0
exitwhen i == 0
endloop
call ToggleIndexer(true)
call TimerStart(gameTime, 1000000., false, null)
set u = null
endfunction
private function PreInit takes nothing returns nothing
static if LIBRARY_UnitIndexerGUI then
call OnUnitIndexerInitialized(function Init)
else
call Init()
endif
endfunction
endlibrary
//TESH.scrollpos=43
//TESH.alwaysfold=0
library ZLibrary //// by D.O.G. version 2.6 ////
///////////////////////////////////////////////
/////////////////// A P I ///////////////////
///////////////////////////////////////////////
// function GetSurfaceZ takes real x, real y returns real
// - Gets terrain Z if specified point is on the ground
// or water surface Z if specified point is in the water
///////////////////////////////////////////////
// function GetTerrainZ takes real x, real y returns real
// - Gets terrain Z regardless of specified point position
///////////////////////////////////////////////
// function GetUnitZ takes unit u returns real
// function SetUnitZ takes unit u, real z returns nothing
// - They work perfectly on ground units with
// pre-added and removed "Crow Form" ability,
// BUT inaccurately on flying units near and
// over cliffs due to War3 smooth flying over
// cliffs system
///////////////////////////////////////////////
// function CreateUnitZ takes player p, integer unitid, real x, real y, real z, real face returns unit
// - The same as "CreateUnit" but with extra parameter Z
///////////////////////////////////////////////
// function GetWaterDepth takes real x, real y returns real
// - gets water depth in specified point
// (returns 0 if no water else positive real number)
///////////////////////////////////////////////
// function GetWaterType takes real x, real y returns integer
// - returns WATER_TYPE_NONE, WATER_TYPE_SHALLOW or WATER_TYPE_DEEP
///////////////////////////////////////////////
///////////// C O N S T A N T S /////////////
///////////////////////////////////////////////
globals
constant integer WATER_TYPE_NONE = 0
constant integer WATER_TYPE_SHALLOW = 1
constant integer WATER_TYPE_DEEP = 2
constant real WATER_DEPTH_SHALLOW = 52.0
endglobals
///////////////////////////////////////////////
////////////////// C O D E //////////////////
///////////////////////////////////////////////
globals
private constant integer PLATFORM = 'GetZ'
private constant real PLATFORM_HEIGHT = 2745.46265
private constant location p = Location(0.0, 0.0)
endglobals
///! external ObjectMerger w3b OTis GetZ bnam "GetZ" bptx "" bfil "Doodads\Terrain\WoodBridgeLarge0\WoodBridgeLarge0"
function GetSurfaceZ takes real x, real y returns real
call MoveLocation(p, x, y)
return GetLocationZ(p)
endfunction
function GetTerrainZ takes real x, real y returns real
local real z
local destructable d = CreateDestructable(PLATFORM, x, y, 0.0, 10.0, 0)
call MoveLocation(p, x, y)
set z = GetLocationZ(p) - PLATFORM_HEIGHT
call RemoveDestructable(d)
set d = null
return z
endfunction
function GetUnitZ takes unit u returns real
if IsUnitType(u, UNIT_TYPE_FLYING) then
return GetSurfaceZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
endif
return GetTerrainZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
endfunction
function SetUnitZ takes unit u, real z returns nothing
if IsUnitType(u, UNIT_TYPE_FLYING) then
call SetUnitFlyHeight(u, z - GetSurfaceZ(GetUnitX(u), GetUnitY(u)), 0.0)
else
call SetUnitFlyHeight(u, z - GetTerrainZ(GetUnitX(u), GetUnitY(u)), 0.0)
endif
endfunction
function CreateUnitZ takes player p, integer unitid, real x, real y, real z, real face returns unit
set bj_lastCreatedUnit = CreateUnit(p, unitid, x, y, face)
// Enable setting fly height for ground units
static if not LIBRARY_AutoFly then
if UnitAddAbility(bj_lastCreatedUnit, 'Amrf') then // "Crow Form"
call UnitRemoveAbility(bj_lastCreatedUnit, 'Amrf')
endif
endif
// Finally set Z
call SetUnitZ(bj_lastCreatedUnit, z)
return bj_lastCreatedUnit
endfunction
function GetWaterDepth takes real x, real y returns real
local real z
local destructable d
call MoveLocation(p, x, y)
set z = GetLocationZ(p)
set d = CreateDestructable(PLATFORM, x, y, 0.0, 10.0, 0)
set z = z + PLATFORM_HEIGHT - GetLocationZ(p)
call RemoveDestructable(d)
set d = null
if z >= 0.0 then // Small negative results may occur such as -0.0001
return z
endif
return 0.0
endfunction
function GetWaterType takes real x, real y returns integer
local real d = GetWaterDepth(x, y)
if d == 0.0 then
return WATER_TYPE_NONE
elseif d < WATER_DEPTH_SHALLOW then
return WATER_TYPE_SHALLOW
endif
return WATER_TYPE_DEEP
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
library Projection
function GetMagnitude2D takes real x, real y returns real
return SquareRoot(x*x+y*y)
endfunction
function GetMagnitude3D takes real x, real y, real z returns real
return SquareRoot(x*x+y*y+z*z)
endfunction
function GetAngle2D takes real x, real y returns real
return Atan2(y, x)
endfunction
function GetAngle3D takes real distance2D, real z returns real
return Atan2(z, distance2D)
endfunction
function GetProjectedX takes real distance2D, real angle2D returns real
return distance2D*Cos(angle2D)
endfunction
function GetProjectedY takes real distance2D, real angle2D returns real
return distance2D*Sin(angle2D)
endfunction
function GetProjectedZ takes real distance3D, real angle3D returns real
return distance3D*Sin(angle3D)
endfunction
function GetProjectedZ2 takes real distance2D, real angle3D returns real
return Tan(angle3D)*distance2D
endfunction
function GetDistance2D takes real distance3D, real angle3D returns real
return distance3D*Cos(angle3D)
endfunction
function GetDistance3D takes real distance2D, real angle3D returns real
return distance2D/Cos(angle3D)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GetUnitCollision /* v2.0.1.0
*************************************************************************************
*
* Retrieves collision size for a unit (different from pathing map)
*
* Assumes collision will always be an integer
*
* 100% accurate to 1 decimal for collision sizes >= 5.1
*
*************************************************************************************
*
* */uses/*
*
* */ Table /* hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
*************************************************************************************
*
* Functions
*
* function GetUnitCollision takes unit whichUnit returns real
*
************************************************************************************/
globals
private Table uc
endglobals
private function C takes unit u, real x, real y, integer i returns real
local real l = 0
local real h = 300
local real m = 150
local real nm
loop
if (IsUnitInRangeXY(u, x+m, y, 0)) then
set l = m
else
set h = m
endif
set nm = (l+h)/2
exitwhen nm+.001 > m and nm-.001 < m
set m = nm
endloop
set m = R2I(m*10)/10.
set uc.real[i] = m
return m
endfunction
function GetUnitCollision takes unit u returns real
local integer i = GetUnitTypeId(u)
if (uc.real.has(i)) then
return uc.real[i]
endif
return C(u, GetUnitX(u), GetUnitY(u), i)
endfunction
private module Initializer
private static method onInit takes nothing returns nothing
set uc = Table.create()
endmethod
endmodule
private struct init extends array
implement Initializer
endstruct
endlibrary
//TESH.scrollpos=72
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
*
* Fields
* -------------------------
*
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
*
* readonly static integer centerX
* readonly static integer centerY
*
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX = R2I(GetRectMaxX(world))
set maxY = R2I(GetRectMaxY(world))
set minX = R2I(GetRectMinX(world))
set minY = R2I(GetRectMinY(world))
set centerX = R2I((maxX + minX)/2)
set centerY = R2I((minY + maxY)/2)
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=27
//TESH.alwaysfold=0
/*
*=================================================================================*
* Map Bounds 1.2 *
* By Adiktuz *
* *
* *
* This is a short snippet that provides some useful functions utilizing *
* the WorldBounds library created by Nestharus *
* *
* *
* Credits: *
* Nestharus for the WorldBounds library *
* Magtheridon96 for suggestions *
* *
* Functions: *
* *
* -> MapContainsX(real x) returns boolean *
* -> MapContainsY(real y) returns boolean *
* -> GetBoundedX(real x) returns real *
* -> GetBoundedY(real y) returns real *
* *
* *
* The MapContainsX/Y functions return if the given x/y value is inside the map *
* *
* The GetBoundedX/Y checks if the given value is within the map. If it's *
* within the map, it will return the value given and if Not *
* it will return the value of the border that it will exceed *
* *
*=================================================================================*
*/
library MapBounds requires WorldBounds
function MapContainsX takes real x returns boolean
return x < WorldBounds.maxX and x > WorldBounds.minX
endfunction
function MapContainsY takes real y returns boolean
return y < WorldBounds.maxY and y > WorldBounds.minY
endfunction
function GetBoundedX takes real x returns real
if x > WorldBounds.maxX then
return I2R(WorldBounds.maxX)
elseif x < WorldBounds.minX then
return I2R(WorldBounds.minX)
endif
return x
endfunction
function GetBoundedY takes real y returns real
if y > WorldBounds.maxY then
return I2R(WorldBounds.maxY)
elseif y < WorldBounds.minY then
return I2R(WorldBounds.minY)
endif
return y
endfunction
endlibrary
//TESH.scrollpos=36
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=513
//TESH.alwaysfold=0
library GigaDrain/* 1.0 by Almia
*************************************************************************************
*
* Giga Drain
*
* Deals 100/200/300 damage to the target unit, per 0.125 seconds. The caster
* then absorbs the damage dealt, healing for 50/60/70 % of the damage dealt.
* The healing/damage is divided to the number of particles released.
*
*
*************************************************************************************
*
* */ requires /*
*
* */MissileRecycler /* hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
* */Projection /* hiveworkshop.com/forums/2038279-post655.html
* */GetUnitCollision /* github.com/nestharus/JASS/blob/master/jass/Systems/GetUnitCollision/script.j
* */MapBounds /* hiveworkshop.com/forums/jass-resources-412/snippet-mapbounds-222870/
* */ZLibrary /* hiveworkshop.com/forums/jass-resources-412/snippet-zlibrary-237821/
* */SpellEffectEvent /* hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
*
*************************************************************************************
*
* Credits
*
* Nestharus - GUC
* Bribe - MissileRecycler, SpellEffectEvent
* D.O.G. - ZLibrary
* Adiktuz - MapBounds
*
*************************************************************************************/
globals
/*
* ability code
*/
private constant integer ABIL = 'giga'
/*
* Timeout iteration
*/
private constant real TIMEOUT = 0.03125
/*
* Damage values
*/
private constant real DAMAGE_BASE = 0
private constant real DAMAGE_PER_LEVEL = 100
/*
* attack type and damage type for damage
*/
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Heal potency (heal = damage*potency)
*/
private constant real HEAL_POTENCY_BASE = 0.4
private constant real HEAL_POTENCY_PER_LEVEL = 0.1
/*
* SFX applied to the target unit
*/
private constant string ON_CAST_TARGET_EFFECT = ""
private constant string ON_CAST_TARGET_ATTACH = ""
/*
* SFX applied to the unit when healed.
*/
private constant string ON_HEAL_EFFECT = ""
private constant string ON_HEAL_ATTACH = ""
/*
* SFX applied to the target when damaged.
*/
private constant string ON_DAMAGE_EFFECT = ""
private constant string ON_DAMAGE_ATTACH = ""
/*
* Release delay of particles
*/
private constant real PARTICLE_DELAY = 0.125
/*
* Particles per delay
*/
private constant integer PARTICLES_PER_DELAY = 4
/*
* Total number of particles released
*/
private constant integer PARTICLE_COUNT = 32
/*
* Model of particles
*/
private constant string PARTICLE_MODEL = "Abilities\\Spells\\Undead\\Possession\\PossessionTarget.mdl"
/*
* Bonus z applied to the unit(approximate height of the "chest"
*/
private constant real UNIT_Z_BONUS = 64
/*
* Time it takes for each particle to transform their appearances
*/
private constant real APPEARANCE_DURATION = 3.0
/*
* Particle sizes (newSize = random(start - variation, start + variation)
*/
private constant real START_SIZE = 1.5
private constant real START_SIZE_VARIATION = 0.5
private constant real END_SIZE = 0.25
private constant real END_SIZE_VARIATION = 0.125
private constant integer START_ALPHA = 255
private constant integer START_ALPHA_VARIATION = 0
private constant integer END_ALPHA = 255
private constant integer END_ALPHA_VARIATION = 0
private constant integer START_RED = 0
private constant integer START_RED_VARIATION = 0
private constant integer END_RED = 0
private constant integer END_RED_VARIATION = 0
private constant integer START_GREEN = 255
private constant integer START_GREEN_VARIATION = 0
private constant integer END_GREEN = 255
private constant integer END_GREEN_VARIATION = 0
private constant integer START_BLUE = 0
private constant integer START_BLUE_VARIATION = 0
private constant integer END_BLUE = 0
private constant integer END_BLUE_VARIATION = 0
/*
* Gravitational speed applied to each particles
* towards their target
*/
private constant real GRAVITY = 0.6129375
/*
* Angles of particles when released.
*/
private constant real ANGLE_START = bj_PI/2
private constant real ANGLE_VARIATION = bj_PI
private constant real ANGLE_Z_START = bj_PI/2
private constant real ANGLE_Z_VARIATION = bj_PI
/*
* How fast the particles travel(also influenced by gravity)
*/
private constant real OUTWARD_SPEED = 250
private constant real OUTWARD_SPEED_VARIATION = 125
endglobals
private function GetDamage takes integer lvl returns real
return DAMAGE_BASE + DAMAGE_PER_LEVEL*lvl
endfunction
private function GetParticleDamage takes integer lvl returns real
return GetDamage(lvl)/PARTICLE_COUNT
endfunction
private function GetHealPotency takes integer lvl returns real
return HEAL_POTENCY_BASE + HEAL_POTENCY_PER_LEVEL*lvl
endfunction
private function GetParticleHeal takes real damage, integer lvl returns real
return (damage*GetHealPotency(lvl))/PARTICLE_COUNT
endfunction
private function GetVarianceReal takes real base, real variation returns real
if variation == 0 then
return base
endif
return GetRandomReal(base - variation, base + variation)
endfunction
private function GetVarianceInt takes integer base, integer variation returns integer
if variation == 0 then
return base
endif
return GetRandomInt(base - variation, base + variation)
endfunction
private function DamageTarget takes unit s, unit t, real amount returns nothing
call UnitDamageTarget(s, t, amount, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endfunction
private function HealTarget takes unit s, real amount returns nothing
call SetWidgetLife(s, GetWidgetLife(s) + amount)
endfunction
private function SetPitch takes unit u, real pitch returns nothing
local integer i = R2I(pitch * 57.2957795 + 90.5)
if (179 < i) then
set i = 179
elseif (0 > i) then
set i = 0
endif
call SetUnitAnimationByIndex(u, i)
endfunction
private function Linear takes real a, real b, real t returns real
return a + (b - a)*t
endfunction
private function LinearI takes integer a, integer b, real t returns integer
return R2I(Linear(I2R(a), I2R(b), t))
endfunction
private struct Particles
// the particle
private unit u
// it's target and collision size of the target
private unit target
private real csize
// the speed and direction
private real speed
private real face
private real face3d
// the appearance duration
private real cdur
// the amount of heal
private real amount
private effect mdl
//start scale and argb
private real ss
private integer sa
private integer sr
private integer sg
private integer sb
//end scale and argb
private real es
private integer ea
private integer er
private integer eg
private integer eb
private static thistype array p
private static thistype array n
private static constant timer t = CreateTimer()
method destroy takes nothing returns nothing
call deallocate()
set n[p[this]] = n[this]
set p[n[this]] = p[this]
if n[0] == 0 then
call PauseTimer(t)
endif
call RecycleMissile(u)
call DestroyEffect(mdl)
set u = null
set target = null
set csize = 0
set speed = 0
set face = 0
set face3d = 0
set cdur = 0
set amount = 0
set mdl = null
set ss = 0
set sa = 0
set sr = 0
set sg = 0
set sb = 0
set es = 0
set ea = 0
set er = 0
set eg = 0
set eb = 0
endmethod
private static method pr takes nothing returns nothing
local thistype this = n[0]
local real angle2
local real angle3
local real px
local real py
local real ptz
local real pz
local real dx
local real dy
local real dtz
local real dz
local real nx
local real ny
local real nz
local real dist2d
local real dist3d
local real pct
loop
exitwhen 0 == this
/*
* Check if target is alive
*/
if UnitAlive(target) then
/*
* if true, initialize coordinates for both the target and the particle
*/
set px = GetUnitX(u)
set py = GetUnitY(u)
set ptz = GetSurfaceZ(px, py)
set pz = GetUnitFlyHeight(u) + ptz
set dx = GetUnitX(target)
set dy = GetUnitY(target)
set dtz = GetSurfaceZ(dx, dy)
set dz = GetUnitFlyHeight(target) + dtz + UNIT_Z_BONUS
/*
* Get the 2d and 3d distances
*/
set dist2d = GetMagnitude2D(dx - px, dy - py)
set dist3d = GetMagnitude3D(dx - px, dy - py, dz - pz)
/*
* Check if particle is within the spherical collision
*/
if dist3d <= csize then
/*
* If true, heal the target then destroy the particle
*/
call HealTarget(target, amount)
call DestroyEffect(AddSpecialEffectTarget(ON_HEAL_EFFECT, target, ON_HEAL_ATTACH))
call destroy()
else
/*
* if not, get the angle towards the target
*/
set angle2 = GetAngle2D(dx - px, dy - py)
set angle3 = GetAngle3D(dist2d, dz - pz)
/*
* Get the next coordinates for the unit(w/o gravity)
*/
set nx = px + speed*Cos(face)*Cos(face3d)
set ny = py + speed*Sin(face)*Cos(face3d)
set nz = pz + speed*Sin(face3d)
/*
* Apply gravity for the new coordinates
*/
set nx = nx + GRAVITY*Cos(angle2)*Cos(angle3)
set ny = ny + GRAVITY*Sin(angle2)*Cos(angle3)
set nz = nz + GRAVITY*Sin(angle3)
/*
* Apply new facing
*/
set face = GetAngle2D(nx - px, ny - py)
set face3d = GetAngle3D(GetMagnitude2D(nx - px, ny - py), nz - pz)
/*
* Move the particle to the coordinates, bounded.
*/
call SetUnitX(u, GetBoundedX(nx))
call SetUnitY(u, GetBoundedY(ny))
call SetUnitFlyHeight(u, nz - GetSurfaceZ(nx, ny), 0)
/*
* Apply pitch and facing
*/
call SetUnitFacing(u, face*bj_RADTODEG)
call SetPitch(u, face3d)
/*
* Check if the appearance timer has not yet ended.
*/
if cdur < APPEARANCE_DURATION then
/*
* If true, get the percentage of the progress
*/
set cdur = cdur + TIMEOUT
set pct = cdur/APPEARANCE_DURATION
/*
* Apply linear calculation to get the new appearance values
*/
call SetUnitScale(u, Linear(ss, es, pct), 0, 0)
call SetUnitVertexColor(u, LinearI(sr, er, pct), LinearI(sg, eg, pct), LinearI(sb, eb, pct), LinearI(sa, ea, pct))
endif
endif
else
/*
* Destroy the particle
*/
call destroy()
endif
set this = n[this]
endloop
endmethod
static method create takes unit source, unit victim, integer level returns thistype
local thistype this = allocate()
/*
* Get the coordinates of the victim
*/
local real x = GetUnitX(victim)
local real y = GetUnitY(victim)
local real z = GetUnitZ(victim)
/*
* Get the damage
*/
local real damage = GetParticleDamage(level)
/*
* get the facing
*/
set face = GetVarianceReal(ANGLE_START, ANGLE_VARIATION)
set face3d = GetVarianceReal(ANGLE_Z_START, ANGLE_Z_VARIATION)
/*
* create the particles
*/
set u = GetRecycledMissile(x, y, z, face*bj_RADTODEG)
call SetPitch(u, face3d)
/*
* Add the model to it.
*/
set mdl = AddSpecialEffectTarget(PARTICLE_MODEL, u, "origin")
/*
* get the appearance values
*/
//! textmacro GD_APPEAR takes att, ATT
set s$att$ = GetVarianceInt(START_$ATT$, START_$ATT$_VARIATION)
set e$att$ = GetVarianceInt(END_$ATT$, END_$ATT$_VARIATION)
//! endtextmacro
//! runtextmacro GD_APPEAR("a", "ALPHA")
//! runtextmacro GD_APPEAR("r", "RED")
//! runtextmacro GD_APPEAR("g", "GREEN")
//! runtextmacro GD_APPEAR("b", "BLUE")
set ss = GetVarianceReal(START_SIZE, START_SIZE_VARIATION)
set es = GetVarianceReal(END_SIZE, END_SIZE_VARIATION)
/*
* Apply the appearances
*/
call SetUnitScale(u, ss, 0, 0)
call SetUnitVertexColor(u, sr, sg, sb, sa)
/*
* Get the speed of the particle
*/
set speed = GetVarianceReal(OUTWARD_SPEED, OUTWARD_SPEED_VARIATION)*TIMEOUT
/*
* Damage the target
*/
call DamageTarget(source, victim, damage)
/*
* Get the heal amount
*/
set amount = GetParticleHeal(damage, level)
/*
* get the target's collision size
*/
set target = source
set csize = GetUnitCollision(target)
set n[this] = 0
set p[this] = p[0]
set n[p[0]] = this
set p[0] = this
if p[this] == 0 then
call TimerStart(t, TIMEOUT, true, function thistype.pr)
endif
return this
endmethod
endstruct
private struct GigaDrain
private integer count
private real spawnDelay
private unit target
private unit source
private integer level
private static thistype array p
private static thistype array n
private static constant timer t = CreateTimer()
private method destroy takes nothing returns nothing
call deallocate()
set n[p[this]] = n[this]
set p[n[this]] = p[this]
if n[0] == 0 then
call PauseTimer(t)
endif
set spawnDelay = 0
set count = 0
set level = 0
set source = null
set target = null
endmethod
private static method pr takes nothing returns nothing
local thistype this = n[0]
local integer i = 0
loop
exitwhen 0 == this
/*
* Check if the count has not yet reached the max count an
* if both the source and the target is still alive
*/
if count < PARTICLE_COUNT and UnitAlive(target) and UnitAlive(source) then
/*
* Check if spawn delay has finished
*/
if spawnDelay > 0 then
set spawnDelay = spawnDelay - TIMEOUT
else
/*
* if true, release the particles
*/
loop
set i = i + 1
set count = count + 1
call Particles.create(source, target, level)
exitwhen i == PARTICLES_PER_DELAY or count >= PARTICLE_COUNT
endloop
set i = 0
/*
* reset the spawn delay timer
*/
set spawnDelay = PARTICLE_DELAY
call DestroyEffect(AddSpecialEffectTarget(ON_DAMAGE_EFFECT, target, ON_DAMAGE_ATTACH))
endif
else
call destroy()
endif
set this = n[this]
endloop
endmethod
private static method onCast takes nothing returns boolean
local thistype this
local integer i = 0
local unit s = GetTriggerUnit()
local unit tg = GetSpellTargetUnit()
local integer lvl = GetUnitAbilityLevel(s, ABIL)
if PARTICLE_DELAY < TIMEOUT then
loop
set i = i + 1
call Particles.create(s, tg, lvl)
exitwhen i == PARTICLE_COUNT
endloop
else
set this = allocate()
set spawnDelay = PARTICLE_DELAY
set source = s
set target = tg
set level = lvl
set count = 0
set n[this] = 0
set p[this] = p[0]
set n[p[0]] = this
set p[0] = this
if p[this] == 0 then
call TimerStart(t, TIMEOUT, true, function thistype.pr)
endif
endif
call DestroyEffect(AddSpecialEffectTarget(ON_CAST_TARGET_EFFECT, tg, ON_CAST_TARGET_ATTACH))
set s = null
set tg = null
return false
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(ABIL, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
struct Test extends array
private static method onInit takes nothing returns nothing
local unit u = gg_unit_Hblm_0001
call FogEnable(false)
call FogMaskEnable(false)
call SetHeroLevel(u, 10, false)
set u = null
endmethod
endstruct