Name | Type | is_array | initial_value |
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
library TableBC requires Table
/*
Backwards-compatibility add-on for scripts employing Vexorian's Table.
Added 31 July 2015: introduced static method operator [] and
static method flush2D for Table, HandleTable and StringTable. Now,
almost all of the Vexorian API has been replicated (minus the .flush paradox).
The Table library itself was unchanged to implement these
enhancements, so you need only update this library to experience the
improved syntax compatibility.
Disclaimer:
The following error does not occur with HandleTables & StringTables, only
with the standard, integer-based Table, so you do not need to make any
changes to StringTable/HandleTable-employing scripts.
The this.flush(key) method from the original Table cannot be parsed with
the new Table. For the scripts that use this method, they need to be up-
dated to use the more fitting this.remove(key) method.
Please don't try using StringTables/HandleTables with features exclusive
to the new Table as they will cause syntax errors. I do not have any plan
to endorse these types of Tables because delegation in JassHelper is not
advanced enough for three types of Tables without copying every single
method over again (as you can see this already generates plenty of code).
StringTable & HandleTable are wrappers for StringHash & GetHandleId, so
just type them out.
*/
//! textmacro TABLE_BC_METHODS
method reset takes nothing returns nothing
call this.flush()
endmethod
method exists takes integer key returns boolean
return this.has(key)
endmethod
static method operator [] takes string id returns Table
local integer index = StringHash(id)
local Table t = Table(thistype.typeid)[index]
if t == 0 then
set t = Table.create()
set Table(thistype.typeid)[index] = t
endif
return t
endmethod
static method flush2D takes string id returns nothing
local integer index = StringHash(id)
local Table t = Table(thistype.typeid)[index]
if t != 0 then
call t.destroy()
call Table(thistype.typeid).remove(index)
endif
endmethod
//! endtextmacro
//! textmacro TABLE_BC_STRUCTS
struct HandleTable extends array
static method operator [] takes string index returns thistype
return Table[index]
endmethod
static method flush2D takes string index returns nothing
call Table.flush2D(index)
endmethod
method operator [] takes handle key returns integer
return Table(this)[GetHandleId(key)]
endmethod
method operator []= takes handle key, integer value returns nothing
set Table(this)[GetHandleId(key)] = value
endmethod
method flush takes handle key returns nothing
call Table(this).remove(GetHandleId(key))
endmethod
method exists takes handle key returns boolean
return Table(this).has(GetHandleId(key))
endmethod
method reset takes nothing returns nothing
call Table(this).flush()
endmethod
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
struct StringTable extends array
static method operator [] takes string index returns thistype
return Table[index]
endmethod
static method flush2D takes string index returns nothing
call Table.flush2D(index)
endmethod
method operator [] takes string key returns integer
return Table(this)[StringHash(key)]
endmethod
method operator []= takes string key, integer value returns nothing
set Table(this)[StringHash(key)] = value
endmethod
method flush takes string key returns nothing
call Table(this).remove(StringHash(key))
endmethod
method exists takes string key returns boolean
return Table(this).has(StringHash(key))
endmethod
method reset takes nothing returns nothing
call Table(this).flush()
endmethod
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
//! endtextmacro
endlibrary
//TESH.scrollpos=0
//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=29
//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
library dead
//returns true if the unit is alive. requires JassHelper
// to declare the AI native "UnitAlive" as a JASS native, so that
// we can use it in the trigger editor. If you trigger in vJASS,
// you should DEFINITELY use this since it is the only way to be
// sure that a unit is alive.
native UnitAlive takes unit id returns boolean
//returns true if the unit is dead. inline friendly.
function UnitDead takes unit u returns boolean
return not UnitAlive(u)
endfunction
//returns true if the unit exists in the game. inline friendly.
function DoesUnitExist takes unit u returns boolean
return GetUnitTypeId(u) != 0
endfunction
endlibrary
//TESH.scrollpos=21
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Alloc ~~ By Sevion ~~ Version 1.09 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Alloc?
// - Alloc implements an intuitive allocation method for array structs
//
// =Pros=
// - Efficient.
// - Simple.
// - Less overhead than regular structs.
//
// =Cons=
// - Must use array structs (hardly a con).
// - Must manually call OnDestroy.
// - Must use Delegates for inheritance.
// - No default values for variables (use onInit instead).
// - No array members (use another Alloc struct as a linked list or type declaration).
//
// Methods:
// - struct.allocate()
// - struct.deallocate()
//
// These methods are used just as they should be used in regular structs.
//
// Modules:
// - Alloc
// Implements the most basic form of Alloc. Includes only create and destroy
// methods.
//
// Details:
// - Less overhead than regular structs
//
// - Use array structs when using Alloc. Put the implement at the top of the struct.
//
// - Alloc operates almost exactly the same as default structs in debug mode with the exception of onDestroy.
//
// How to import:
// - Create a trigger named Alloc.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Nestharus for the method of allocation and suggestions on further merging.
// - Bribe for suggestions like the static if and method names.
// - PurgeandFire111 for some suggestions like the merging of Alloc and AllocX as well as OnDestroy stuff.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Alloc
module Alloc
private static integer instanceCount = 0
private thistype recycle
static method allocate takes nothing returns thistype
local thistype this
if (thistype(0).recycle == 0) then
debug if (instanceCount == JASS_MAX_ARRAY_SIZE) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to allocate too many instances!")
debug return 0
debug endif
set instanceCount = instanceCount + 1
set this = instanceCount
else
set this = thistype(0).recycle
set thistype(0).recycle = thistype(0).recycle.recycle
endif
debug set this.recycle = -1
return this
endmethod
method deallocate takes nothing returns nothing
debug if (this.recycle != -1) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to deallocate an invalid instance at [" + I2S(this) + "]!")
debug return
debug endif
set this.recycle = thistype(0).recycle
set thistype(0).recycle = this
endmethod
endmodule
endlibrary
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 playMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set playMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set playMinX = GetRectMinX(bj_mapInitialPlayableArea)
set playMinY = GetRectMinY(bj_mapInitialPlayableArea)
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
readonly static real playMaxX
readonly static real playMaxY
readonly static real playMinX
readonly static real playMinY
implement WorldBoundInit
endstruct
endlibrary
library MissileEffect requires WorldBounds, Alloc
/* ------------------------------------- Missile Effect v2.8 ------------------------------------ */
// This is a simple helper library for the Relativistic Missiles system.
// Credits:
// Sevion for the Alloc module
// - www.hiveworkshop.com/threads/snippet-alloc.192348/
// Nestharus for World Bounds Library
/* ---------------------------------------- By Chopinski ---------------------------------------- */
/* ---------------------------------------------------------------------------------------------- */
/* System */
/* ---------------------------------------------------------------------------------------------- */
private module LinkedList
readonly thistype next
readonly thistype prev
method init takes nothing returns thistype
set next = this
set prev = this
return this
endmethod
method pushBack takes thistype node returns thistype
set node.prev = prev
set node.next = this
set prev.next = node
set prev = node
return node
endmethod
method pushFront takes thistype node returns thistype
set node.prev = this
set node.next = next
set next.prev = node
set next = node
return node
endmethod
method pop takes nothing returns nothing
set prev.next = next
set next.prev = prev
endmethod
endmodule
private struct Effect extends array
implement LinkedList
implement Alloc
real x
real y
real z
real size
real yaw
real pitch
real roll
string path
effect effect
method remove takes nothing returns nothing
call DestroyEffect(effect)
call pop()
call deallocate()
set effect = null
endmethod
method insert takes string fxpath, real x, real y, real z, real scale returns thistype
local thistype node = pushBack(allocate())
set node.x = x
set node.y = y
set node.z = z
set node.yaw = 0.
set node.pitch = 0.
set node.roll = 0.
set node.path = fxpath
set node.size = scale
set node.effect = AddSpecialEffect(fxpath, x, y)
call BlzSetSpecialEffectZ(node.effect, z)
call BlzSetSpecialEffectScale(node.effect, scale)
return node
endmethod
static method create takes nothing returns thistype
return thistype(allocate()).init()
endmethod
endstruct
struct MissileEffect
real size
real yaw
real pitch
real roll
real time
integer transparency
integer animtype
integer playercolor
string path
effect effect
Effect attachments
/* -------------------------------- Operators ------------------------------- */
method operator timeScale= takes real newTimeScale returns nothing
set time = newTimeScale
call BlzSetSpecialEffectTimeScale(effect, time)
endmethod
method operator timeScale takes nothing returns real
return time
endmethod
method operator alpha= takes integer newAlpha returns nothing
set transparency = newAlpha
call BlzSetSpecialEffectAlpha(effect, transparency)
endmethod
method operator alpha takes nothing returns integer
return transparency
endmethod
method operator playerColor= takes integer playerId returns nothing
set playercolor = playerId
call BlzSetSpecialEffectColorByPlayer(effect, Player(playerId))
endmethod
method operator playerColor takes nothing returns integer
return playercolor
endmethod
method operator animation= takes integer animType returns nothing
set animtype = animType
call BlzPlaySpecialEffect(effect, ConvertAnimType(animtype))
endmethod
method operator animation takes nothing returns integer
return animtype
endmethod
/* --------------------------------- Methods -------------------------------- */
method scale takes effect sfx, real scale returns nothing
set size = scale
call BlzSetSpecialEffectScale(sfx, scale)
endmethod
method orient takes real yaw, real pitch, real roll returns nothing
local Effect node = attachments.next
set .yaw = yaw
set .pitch = pitch
set .roll = roll
call BlzSetSpecialEffectOrientation(effect, yaw, pitch, roll)
loop
exitwhen node == attachments
set node.yaw = yaw
set node.pitch = pitch
set node.roll = roll
call BlzSetSpecialEffectOrientation(node.effect, yaw, pitch, roll)
set node = node.next
endloop
endmethod
method move takes real x, real y, real z returns boolean
local Effect node = attachments.next
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call BlzSetSpecialEffectPosition(effect, x, y, z)
loop
exitwhen node == attachments
call BlzSetSpecialEffectPosition(node.effect, x - node.x, y - node.y, z - node.z)
set node = node.next
endloop
return true
endif
return false
endmethod
method attach takes string fxpath, real dx, real dy, real dz, real scale returns effect
local Effect node = attachments.insert(fxpath, dx, dy, dz, scale)
call BlzSetSpecialEffectPosition(node.effect, BlzGetLocalSpecialEffectX(effect) - dx, BlzGetLocalSpecialEffectY(effect) - dy, BlzGetLocalSpecialEffectZ(effect) - dz)
return node.effect
endmethod
method detach takes effect sfx returns nothing
local Effect node = attachments.next
loop
exitwhen node == attachments
if GetHandleId(node.effect) == GetHandleId(sfx) then
call node.remove()
exitwhen true
endif
set node = node.next
endloop
endmethod
method setColor takes integer red, integer green, integer blue returns nothing
call BlzSetSpecialEffectColor(effect, red, green, blue)
endmethod
/* -------------------------- Contructor/Destructor ------------------------- */
method destroy takes nothing returns nothing
local Effect node = attachments.next
loop
exitwhen node == attachments
call node.remove()
set node = node.next
endloop
call DestroyEffect(effect)
call attachments.deallocate()
set effect = null
set path = null
set size = 1.
call deallocate()
endmethod
static method create takes real x, real y, real z returns thistype
local thistype this = thistype.allocate()
set effect = AddSpecialEffect("", x, y)
set path = ""
set size = 1
set time = 0
set transparency = 0
set animtype = 0
set playercolor = 0
set attachments = Effect.create()
call BlzSetSpecialEffectZ(effect, z)
return this
endmethod
endstruct
endlibrary
library MissileUtils requires Missiles, Alloc
/* ------------------------------------- Missile Utils v2.8 ------------------------------------- */
// This is a simple Utils library for the Relativistic Missiles system.
// Credits:
// Sevion for the Alloc module
// - www.hiveworkshop.com/threads/snippet-alloc.192348/
/* ---------------------------------------- By Chopinski ---------------------------------------- */
/* ---------------------------------------------------------------------------------------------- */
/* JASS API */
/* ---------------------------------------------------------------------------------------------- */
function CreateMissileGroup takes nothing returns MissileGroup
return MissileGroup.create()
endfunction
function DestroyMissileGroup takes MissileGroup missiles returns nothing
if missiles != 0 then
call missiles.clear()
call missiles.destroy()
endif
endfunction
function MissileGroupGetSize takes MissileGroup missiles returns integer
if missiles != 0 then
return missiles.size
else
return 0
endif
endfunction
function GroupMissileAt takes MissileGroup missiles, integer position returns Missiles
if missiles != 0 then
return missiles.missileAt(position)
else
return 0
endif
endfunction
function ClearMissileGroup takes MissileGroup missiles returns nothing
if missiles != 0 then
call missiles.clear()
endif
endfunction
function IsMissileInGroup takes Missiles missile, MissileGroup missiles returns boolean
if missiles != 0 and missile != 0 then
if missiles.size > 0 then
return missiles.contains(missile)
else
return false
endif
else
return false
endif
endfunction
function GroupRemoveMissile takes MissileGroup missiles, Missiles missile returns nothing
if missiles != 0 and missile != 0 then
if missiles.size > 0 then
call missiles.remove(missile)
endif
endif
endfunction
function GroupAddMissile takes MissileGroup missiles, Missiles missile returns nothing
if missiles != 0 and missile != 0 then
if not missiles.contains(missile) then
call missiles.insert(missile)
endif
endif
endfunction
function GroupPickRandomMissile takes MissileGroup missiles returns Missiles
if missiles != 0 then
if missiles.size > 0 then
return missiles.missileAt(GetRandomInt(0, missiles.size - 1))
else
return 0
endif
else
return 0
endif
endfunction
function FirstOfMissileGroup takes MissileGroup missiles returns Missiles
if missiles != 0 then
if missiles.size > 0 then
return missiles.group.next.missile
else
return 0
endif
else
return 0
endif
endfunction
function GroupAddMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
if source != 0 and destiny != 0 then
if source.size > 0 and source != destiny then
call destiny.addGroup(source)
endif
endif
endfunction
function GroupRemoveMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
if source != 0 and destiny != 0 then
if source == destiny then
call source.clear()
elseif source.size > 0 then
call destiny.removeGroup(source)
endif
endif
endfunction
function GroupEnumMissilesOfType takes MissileGroup missiles, integer whichType returns nothing
local integer i
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if missile.type == whichType then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfTypeCounted takes MissileGroup missiles, integer whichType, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if missile.type == whichType then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfPlayer takes MissileGroup missiles, player p returns nothing
local integer i
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if missile.owner == p then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfPlayerCounted takes MissileGroup missiles, player p, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if missile.owner == p then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRect takes MissileGroup missiles, rect r returns nothing
local integer i
local Missiles missile
if missiles != 0 and r != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if GetRectMinX(r) <= missile.x and missile.x <= GetRectMaxX(r) and GetRectMinY(r) <= missile.y and missile.y <= GetRectMaxY(r) then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRectCounted takes MissileGroup missiles, rect r, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and r != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if GetRectMinX(r) <= missile.x and missile.x <= GetRectMaxX(r) and GetRectMinY(r) <= missile.y and missile.y <= GetRectMaxY(r) then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeOfLoc takes MissileGroup missiles, location loc, real radius returns nothing
local real dx
local real dy
local integer i
local Missiles missile
if missiles != 0 and radius > 0 and loc != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
set dx = missile.x - GetLocationX(loc)
set dy = missile.y - GetLocationY(loc)
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeOfLocCounted takes MissileGroup missiles, location loc, real radius, integer amount returns nothing
local real dx
local real dy
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and radius > 0 and loc != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
set dx = missile.x - GetLocationX(loc)
set dy = missile.y - GetLocationY(loc)
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRange takes MissileGroup missiles, real x, real y, real radius returns nothing
local real dx
local real dy
local integer i
local Missiles missile
if missiles != 0 and radius > 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeCounted takes MissileGroup missiles, real x, real y, real radius, integer amount returns nothing
local real dx
local real dy
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and radius > 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
/* ---------------------------------------------------------------------------------------------- */
/* System */
/* ---------------------------------------------------------------------------------------------- */
private module LinkedList
readonly thistype next
readonly thistype prev
method init takes nothing returns thistype
set next = this
set prev = this
return this
endmethod
method pushBack takes thistype node returns thistype
set node.prev = prev
set node.next = this
set prev.next = node
set prev = node
return node
endmethod
method pushFront takes thistype node returns thistype
set node.prev = this
set node.next = next
set next.prev = node
set next = node
return node
endmethod
method pop takes nothing returns nothing
set prev.next = next
set next.prev = prev
endmethod
endmodule
private struct MGroup extends array
implement LinkedList
implement Alloc
Missiles missile
method remove takes nothing returns nothing
call pop()
call deallocate()
endmethod
method insert takes Missiles m returns thistype
local thistype node = pushBack(allocate())
set node.missile = m
return node
endmethod
static method create takes nothing returns thistype
return thistype(allocate()).init()
endmethod
endstruct
struct MissileGroup
MGroup group
integer size
method destroy takes nothing returns nothing
call group.deallocate()
call deallocate()
endmethod
method missileAt takes integer i returns Missiles
local MGroup node = group.next
local integer j = 0
if size > 0 and i <= size - 1 then
loop
exitwhen j == i
set node = node.next
set j = j + 1
endloop
return node.missile
else
return 0
endif
endmethod
method remove takes Missiles missile returns nothing
local MGroup node = group.next
loop
exitwhen node == group
if node.missile == missile then
set size = size - 1
call node.remove()
exitwhen true
endif
set node = node.next
endloop
endmethod
method insert takes Missiles missile returns nothing
set size = size + 1
call group.insert(missile)
endmethod
method clear takes nothing returns nothing
local MGroup node = group.next
loop
exitwhen node == group
call node.remove()
set node = node.next
endloop
set size = 0
endmethod
method contains takes Missiles missile returns boolean
local MGroup node = group.next
local boolean found = false
loop
exitwhen node == group
if node.missile == missile then
set found = true
exitwhen true
endif
set node = node.next
endloop
return found
endmethod
method addGroup takes MissileGroup source returns nothing
local MGroup node = source.group.next
loop
exitwhen node == source.group
if not contains(node.missile) then
call insert(node.missile)
endif
set node = node.next
endloop
endmethod
method removeGroup takes MissileGroup source returns nothing
local MGroup node = source.group.next
loop
exitwhen node == source.group
if contains(node.missile) then
call remove(node.missile)
endif
set node = node.next
endloop
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set group = MGroup.create()
set size = 0
return this
endmethod
endstruct
endlibrary
library Missiles requires MissileEffect, TimerUtils, WorldBounds
/* ---------------------------------------- Missiles v2.8 --------------------------------------- */
// Thanks and Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
// this Missiles library. Credits and thanks to AGD and for the effect orientation ideas.
// This version of Missiles requires patch 1.31+
// How to Import:
// 1 - Copy this, MissileEffect and optionaly the MissileUtils libraries to your map
/* ---------------------------------------- By Chopinski ---------------------------------------- */
/* ---------------------------------------------------------------------------------------------- */
/* System */
/* ---------------------------------------------------------------------------------------------- */
globals
// The update period of the system
public constant real PERIOD = 1./40.
// The max amount of Missiles processed in a PERIOD
// You can play around with both these values to find
// your sweet spot. If equal to 0, the system will
// process all missiles at once every period.
public constant real SWEET_SPOT = 150
// the avarage collision size compensation when detecting collisions
private constant real COLLISION_SIZE = 128.
// item size used in z collision
private constant real ITEM_SIZE = 16.
// Raw code of the dummy unit used for vision
private constant integer DUMMY = 'dumi'
// Needed, don't touch.
private location LOC = Location(0., 0.)
endglobals
private interface MissileEvents
method onHit takes unit hit returns boolean defaults false
method onMissile takes Missiles missile returns boolean defaults false
method onDestructable takes destructable dest returns boolean defaults false
method onItem takes item i returns boolean defaults false
method onCliff takes nothing returns boolean defaults false
method onTerrain takes nothing returns boolean defaults false
method onTileset takes integer tileset returns boolean defaults false
method onPeriod takes nothing returns boolean defaults false
method onFinish takes nothing returns boolean defaults false
method onBoundaries takes nothing returns boolean defaults false
method onPause takes nothing returns boolean defaults false
method onResume takes nothing returns boolean defaults false
method onRemove takes nothing returns nothing defaults nothing
endinterface
private function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
private function GetUnitZ takes unit u returns real
return GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
endfunction
private function SetUnitZ takes unit u, real z returns nothing
call SetUnitFlyHeight(u, z - GetLocZ(GetUnitX(u), GetUnitY(u)), 0)
endfunction
private function GetMapCliffLevel takes nothing returns integer
return GetTerrainCliffLevel(WorldBounds.maxX, WorldBounds.maxY)
endfunction
private struct Pool
private static player player = Player(PLAYER_NEUTRAL_PASSIVE)
private static group group = CreateGroup()
timer timer
unit unit
static method recycle takes unit dummy returns nothing
if GetUnitTypeId(dummy) == DUMMY then
call GroupAddUnit(group, dummy)
call SetUnitX(dummy, WorldBounds.maxX)
call SetUnitY(dummy, WorldBounds.maxY)
call SetUnitOwner(dummy, player, false)
call PauseUnit(dummy, true)
endif
endmethod
static method retrieve takes real x, real y, real z, real face returns unit
if BlzGroupGetSize(group) > 0 then
set bj_lastCreatedUnit = FirstOfGroup(group)
call PauseUnit(bj_lastCreatedUnit, false)
call GroupRemoveUnit(group, bj_lastCreatedUnit)
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitZ(bj_lastCreatedUnit, z)
call BlzSetUnitFacingEx(bj_lastCreatedUnit, face)
else
set bj_lastCreatedUnit = CreateUnit(player, DUMMY, x, y, face)
call SetUnitZ(bj_lastCreatedUnit, z)
call UnitRemoveAbility(bj_lastCreatedUnit, 'Amrf')
endif
return bj_lastCreatedUnit
endmethod
private static method onExpire takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call recycle(unit)
call ReleaseTimer(timer)
set timer = null
set unit = null
call deallocate()
endmethod
static method recycleTimed takes unit dummy, real delay returns nothing
local thistype this
if GetUnitTypeId(dummy) != DUMMY then
debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
else
set this = thistype.allocate()
set timer = NewTimerEx(this)
set unit = dummy
call TimerStart(timer, delay, false, function thistype.onExpire)
endif
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
local unit u
loop
exitwhen i == SWEET_SPOT
set u = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
call PauseUnit(u, false)
call GroupAddUnit(group, u)
call UnitRemoveAbility(u, 'Amrf')
set i = i + 1
endloop
set u = null
endmethod
endstruct
private struct Coordinates
readonly real x
readonly real y
readonly real z
readonly real angle
readonly real distance
readonly real square
readonly real slope
readonly real alpha
// Creates an origin - impact link.
private thistype ref
private static method math takes thistype a, thistype b returns nothing
local real dx
local real dy
loop
set dx = b.x - a.x
set dy = b.y - a.y
set dx = dx*dx + dy*dy
set dy = SquareRoot(dx)
exitwhen dx != 0. and dy != 0.
set b.x = b.x + .01
set b.z = b.z - GetLocZ(b.x -.01, b.y) + GetLocZ(b.x, b.y)
endloop
set a.square = dx
set a.distance = dy
set a.angle = Atan2(b.y - a.y, b.x - a.x)
set a.slope = (b.z - a.z)/dy
set a.alpha = Atan(a.slope)
// Set b.
if b.ref == a then
set b.angle = a.angle + bj_PI
set b.distance = dy
set b.slope = -a.slope
set b.alpha = -a.alpha
set b.square = dx
endif
endmethod
static method link takes thistype a, thistype b returns nothing
set a.ref = b
set b.ref = a
call math(a, b)
endmethod
method move takes real toX, real toY, real toZ returns nothing
set x = toX
set y = toY
set z = toZ + GetLocZ(toX, toY)
if ref != this then
call math(this, ref)
endif
endmethod
method destroy takes nothing returns nothing
call .deallocate()
endmethod
static method create takes real x, real y, real z returns Coordinates
local thistype this = thistype.allocate()
set ref = this
call move(x, y, z)
return this
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private module OnHit
set o = origin
set h = height
set c = open
set d = o.distance
if .onHit.exists then
if allocated and collision > 0 then
call GroupEnumUnitsInRange(group, x, y, collision + COLLISION_SIZE, null)
loop
set u = FirstOfGroup(group)
exitwhen u == null
if not HaveSavedBoolean(table, this, GetHandleId(u)) then
if IsUnitInRangeXY(u, x, y, collision) then
if collideZ then
set dx = GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
set dy = BlzGetUnitCollisionSize(u)
if dx + dy >= z - collision and dx <= z + collision then
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
else
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
endif
endif
call GroupRemoveUnit(group, u)
endloop
endif
endif
endmodule
private module OnMissile
if .onMissile.exists then
if allocated and collision > 0 then
set k = 0
loop
exitwhen k > count
set missile = collection[k]
if missile != this then
if not HaveSavedBoolean(table, this, missile) then
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= collision then
call SaveBoolean(table, this, missile, true)
if allocated and .onMissile(missile) then
call terminate()
exitwhen true
endif
endif
endif
endif
set k = k + 1
endloop
endif
endif
endmodule
private module OnDestructable
if .onDestructable.exists then
if allocated and collision > 0 then
set dx = collision
call SetRect(rect, x - dx, y - dx, x + dx, y + dx)
call EnumDestructablesInRect(rect, null, function thistype.onDest)
endif
endif
endmodule
private module OnItem
if .onItem.exists then
if allocated and collision > 0 then
set dx = collision
call SetRect(rect, x - dx, y - dx, x + dx, y + dx)
call EnumItemsInRect(rect, null, function thistype.onItems)
endif
endif
endmodule
private module OnCliff
if .onCliff.exists then
set dx = GetTerrainCliffLevel(nextX, nextY)
set dy = GetTerrainCliffLevel(x, y)
if dy < dx and z < (dx - GetMapCliffLevel())*bj_CLIFFHEIGHT then
if allocated and .onCliff() then
call terminate()
endif
endif
endif
endmodule
private module OnTerrain
if .onTerrain.exists then
if GetLocZ(x, y) > z then
if allocated and .onTerrain() then
call terminate()
endif
endif
endif
endmodule
private module OnTileset
if .onTileset.exists then
set k = GetTerrainType(x, y)
if k != tileset then
if allocated and .onTileset(k) then
call terminate()
endif
endif
set tileset = k
endif
endmodule
private module OnPeriod
if .onPeriod.exists then
if allocated and .onPeriod() then
call terminate()
endif
endif
endmodule
private module OnOrient
// Homing or not
set u = target
if u != null and GetUnitTypeId(u) != 0 then
call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + toZ)
set dx = impact.x - nextX
set dy = impact.y - nextY
set a = Atan2(dy, dx)
set travel = o.distance - SquareRoot(dx*dx + dy*dy)
else
set a = o.angle
set target = null
endif
// turn rate
if turn != 0 and not (Cos(cA-a) >= Cos(turn)) then
if Sin(a-cA) >= 0 then
set cA = cA + turn
else
set cA = cA - turn
endif
else
set cA = a
endif
set vel = veloc*dilation
set yaw = cA
set s = travel + vel
set veloc = veloc + acceleration
set travel = s
set pitch = o.alpha
set prevX = x
set prevY = y
set prevZ = z
set x = nextX
set y = nextY
set z = nextZ
set nextX = x + vel*Cos(yaw)
set nextY = y + vel*Sin(yaw)
// arc calculation
if h != 0 or o.slope != 0 then
set nextZ = 4*h*s*(d-s)/(d*d) + o.slope*s + o.z
set pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))
endif
// curve calculation
if c != 0 then
set dx = 4*c*s*(d-s)/(d*d)
set a = yaw + bj_PI/2
set x = x + dx*Cos(a)
set y = y + dx*Sin(a)
set yaw = yaw + Atan(-((4*c)*(2*s - d))/(d*d))
endif
endmodule
private module OnFinish
if s >= d - 0.0001 then
set finished = true
if .onFinish.exists then
if allocated and .onFinish() then
call terminate()
else
if travel > 0 and not paused then
call terminate()
endif
endif
else
call terminate()
endif
else
if not roll then
call effect.orient(yaw, -pitch, 0)
else
call effect.orient(yaw, -pitch, Atan2(c, h))
endif
endif
endmodule
private module OnBoundaries
if not effect.move(x, y, z) then
if .onBoundaries.exists then
if allocated and .onBoundaries() then
call terminate()
endif
endif
else
if dummy != null then
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
endif
endif
endmodule
private module OnPause
set pid = pid + 1
set pkey = pid
set frozen[pid] = this
if .onPause.exists then
if allocated and .onPause() then
call terminate()
endif
endif
endmodule
private module OnResume
local thistype aux
set paused = flag
if not paused and pkey != -1 then
set id = id + 1
set missiles[id] = this
set aux = frozen[pid]
set aux.pkey = pkey
set frozen[pkey] = frozen[pid]
set pid = pid - 1
set pkey = -1
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if id == 0 then
call TimerStart(timer, PERIOD, true, function thistype.move)
endif
if .onResume.exists then
if allocated and .onResume() then
call terminate()
else
if finished then
call terminate()
endif
endif
else
if finished then
call terminate()
endif
endif
endif
endmodule
private module OnRemove
local thistype aux
if allocated and launched then
set allocated = false
if pkey != -1 then
set aux = frozen[pid]
set aux.pkey = pkey
set frozen[pkey] = frozen[pid]
set pid = pid - 1
set pkey = -1
endif
if .onRemove.exists then
call .onRemove()
endif
if dummy != null then
call Pool.recycle(dummy)
endif
set aux = collection[count]
set aux.index = index
set collection[index] = collection[count]
set count = count - 1
set index = -1
call origin.destroy()
call impact.destroy()
call effect.destroy()
call reset()
call FlushChildHashtable(table, this)
endif
endmodule
private module Operators
/* -------------------------- Model of the missile -------------------------- */
method operator model= takes string fx returns nothing
call DestroyEffect(effect.effect)
set effect.path = fx
set effect.effect = AddSpecialEffect(fx, origin.x, origin.y)
call BlzSetSpecialEffectZ(effect.effect, origin.z)
call BlzSetSpecialEffectYaw(effect.effect, cA)
endmethod
method operator model takes nothing returns string
return effect.path
endmethod
/* ----------------------------- Curved movement ---------------------------- */
method operator curve= takes real value returns nothing
set open = Tan(value*bj_DEGTORAD)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)*bj_RADTODEG
endmethod
/* ----------------------------- Arced Movement ----------------------------- */
method operator arc= takes real value returns nothing
set height = Tan(value*bj_DEGTORAD)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)*bj_RADTODEG
endmethod
/* ------------------------------ Effect scale ------------------------------ */
method operator scale= takes real value returns nothing
set effect.size = value
call effect.scale(effect.effect, value)
endmethod
method operator scale takes nothing returns real
return effect.size
endmethod
/* ------------------------------ Missile Speed ----------------------------- */
method operator speed= takes real newspeed returns nothing
local real d = origin.distance
local real s
local real vel
set veloc = newspeed*PERIOD
set vel = veloc*dilation
set s = travel + vel
set nextX = x + vel*Cos(cA)
set nextY = y + vel*Sin(cA)
if height != 0 or origin.slope != 0 then
set nextZ = 4*height*s*(d-s)/(d*d) + origin.slope*s + origin.z
set z = nextZ
endif
endmethod
method operator speed takes nothing returns real
return veloc/PERIOD
endmethod
/* ------------------------------- Flight Time ------------------------------ */
method operator duration= takes real flightTime returns nothing
local real d = origin.distance
local real s
local real vel
set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
set time = flightTime
set vel = veloc*dilation
set s = travel + vel
set nextX = x + vel*Cos(cA)
set nextY = y + vel*Sin(cA)
if height != 0 or origin.slope != 0 then
set nextZ = 4*height*s*(d-s)/(d*d) + origin.slope*s + origin.z
set z = nextZ
endif
endmethod
method operator duration takes nothing returns real
return time
endmethod
/* ------------------------------- Sight Range ------------------------------ */
method operator vision= takes real sightRange returns nothing
set sight = sightRange
if dummy == null then
if owner == null then
if source != null then
set dummy = Pool.retrieve(x, y, z, 0)
call SetUnitOwner(dummy, GetOwningPlayer(source), false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
else
set dummy = Pool.retrieve(x, y, z, 0)
call SetUnitOwner(dummy, owner, false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
else
call SetUnitOwner(dummy, owner, false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
endmethod
method operator vision takes nothing returns real
return sight
endmethod
/* ------------------------------- Time Scale ------------------------------- */
method operator timeScale= takes real newTimeScale returns nothing
set effect.timeScale = newTimeScale
endmethod
method operator timeScale takes nothing returns real
return effect.timeScale
endmethod
/* ---------------------------------- Alpha --------------------------------- */
method operator alpha= takes integer newAlpha returns nothing
set effect.alpha = newAlpha
endmethod
method operator alpha takes nothing returns integer
return effect.alpha
endmethod
/* ------------------------------ Player Color ------------------------------ */
method operator playerColor= takes integer playerId returns nothing
set effect.playerColor = playerId
endmethod
method operator playerColor takes nothing returns integer
return effect.playerColor
endmethod
/* -------------------------------- Animation ------------------------------- */
method operator animation= takes integer animType returns nothing
set effect.animation = animType
endmethod
method operator animation takes nothing returns integer
return effect.animation
endmethod
endmodule
private module Methods
/* --------------------------- Bounce and Deflect --------------------------- */
method bounce takes nothing returns nothing
call origin.move(x, y, z - GetLocZ(x, y))
set travel = 0
set finished = false
endmethod
method deflect takes real tx, real ty, real tz returns nothing
local real locZ = GetLocZ(x, y)
set target = null
set toZ = tz
if z < locZ and .onTerrain.exists then
set nextX = prevX
set nextY = prevY
set nextZ = prevZ
endif
call impact.move(tx, ty, tz)
call origin.move(x, y, z - locZ)
set travel = 0
set finished = false
endmethod
method deflectTarget takes unit u returns nothing
call deflect(GetUnitX(u), GetUnitY(u), toZ)
set target = u
endmethod
/* ---------------------------- Flush hit targets --------------------------- */
method flushAll takes nothing returns nothing
call FlushChildHashtable(table, this)
endmethod
method flush takes widget w returns nothing
if w != null then
call RemoveSavedBoolean(table, this, GetHandleId(w))
endif
endmethod
method hitted takes widget w returns boolean
return HaveSavedBoolean(table, this, GetHandleId(w))
endmethod
/* ----------------------- Missile attachment methods ----------------------- */
method attach takes string model, real dx, real dy, real dz, real scale returns effect
return effect.attach(model, dx, dy, dz, scale)
endmethod
method detach takes effect attachment returns nothing
if attachment != null then
call effect.detach(attachment)
endif
endmethod
/* ------------------------------ Missile Pause ----------------------------- */
method pause takes boolean flag returns nothing
implement OnResume
endmethod
/* ---------------------------------- Color --------------------------------- */
method color takes integer red, integer green, integer blue returns nothing
call effect.setColor(red, green, blue)
endmethod
/* ---------------------- Destructable collision method --------------------- */
static method onDest takes nothing returns nothing
local thistype this = temp
local destructable d = GetEnumDestructable()
local real dz
local real tz
if not HaveSavedBoolean(table, this, GetHandleId(d)) then
if collideZ then
set dz = GetLocZ(GetWidgetX(d), GetWidgetY(d))
set tz = GetDestructableOccluderHeight(d)
if dz + tz >= z - collision and dz <= z + collision then
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
else
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
endif
set d = null
endmethod
/* -------------------------- Item collision method ------------------------- */
static method onItems takes nothing returns nothing
local thistype this = temp
local item i = GetEnumItem()
local real dz
if not HaveSavedBoolean(table, this, GetHandleId(i)) then
if collideZ then
set dz = GetLocZ(GetItemX(i), GetItemY(i))
if dz + ITEM_SIZE >= z - collision and dz <= z + collision then
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
else
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
endif
set i = null
endmethod
/* -------------------------------- Terminate ------------------------------- */
method terminate takes nothing returns nothing
implement OnRemove
endmethod
endmodule
struct Missiles extends MissileEvents
private static timer timer = CreateTimer()
private static group group = CreateGroup()
private static rect rect = Rect(0., 0., 0., 0.)
private static hashtable table = InitHashtable()
private static integer last = 0
private static thistype temp = 0
private static integer id = -1
private static integer pid = -1
private static thistype array missiles
private static thistype array frozen
private static real dilation = 1
readonly static thistype array collection
readonly static integer count = -1
private real cA
private real height
private real open
private real toZ
private real time
private real sight
private unit dummy
private integer pkey
private integer index
Coordinates impact
Coordinates origin
MissileEffect effect
readonly real x
readonly real y
readonly real z
readonly real prevX
readonly real prevY
readonly real prevZ
readonly real nextX
readonly real nextY
readonly real nextZ
readonly real turn
readonly real veloc
readonly real travel
readonly boolean launched
readonly boolean allocated
readonly boolean finished
readonly boolean paused
readonly integer tileset
unit source
unit target
player owner
boolean collideZ
real collision
real damage
real acceleration
integer data
integer type
boolean roll
implement Operators
implement Methods
/* ------------------------------ Reset members ----------------------------- */
private method reset takes nothing returns nothing
set launched = false
set finished = false
set collideZ = false
set paused = false
set roll = false
set source = null
set target = null
set owner = null
set dummy = null
set open = 0.
set height = 0.
set veloc = 0.
set acceleration = 0.
set collision = 0.
set damage = 0.
set travel = 0.
set turn = 0.
set time = 0.
set sight = 0.
set data = 0
set type = 0
set tileset = 0
set pkey = -1
set index = -1
endmethod
/* -------------------------- Destroys the missile -------------------------- */
private method remove takes integer i returns integer
if paused then
implement OnPause
else
implement OnRemove
endif
set missiles[i] = missiles[id]
set id = id - 1
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1
endif
if id == -1 then
call PauseTimer(timer)
endif
if not allocated then
call deallocate()
endif
return i - 1
endmethod
/* ---------------------------- Missiles movement --------------------------- */
private static method move takes nothing returns nothing
local integer j = 0
local integer i
local integer k
local unit u
local real a
local real d
local real s
local real h
local real c
local real dx
local real dy
local real vel
local real yaw
local real pitch
local Missiles missile
local Coordinates o
local thistype this
if SWEET_SPOT > 0 then
set i = last
else
set i = 0
endif
loop
exitwhen ((j >= SWEET_SPOT and SWEET_SPOT > 0) or j > id)
set this = missiles[i]
set temp = this
if allocated and not paused then
implement OnHit
implement OnMissile
implement OnDestructable
implement OnItem
implement OnCliff
implement OnTerrain
implement OnTileset
implement OnPeriod
implement OnOrient
implement OnFinish
implement OnBoundaries
else
set i = remove(i)
set j = j - 1
endif
set i = i + 1
set j = j + 1
if i > id and SWEET_SPOT > 0 then
set i = 0
endif
endloop
set last = i
set u = null
endmethod
/* --------------------------- Launch the Missile --------------------------- */
method launch takes nothing returns nothing
if not launched and allocated then
set launched = true
set id = id + 1
set missiles[id] = this
set count = count + 1
set index = count
set collection[count] = this
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if id == 0 then
call TimerStart(timer, PERIOD, true, function thistype.move)
endif
endif
endmethod
/* --------------------------- Main Creator method -------------------------- */
static method create takes real x, real y, real z, real toX, real toY, real toZ returns thistype
local thistype this = thistype.allocate()
call .reset()
set .origin = Coordinates.create(x, y, z)
set .impact = Coordinates.create(toX, toY, toZ)
set .effect = MissileEffect.create(x, y, origin.z)
call Coordinates.link(origin, impact)
set .allocated = true
set .cA = origin.angle
set .x = x
set .y = y
set .z = impact.z
set .prevX = x
set .prevY = y
set .prevZ = impact.z
set .nextX = x
set .nextY = y
set .nextZ = impact.z
set .toZ = toZ
return this
endmethod
endstruct
endlibrary
scope StarStrike
globals
constant real PERIOD = 0.03 //tick rate of the shockwave
constant string STAR_MODEL = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl" //model of the falling star
constant string SHOCKWAVE_MODEL = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl" //modoel of the shockwave
constant integer AId = 'A000' //rawcode of the ability
constant real STAR_SCALE = 2.0 //scale of the star projectile
constant real SHOCKWAVE_START_AOE = 200.0 //starting aoe of the shockwave
constant real SHOCKWAVE_END_AOE = 600.0 //ending aoe of the shockwave
constant real SHOCKWAVE_START_SCALE = 2.0 //starting scale of the shockwave effect
constant real SHOCKWAVE_END_SCALE = 12.0 //ending scale of the shockwave effect
constant real SHOCKWAVE_EXPANSION_TIME = 1.0 //time from when the shockwave is created to when its reaches maximum scale
endglobals
private struct Shockwave
private timer timer
private group hit
private real x
private real y
private real damage
private real endingaoe
private real totaltime
private real expansionpertick
private effect fx
private real fxscalepertick
private real currentaoe
private real currentfxscale
private unit source
private player owner
private static method onPeriod takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit temp
local group enemies = CreateGroup()
if currentaoe <= endingaoe then
call GroupEnumUnitsInRange(enemies, .x,.y, currentaoe, null)
loop
set temp = FirstOfGroup(enemies)
exitwhen temp == null
if not IsUnitInGroup(temp,hit) then
call GroupAddUnit(hit,temp)
//call BJDebugMsg("Shockwave hit " + GetUnitName(temp))
if IsUnitEnemy(temp, owner) and not IsUnitType(temp, UNIT_TYPE_MAGIC_IMMUNE) and not BlzIsUnitInvulnerable(temp) and UnitAlive(temp) and not IsUnitType(temp, UNIT_TYPE_STRUCTURE) then
call UnitDamageTarget(.source, temp, damage, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
//If you want secondary effects on the shockwave target units, add them here
endif
endif
call GroupRemoveUnit(enemies, temp)
endloop
set .currentaoe = .currentaoe + .expansionpertick
set .currentfxscale = .currentfxscale + .fxscalepertick
call BlzSetSpecialEffectScale(fx,currentfxscale)
else
call DestroyEffect(fx)
call ReleaseTimer(timer)
call deallocate()
call DestroyGroup(hit)
set timer = null
set fx=null
endif
call DestroyGroup(enemies)
set enemies=null
set temp=null
endmethod
static method Create takes real x, real y, real damage, real startingaoe, real endingaoe, real totaltime, unit source, player owner, real fxstartscale, real fxendscale returns nothing
local thistype this = thistype.create()
set timer = NewTimerEx(this)
set .x = x
set .y = y
set .damage = damage
set .currentaoe = startingaoe
set .endingaoe = endingaoe
set .totaltime = .totaltime
set .currentfxscale = fxstartscale
set .source = source
set .owner = owner
set .fx = AddSpecialEffect(SHOCKWAVE_MODEL,x,y)
call BlzSetSpecialEffectScale(fx,fxstartscale)
set .expansionpertick = ((.endingaoe - startingaoe) / totaltime) * PERIOD
set .fxscalepertick = ((fxendscale - fxstartscale) / totaltime) * PERIOD
set .hit = CreateGroup()
call TimerStart(timer, PERIOD, true, function thistype.onPeriod)
endmethod
endstruct
private struct Star extends Missiles
real aoe
method onFinish takes nothing returns boolean
local unit temp
local group enemies = CreateGroup()
call GroupEnumUnitsInRange(enemies, .x,.y, aoe, null)
loop
set temp = FirstOfGroup(enemies)
exitwhen temp == null
if IsUnitEnemy(temp, owner) and not IsUnitType(temp, UNIT_TYPE_MAGIC_IMMUNE) and not BlzIsUnitInvulnerable(temp) and UnitAlive(temp) and not IsUnitType(temp, UNIT_TYPE_STRUCTURE) then
call UnitDamageTarget(.source, temp, damage, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
//If you want secondary effects on the impact target units, add them here
endif
call GroupRemoveUnit(enemies, temp)
endloop
call Shockwave.Create(.x, .y, 100.0, SHOCKWAVE_START_AOE, SHOCKWAVE_END_AOE, SHOCKWAVE_EXPANSION_TIME, source, owner, SHOCKWAVE_START_SCALE ,SHOCKWAVE_END_SCALE)
call DestroyGroup(enemies)
set enemies=null
set temp=null
return true
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local thistype this = thistype.create(x, y, 1500, GetSpellTargetX(), GetSpellTargetY(), 0)
set source = c
set model = STAR_MODEL
set duration = 1.0
set arc = 0.
set curve = 0.0
set owner = GetOwningPlayer(c)
set damage = 250.0
set scale = STAR_SCALE
set aoe = 200.0
call launch()
set c = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(AId, function thistype.onCast)
endmethod
endstruct
endscope