Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
AOEDamageEvent | real | No | |
ClearDamageEvent | trigger | No | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DamageBlockingAbility | abilcode | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageModifierEvent | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypeReduced | integer | No | |
DmgEvBracers | itemcode | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
EnhancedDamageTarget | unit | No | |
HideDamageFrom | boolean | Yes | |
IsDamageSpell | boolean | No | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
SpellDamageAbility | abilcode | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitDamageRegistered | boolean | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes |
//TESH.scrollpos=51
//TESH.alwaysfold=0
library UnitIndexerGUI //requires GUI Unit Indexer
globals
private trigger index = null
private trigger deindex = null
private trigger init = null
private trigger tempTrig
endglobals
private function DoTheThings takes trigger t, code c, real r returns trigger
if t == null then
set t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, r)
endif
call TriggerAddCondition(t, Filter(c))
set tempTrig = t
set t = null
return tempTrig
endfunction
//call RegisterUnitIndexEvent(Filter(function Blah), EVENT_UNIT_INDEXED)
//->
//call OnUnitIndex(function Blah)
function OnUnitIndex takes code c returns nothing
set index = DoTheThings(index, c, 1.)
endfunction
//call RegisterUnitIndexEvent(Filter(function Blah), EVENT_UNIT_DEINDEXED)
//->
//call OnUnitDeindex(function Blah)
function OnUnitDeindex takes code c returns nothing
set deindex = DoTheThings(deindex, c, 2.)
endfunction
private function DestroyInit takes nothing returns boolean
call DestroyTrigger(init) //will still allow consecutive triggerconditions to run.
set init = null
return false
endfunction
//GUI Unit Indexer will initialize after any vJass stuff, so you need to do your
//unit-indexer-requiring stuff after this event instead of a module/struct/library
//initializer.
function OnUnitIndexerInitialized takes code c returns nothing
if init == null then
set init = CreateTrigger()
call TriggerAddCondition(init, Filter(function DestroyInit))
call TriggerRegisterVariableEvent(init, "udg_UnitIndexEvent", EQUAL, 3.00)
endif
call TriggerAddCondition(init, Filter(c))
endfunction
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
function GetUnitById takes integer id returns unit
return udg_UDexUnits[id]
endfunction
function GetIndexedUnit takes nothing returns unit
return udg_UDexUnits[udg_UDex]
endfunction
function GetIndexedUnitId takes nothing returns integer
return udg_UDex
endfunction
function IsUnitIndexed takes unit u returns boolean
return udg_UDexUnits[GetUnitUserData(u)] == u
endfunction
endlibrary
//TESH.scrollpos=0
//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 Missiles requires WorldBounds, optional DummyRecycler
/* ----------------------- Missiles v1.5 by Chopinski ----------------------- */
// Thanks and Full Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
// this Missiles library. Credits to Vexorian for the dummy model.
// How to Import:
// 1 -First copy the Missile dummy unit into your map and then import the dummy.mdx
// model, setting the missile dummy model path to imported dummy.mdx model.
// Dummy model: https://www.hiveworkshop.com/threads/vexorians-dummy-model.149230/
// WorldBounds: https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
// DummyRecycler: https://www.hiveworkshop.com/threads/dummy-recycler-v1-25.277659/ (Highly Recommended)
// 2 - Copy this library into your map and set the
// DUMMY_RAW_CODE to the raw code of the missile dummy (ctrl + d) and you
// are done
// How to Use:
// This system works almost identicaly to the Missile library by BPower but
// with a more user friendly interface in my opinion. Also this system
// allows you to create Arc/Curved Homing missiles like Dirac's system.
// Differently than both BPower and Dirac systems, you are not required
// to use the ImplementStruct at the end of your struct. To make your struct
// behave like a Missile simply make it extends Missiles and you are done.
// After that you will have access to the events in the MissileEvents Interface.
// Simply declare the event you want for your strcut to have and the system
// takes care of the rest. You can access the members and functions using
// the "this" or "." syntax which is a plus. to terminate a missile, simply
// return true or call terminate() within the method. Example:
// struct MySpell extends Missiles
// method onPeriod takes nothing returns boolean
// // will display the missile current position
// call ClearTextMessages()
// call BJDebugMsg(I2S(.x))
// call BJDebugMsg(I2S(.y))
// call BJDebugMsg(I2S(.z))
// return false
// endmethod
// endstruct
// function Spell takes nothing returns nothing
// // the create method takes the initial and the final coordinates
// // if the target member is set, the missile home
// local MySpell spell = MySpell.create(fromX, fromY, fromZ, toX, toY, toZ)
// set spell.source = GetTriggerUnit()
// set spell.target = GetSpellTargetUnit() -> when a target is specified the missile will be homing
// set spell.speed = 1000 -> warcraft 3 speed unit
// set spell.duration = 1.5 -> will set the speed to match the time passed
// set spell.model = "Some Model.dmx"
// set spell.scale = 1.3
// set spell.arc = 30 (degrees converted to radians internally)
// set spell.curve = GetRandomReal(-40, 40) (degrees converted to radians internally
// call spell.launch()
// endfunction
// Available members and methods:
// Coordinates impact -> the impact coordiantes (x, y, z, anngle, slope, alpha, distance)
// Coordinates origin -> same, but for origin
// //-------------------------------------------------------
// readonly real x -> current x position of the missile
// readonly real y -> current y position of the missile
// readonly real z -> current z position of the missile
// readonly real prevX -> last x position of the missile
// readonly real prevY -> last y position of the missile
// readonly real prevZ -> last z position of the missile
// readonly real height -> the arc of the missile (change it using the .arc operator)
// readonly real turn -> the turn rate of the missile
// readonly real open -> the curvature of the missile (change it using the .curve operator)
// readonly real veloc -> the missile speed (change it using the .speed or .duration operator)
// readonly real travel -> distance travelled
// readonly unit dummy -> the dummy unit
// readonly boolean launched -> true if the missile was already launched
// readonly boolean allocated -> true if the missile can still perform its movement operations
// //-------------------------------------------------------
// unit source -> the source unit (optional)
// unit target -> the target unit (optional and if set to a valid unit the missile will be homing)
// player owner -> the owner of the missile (optional)
// boolean collideZ -> set to true if you want the missile to consider z in collisions
// real collision -> the collision size of the missile (optional. The onHit and onDestructable events only works if this is greater than 0)
// real damage -> stores the damage you want the missile to deal (optional)
// real acceleration -> if different than 0 then the missile will change its speed with time (optional)
// integer data -> just in case you want to pass some information to the missile to retrieve it later (optional)
// (call)
// method deflect takes real x, real y returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x and y.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method deflectZ takes real x, real y, real z returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x, y and z.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method bounce takes nothing returns nothing
// - Bounces the missile from it's current Z position.
// - Useful when assigning targets to missiles that were
// - already active, it fixes the rough Z position change.
// (call)
// method flush takes widget w returns nothing
// - call this method to allow the missile to be able to hit
// - a unit or destructable again
// (call)
// method flushAll nothing returns nothing
// - flushes the hit table for the missile
// (call)
// method hitted takes widget w returns boolean
// - returns true if the missile has hitted the widget
// (optional)
// method onHit takes unit hit returns boolean
// - Runs every time the missile collides with a unit.
// - If returns true the missile is destroyed.
// (optional)
// method onDestructable takes destructable dest returns boolean
// - Runs every time the missile collides with a destructable.
// - If returns true the missile is destroyed.
// (optional)
// method onItem takes item i returns boolean
// - Runs every time the missile collides with an item.
// - If returns true the missile is destroyed.
// (optional)
// method onMissile takes Missiles missile returns boolean
// - Runs every time the missile collides with another missile.
// - By default, missiles collide only once
// - If returns true the missile is destroyed.
// - Please, be aware that this method can be very performance
// - intensive, so careful!
// (optional)
// method onPeriod takes nothing returns boolean
// - Runs every period.
// - If returns true the missile is destroyed.
// (optional)
// method onFinish takes nothing returns boolean
// - Runs whenever the missile finishes it's course.
// - If returns true the missile is destroyed.
// (optional)
// method onTerrain takes nothing returns boolean
// - Runs whenever the missile collides with terrain height
// - greater then the missile current z
// - If returns true the missile is destroyed.
// (optional)
// method onRemove takes nothing returns nothing
// - Runs whenever the missile is deallocated.
// (optional)
// method onBoundaries takes nothing returns boolean
// - Runs whenever the missile is trying to move
// - out of map bounds.
// /* ----------------------------------- END ---------------------------------- */
/* --------------------------------- System --------------------------------- */
globals
// The raw code of the dummy unit. Match it with the
// Object Editor id
private constant integer DUMMY_RAW_CODE = 'dum0'
// 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.
// uint size used in z collision
private constant real BODY_SIZE = 64.
// How long takes for the missile to be removed.
// This is necessary so the death animation of the
// effect can play through
private constant real RECYCLE_TIME = 2.
// Needed, dont touch. Seriously, dont touch!
private location LOC = Location(0., 0.)
private rect RECT = Rect(0., 0., 0., 0.)
private hashtable table = InitHashtable()
endglobals
// The available methods the user can implement in their structs
private interface MissileEvents
method onHit takes unit hit returns boolean defaults false
method onItem takes item i returns boolean defaults false
method onMissile takes Missiles missile returns boolean defaults false
method onTerrain takes nothing returns boolean defaults false
method onPeriod takes nothing returns boolean defaults false
method onFinish takes nothing returns boolean defaults false
method onDestructable takes destructable dest returns boolean defaults false
method onBoundaries takes nothing returns boolean defaults false
method onRemove takes nothing returns nothing defaults nothing
endinterface
function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
//Little snippet to remove the dummy unit after a delay
//I'm not using MissileRecycler because of a leaking problem i discover
//This will only be used if you dont have DummyRecycler
private struct Timed
timer t
unit u
static method onPeriod takes nothing returns nothing
local thistype this = LoadInteger(table, GetHandleId(GetExpiredTimer()), 0)
call FlushChildHashtable(table, GetHandleId(.t))
call PauseTimer(.t)
call DestroyTimer(.t)
call RemoveUnit(.u)
set .u = null
set .t = null
call .deallocate()
endmethod
static method Recycle takes unit u, real timeout returns nothing
local thistype this = thistype.allocate()
set .u = u
set .t = CreateTimer()
call SaveInteger(table, GetHandleId(.t), 0, this)
call TimerStart(.t, timeout, false, function thistype.onPeriod)
endmethod
endstruct
//Credits to Dirac for AdvLoc and BPower for fixing an error in it
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)*bj_RADTODEG
// 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
/* ------------------------------ Missile Sytem ----------------------------- */
struct Missiles extends MissileEvents
private static thistype array missiles
private static integer didx = -1
private static integer last = 0
private static real dilation
private static timer t = CreateTimer()
private static group hitGroup = CreateGroup()
private static thistype temp = 0
//-------------------------------------------------------
private real cA // current angle
private effect sfx // effect
private string fxpath // model
private real size // scale
private real height // Arcs
private real open // Curves
private real toZ // Necessary to fix a bug for homming missiles
//-------------------------------------------------------
Coordinates impact
Coordinates origin
//-------------------------------------------------------
readonly real x
readonly real y
readonly real z
readonly real prevX
readonly real prevY
readonly real prevZ
readonly real turn
readonly real veloc
readonly real travel
readonly unit dummy
readonly boolean launched
readonly boolean allocated
//-------------------------------------------------------
unit source
unit target
player owner
boolean collideZ
real collision
real damage
real acceleration
integer data
//-------------------------------------------------------
/* -------------------------- Model of the missile -------------------------- */
method operator model= takes string path returns nothing
call DestroyEffect(sfx)
set fxpath = path
set sfx = AddSpecialEffectTarget(path, dummy, "origin")
endmethod
method operator model takes nothing returns string
return fxpath
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)
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)
endmethod
/* ------------------------------ Effect scale ------------------------------ */
method operator scale= takes real v returns nothing
call SetUnitScale(dummy, v, v, v)
set size = v
endmethod
method operator scale takes nothing returns real
return size
endmethod
/* ------------------------------ Missile Speed ----------------------------- */
method operator speed= takes real newspeed returns nothing
set veloc = newspeed*PERIOD
endmethod
method operator speed takes nothing returns real
return veloc
endmethod
/* ------------------------------- Flight Time ------------------------------ */
method operator duration= takes real flightTime returns nothing
set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
endmethod
/* ---------------------------- Bound and Deflect --------------------------- */
method bounce takes nothing returns nothing
local real locZ = GetLocZ(x, y)
// This is here just to avoid an infinite loop
// with a deflect being called within an onTerrain
// event
if z < locZ then
set z = locZ
call impact.move(impact.x, impact.y, origin.z - GetLocZ(impact.x, impact.y))
endif
call origin.move(x, y, origin.z - GetLocZ(origin.x, origin.y))
set travel = 0
endmethod
method deflect takes real tx, real ty returns nothing
if target != null then
set target = null
endif
call impact.move(tx, ty, impact.z - GetLocZ(impact.x, impact.y))
call bounce()
endmethod
method deflectZ takes real tx, real ty, real tz returns nothing
call impact.move(impact.x, impact.y, tz)
call deflect(tx, ty)
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
/* ------------------------- Destructable hit method ------------------------ */
private 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)) - GetLocZ(x, y)
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 ------------------------- */
private 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)) - GetLocZ(x, y)
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
/* ------------------------------ Reset members ----------------------------- */
private method reset takes nothing returns nothing
set launched = false
set collideZ = false
set source = null
set target = null
set owner = null
set sfx = null
set dummy = null
set fxpath = ""
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 size = 0.
set data = 0
endmethod
/* -------------------------------- Terminate ------------------------------- */
method terminate takes nothing returns nothing
if allocated then
set allocated = false
// onRemove event
if .onRemove.exists then
call .onRemove()
endif
call FlushChildHashtable(table, this)
endif
endmethod
/* -------------------------- Destroys the missile -------------------------- */
method remove takes integer i returns integer
call terminate()
call DestroyEffect(sfx)
call origin.destroy()
call impact.destroy()
static if LIBRARY_DummyRecycler then
call DummyAddRecycleTimer(dummy, RECYCLE_TIME)
else
call Timed.Recycle(dummy, RECYCLE_TIME)
endif
call reset()
set missiles[i] = missiles[didx]
set didx = didx - 1
//Compensation for time dilation
if didx + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (didx + 1)/SWEET_SPOT
else
set dilation = 1
endif
if didx == -1 then
call PauseTimer(t)
endif
call deallocate()
return i - 1
endmethod
/* --------------------------- Launch the Missile --------------------------- */
method launch takes nothing returns nothing
if not launched and allocated then
set launched = true
set didx = didx + 1
set missiles[didx] = this
//Compensation for time dilation
if didx + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (didx + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if didx == 0 then
call TimerStart(t, PERIOD, true, function thistype.move)
endif
endif
endmethod
/* ---------------------------- Missiles movement --------------------------- */
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 locZ
local real yaw
local real pitch
local Missiles missile
local Coordinates o
local thistype this
// SWEET_SPOOT used to enable or disable
// relativistic processing
if SWEET_SPOT > 0 then
set i = last
else
set i = 0
endif
loop
exitwhen ((j >= SWEET_SPOT and SWEET_SPOT > 0) or j > didx)
set this = missiles[i]
set temp = this
if allocated then
set o = origin
set h = height
set c = open
set d = o.distance
set locZ = GetLocZ(x, y)
//onPeriod Event
if .onPeriod.exists then
if allocated and .onPeriod() then
call terminate()
endif
endif
// onHit Event
if .onHit.exists then
if allocated and collision > 0 then
call GroupEnumUnitsInRange(hitGroup, x, y, collision + COLLISION_SIZE, null)
loop
set u = FirstOfGroup(hitGroup)
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) - locZ
set dy = BODY_SIZE
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(hitGroup, u)
endloop
endif
endif
// onMissile Event
if .onMissile.exists then
if allocated and collision > 0 then
set k = 0
loop
exitwhen k > didx
set missile = missiles[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
// onDestructable Event
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
// onItem Event
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
// 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 - prevX
set dy = impact.y - prevY
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 x = prevX + vel*Cos(cA)
set y = prevY + vel*Sin(cA)
set s = travel + vel
set prevX = x
set prevY = y
set prevZ = z
set travel = s
set veloc = veloc + acceleration
set pitch = origin.alpha
set yaw = cA*bj_RADTODEG
// arc calculation
if h != 0 or o.slope != 0 then
set z = 4*h*s*(d-s)/(d*d) + o.slope*s + o.z
set pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))*bj_RADTODEG
call SetUnitFlyHeight(dummy, z - GetLocZ(x, y), 0)
endif
// curve calculation
if c != 0 then
set dx = 4*c*s*(d-s)/(d*d)
set a = cA + bj_PI/2
set x = x + dx*Cos(a)
set y = y + dx*Sin(a)
set yaw = (cA + Atan(-((4*c)*(2*s - d))/(d*d)))*bj_RADTODEG
endif
// onTerrain event
if .onTerrain.exists then
if GetLocZ(x, y) > z then
if allocated and .onTerrain() then
call terminate()
endif
endif
endif
if s >= d then
// onFinish event
if .onFinish.exists then
if allocated and .onFinish() then
call terminate()
else
// deflected onFinish
if travel > 0 then
call terminate()
endif
endif
else
call terminate()
endif
endif
// missile orientation and positioning
call SetUnitAnimationByIndex(dummy, R2I(pitch + 90.5))
call SetUnitFacing(dummy, yaw)
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
else
// onBoundaries event
if .onBoundaries.exists then
if allocated and .onBoundaries() then
call terminate()
endif
endif
endif
else
set i = remove(i)
set j = j - 1
endif
set i = i + 1
set j = j + 1
if i > didx and SWEET_SPOT > 0 then
set i = 0
endif
endloop
set last = i
set u = null
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()
local real angle = Atan2(toY - y, toX - x)*bj_RADTODEG
call .reset()
set .origin = Coordinates.create(x, y, z)
set .impact = Coordinates.create(toX, toY, toZ)
set .allocated = true
set .x = x
set .y = y
set .z = z
set .prevX = x
set .prevY = y
set .prevZ = z
set .toZ = toZ
static if LIBRARY_DummyRecycler then
set .dummy = GetRecycledDummy(x, y, z - GetLocZ(x, y), angle)
else
set .dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_RAW_CODE, x, y, angle)
call SetUnitFlyHeight(.dummy, z - GetLocZ(x, y), 0)
endif
call Coordinates.link(origin, impact)
set .cA = origin.angle
return this
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DummyRecycler /*
// DummyRecycler v1.24
// by Flux
//
// A system that recycles dummy units while considering their facing angle.
// It can be used as attachment units or as dummy caster.
//
// Why is recycling a unit important?
// Because creating a unit is is one of the slowest function in the game
// and it will always leave a permanent tiny bit of memory (0.04 KB).
// Furthermore, if a Damage Detection System (DDS) or a Unit Indexer is
// imported to your map, using this system will even result to better
// performance because DDS and Unit Indexer process new created units.
//
//
*/ requires /*
nothing
*/ optional Table/*
if not found, this system will use a hashtable. Hashtables are limited to
255 per map.
*/ optional WorldBounds /*
if not found, this system will initialize its own Map Boundaries.
//
//
// Features:
//
// -- Dummy Sharing
// When a Dummy List gets low on unit count, it will borrow Dummy Units
// from the Dummy List with the highest unit count. The transfer is not
// instant because the shared Dummy Unit has to turn to the appropriate
// angle of its new Dummy List before it can be recycled.
// See BORROW_REQUEST.
//
// -- Self-balancing recycling algorithm
// Recycled Dummy Units will be thrown to the List having the least number
// of Dummy Units.
//
// -- Recycling least used
// Allows recycling a Dummy from the Dummy List with the highest
// unit count. It is useful when the facing angle of the Dummy Unit
// does not matter.
// See GetRecycledDummyAnyAngle.
//
// -- Self-adaptation
// When there are no free Dummy Units from a Dummy List, it will end up creating
// a new unit instead but that unit will be permanently added as a Dummy
// Unit to be recycled increasing the overall total Dummy Unit count.
//
// -- Count control
// Allows limiting the overall number of Dummy Units.
// See MAX_DUMMY_COUNT.
//
// -- Delayed Recycle
// Allows recycling Dummy Units after some delay to allocate time for the
// death animation of Special Effects to be seen.
// See DummyAddRecycleTimer.
//
// ******************************************************************
// ***************************** API: *******************************
// ******************************************************************
//
// function GetRecycledDummy takes real x, real y, real z, real facing returns unit
// - Retrieve an unused Dummy Unit from the List.
// - The equivalent of CreateUnit.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
// - Use this function if the facing angle of the Dummy doesn't matter to you.
// - It will return a unit from the list having the highest number of unused Dummy Units.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function RecycleDummy takes unit u returns nothing
// - Recycle the Dummy unit for it to be used again later.
// - The equivalent of RemoveUnit.
//
// function DummyAddRecycleTimer takes unit u, real time returns nothing
// - Recycle the Dummy unit after a certain time.
// - Use this to allocate time for the the death animation of an effect attached to the
// Dummy Unit to finish..
// - The equivalent of UnitApplyTimedLife.
//
//--------------------
// CREDITS
//--------------------
// Bribe - for the MissileRecycler (vJASS) where I got this concept from
// http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
// - for the optional Table
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
// Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
// http://www.wc3c.net/showthread.php?t=101150
// Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
// http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
// Nestharus - for the data structure
// http://www.hiveworkshop.com/forums/2809461-post7.html
// - for the optional WorldBounds
// http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// =============================================================== //
// ====================== CONFIGURATION ========================== //
// =============================================================== */
globals
//The rawcode of the Dummy Unit
private constant integer DUMMY_ID = 'dum0'
//The owner of the Dummy Unit
private constant player OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
//The number of indexed angle. The higher the value the:
// - Lesser the turning time for the Dummy Units.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 10 (Max difference of 18 degrees)
private constant integer ANGLES_COUNT = 10
//The number of Dummy units per ANGLES_COUNT. The higher the value the:
// - Higher the number of units that can be recycled per angle, when
// no more units are in queue, the system will resort to use CreateUnit.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
private constant integer STORED_UNIT_COUNT = 3
//The maximum number of Dummy units that can exist. When the system resort
//to using CreateUnit, the unit will be permanently added to the Dummy
//List. To avoid spamming Dummy Units and having too much free Dummy
//Units to allocate, the maximum number of Dummy Units is capped.
// Recommended Value: 80 to 120
private constant integer MAX_DUMMY_COUNT = 100
//When a certain angle have less than BORROW_REQUEST units in its list,
//it will start to borrow Dummy Units from the list with the highest
//Dummy Unit count.
// Recommended Value: Half of maximum STORED_UNIT_COUNT
private constant integer BORROW_REQUEST = 5
//It will only return a Dummy if the current dummy is close
//to it's appropriate facing angle. This is to avoid returning
//a Dummy which is still turning to face it's list angle.
private constant real ANGLE_TOLERANCE = 10.0
//An additional option to automatically hide recycled dummy units in the
//corner of the map camera bounds
private constant boolean HIDE_ON_MAP_CORNER = true
endglobals
//Every time a new dummy unit is retrieved, it will apply this resets
//If it is redundant/you dont need it, remove it.
//! textmacro DUMMY_UNIT_RESET
call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
//! endtextmacro
// =============================================================== //
// ==================== END CONFIGURATION ======================== //
// =============================================================== //
globals
private integer dummyCount = ANGLES_COUNT*STORED_UNIT_COUNT
private real array angle
private integer array count
private integer array countHead
private integer array countNext
private integer array countPrev
private integer array next
private integer array prev
private unit array dummy
private integer upper
private integer lower
private integer lastInstance
private constant real FACING_OFFSET = 180.0/ANGLES_COUNT
endglobals
static if HIDE_ON_MAP_CORNER and not LIBRARY_WorldBounds then
private module BoundsInit
readonly static real x
readonly static real y
private static method onInit takes nothing returns nothing
local rect map = GetWorldBounds()
set thistype.x = GetRectMaxX(map)
set thistype.y = GetRectMaxY(map)
call RemoveRect(map)
set map = null
endmethod
endmodule
private struct Bounds extends array
implement BoundsInit
endstruct
endif
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable hash = InitHashtable()
endif
private static method onInit takes nothing returns nothing
local real add = 360.0/ANGLES_COUNT
local real a = 0
local integer this = ANGLES_COUNT
local integer head = 0
local integer cHead = JASS_MAX_ARRAY_SIZE - 1 //avoid allocation collision
local integer i = R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
set upper = STORED_UNIT_COUNT
set lower = STORED_UNIT_COUNT
static if LIBRARY_Table then
set tb = Table.create()
endif
//Initialize countHeads
loop
exitwhen i < 0
set countNext[cHead] = cHead
set countPrev[cHead] = cHead
set countHead[i] = cHead
set cHead = cHead - 1
set i = i - 1
endloop
set cHead = countHead[STORED_UNIT_COUNT] //All heads will be inserted here initially
//Create the Dummy units
loop
exitwhen a >= 360
//Initialize head
set next[head] = head
set prev[head] = head
set count[head] = STORED_UNIT_COUNT
set angle[head] = a
//Insert head in the Count List
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
set i = 0
loop
exitwhen i >= STORED_UNIT_COUNT
//Queued Linked List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, a)
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, Bounds.x, Bounds.y, a)
endif
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, 0, 0, a)
endif
call PauseUnit(dummy[this], true)
static if LIBRARY_Table then
set tb[GetHandleId(dummy[this])] = this
else
call SaveInteger(hash, GetHandleId(dummy[this]), 0, this)
endif
set this = this + 1
set i = i + 1
endloop
set head = head + 1
set a = a + add
endloop
set lastInstance = this
endmethod
endmodule
private struct S extends array
implement M
endstruct
private function GetHead takes integer facing returns integer
if facing < 0 or facing >= 360 then
set facing = facing - (facing/360)*360
if facing < 0 then
set facing = facing + 360
endif
endif
return R2I((facing*ANGLES_COUNT/360.0))
endfunction
function GetRecycledDummy takes real x, real y, real z, real facing returns unit
local integer head = GetHead(R2I(facing + FACING_OFFSET))
local integer this = next[head]
local integer cHead
//If there are Dummy Units in the Queue List already facing close to the appropriate angle
if this != head and RAbsBJ(GetUnitFacing(dummy[this]) - angle[head]) <= ANGLE_TOLERANCE then
//Remove from the Queue List
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//For double free protection
set next[this] = -1
//Unit Properties
set bj_lastCreatedUnit = dummy[this]
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitFacing(bj_lastCreatedUnit, facing)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
//! runtextmacro DUMMY_UNIT_RESET()
//Update Count and Bounds
set count[head] = count[head] - 1
//------------------------------------------------
// Unit Sharing
//------------------------------------------------
if count[head] < BORROW_REQUEST and count[countNext[countHead[upper]]] > count[head] then
set count[head] = count[head] + 1
set this = next[countNext[countHead[upper]]]
call SetUnitFacing(dummy[this], angle[head])
//Remove
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//Add to the Current List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
set head = countNext[countHead[upper]]
set count[head] = count[head] - 1
endif
//---------------------------
//Update Count Lists
//---------------------------
//Remove from the current Count List
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[upper]
if countNext[cHead] == cHead then
set upper = upper - 1
endif
if count[head] < lower then
set lower = count[head]
endif
else
set bj_lastCreatedUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
call PauseUnit(bj_lastCreatedUnit, true)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
if dummyCount < MAX_DUMMY_COUNT then
set this = lastInstance
//For double free protection
set next[this] = -1
set dummy[this] = bj_lastCreatedUnit
static if LIBRARY_Table then
set S.tb[GetHandleId(bj_lastCreatedUnit)] = this
else
call SaveInteger(S.hash, GetHandleId(bj_lastCreatedUnit), 0, this)
endif
set lastInstance = lastInstance + 1
endif
set dummyCount = dummyCount + 1
endif
return bj_lastCreatedUnit
endfunction
function RecycleDummy takes unit u returns nothing
static if LIBRARY_Table then
local integer this = S.tb[GetHandleId(u)]
else
local integer this = LoadInteger(S.hash, GetHandleId(u), 0)
endif
local integer head
local integer cHead
//If the unit is a legit Dummy Unit
if this > 0 and next[this] == -1 then
//Find where to insert based on the list having the least number of units
set head = countNext[countHead[lower]]
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
//Update Status
call SetUnitFacing(u, angle[head])
call PauseUnit(u, true)
call SetUnitOwner(u, OWNER, false)
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
call SetUnitX(u, WorldBounds.maxX)
call SetUnitY(u, WorldBounds.maxY)
else
call SetUnitX(u, Bounds.x)
call SetUnitY(u, Bounds.y)
endif
else
call SetUnitScale(u, 0, 0, 0)
call SetUnitVertexColor(u, 0, 0, 0, 0)
endif
set count[head] = count[head] + 1
//---------------------------
// Update Count Lists
//---------------------------
//Remove
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[lower]
if countNext[cHead] == cHead then
set lower = lower + 1
endif
if count[head] > upper then
set upper = count[head]
endif
elseif this == 0 then
call RemoveUnit(u)
debug elseif next[this] != -1 then
debug call BJDebugMsg("|cffffcc00[DummyRecycler]:|r Attempted to recycle a pending/free Dummy Unit.")
endif
endfunction
private function Expires takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
static if LIBRARY_Table then
call RecycleDummy(S.tb.unit[id])
call S.tb.unit.remove(id)
else
call RecycleDummy(LoadUnitHandle(S.hash, id, 0))
call FlushChildHashtable(S.hash, id)
endif
call DestroyTimer(t)
set t = null
endfunction
function DummyAddRecycleTimer takes unit u, real time returns nothing
local timer t = CreateTimer()
static if LIBRARY_Table then
set S.tb.unit[GetHandleId(t)] = u
else
call SaveUnitHandle(S.hash, GetHandleId(t), 0, u)
endif
call TimerStart(t, time, false, function Expires)
set t = null
endfunction
function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
return GetRecycledDummy(x, y, z, angle[countNext[countHead[upper]]])
endfunction
// runtextmacro DUMMY_DEBUG_TOOLS()
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library RemoveAbilityTimed uses ListT optional DummyRecycler
//! novjass
// API
call RemoveAbilityTimed.create(whichUnit, whichAbility, whichTimeout, recycle)
whichAbility will be removed from whichUnit after whichTimeout. If recycle is true, the unit
will be recycled if you have DummyRecycler. Otherwise it will be killed.
//! endnovjass
private module RemoveAbilityTimedModule
private static constant real TIMEOUT = .1
private static constant integer DUMMY_ID = 'dum0'
private static IntegerList List
private static timer Clock = CreateTimer()
private unit u
private integer abil
private boolean recycle
private real time
private method destroy takes nothing returns nothing
set this.u = null
set this.abil = 0
set this.recycle = false
set this.time = 0.
call this.deallocate()
endmethod
private static method update takes nothing returns nothing
local IntegerListItem node = List.first
local IntegerListItem nodeNext
local thistype this
loop
exitwhen node == 0
set nodeNext = node.next
set this = node.data
set this.time = this.time - TIMEOUT
if this.time <= 0. then
call UnitRemoveAbility(this.u, this.abil)
static if LIBRARY_DummyRecycler then
if this.recycle then
if GetUnitTypeId(this.u) == DUMMY_ID then
call RecycleDummy(this.u)
else
call UnitApplyTimedLife(this.u, 'BTLF', .01)
endif
endif
else
if this.recycle then
call UnitApplyTimedLife(this.u, 'BTLF', .01)
endif
endif
call List.erase(node)
call this.destroy()
endif
set node = nodeNext
endloop
if List.empty() then
call PauseTimer(Clock)
endif
endmethod
static method create takes unit u, integer abil, real time, boolean recycle returns thistype
local thistype this = allocate()
set this.u = u
set this.abil = abil
set this.recycle = recycle
set this.time = time
if List.empty() then
call TimerStart(Clock, TIMEOUT, true, function thistype.update)
endif
call List.push(this)
return this
endmethod
private static method onInit takes nothing returns nothing
set List = IntegerList.create()
endmethod
endmodule
struct RemoveAbilityTimed
implement RemoveAbilityTimedModule
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=0
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 500.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* List<T> v2.1.2.0
* by Bannar
*
* Doubly-linked list.
*
******************************************************************************
*
* Requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
* Alloc - choose whatever you like
* e.g.: by Sevion hiveworkshop.com/threads/snippet-alloc.192348/
*
******************************************************************************
*
* Implementation:
*
* macro DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
*
* macro DEFINE_LIST takes ACCESS, NAME, TYPE
*
* ACCESS - encapsulation, choose restriction access
* NAME - name of list type
* TYPE - type of values stored
*
* Implementation notes:
*
* - DEFINE_STRUCT_LIST macro purpose is to provide natural typecasting syntax for struct types.
* - <NAME>Item structs inline directly into hashtable operations thus generate basically no code.
* - Lists defined with DEFINE_STRUCT_LIST are inlined nicely into single create method and single integer array.
*
******************************************************************************
*
* struct API:
*
* struct <NAME>Item:
*
* | <TYPE> data
* | <NAME>Item next
* | <NAME>Item prev
*
*
* General:
*
* | static method create takes nothing returns thistype
* | Default ctor.
* |
* | static method operator [] takes thistype other returns thistype
* | Copy ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
* |
* | method empty takes nothing returns boolean
* | Checks whether the list is empty.
* |
* | method size takes nothing returns integer
* | Returns size of a list.
*
*
* Access:
*
* | readonly <NAME>Item first
* | readonly <NAME>Item last
* |
* | method front takes nothing returns $TYPE$
* | Retrieves first element.
* |
* | method back takes nothing returns $TYPE$
* | Retrieves last element.
*
*
* Modifiers:
*
* | method clear takes nothing returns nothing
* | Flushes list and recycles its nodes.
* |
* | method push takes $TYPE$ value returns thistype
* | Adds elements to the end.
* |
* | method unshift takes $TYPE$ value returns thistype
* | Adds elements to the front.
* |
* | method pop takes nothing returns thistype
* | Removes the last element.
* |
* | method shift takes nothing returns thistype
* | Removes the first element.
* |
* | method find takes $TYPE$ value returns $NAME$Item
* | Returns the first node which data equals value.
* |
* | method erase takes $NAME$Item node returns boolean
* | Removes node from the list, returns true on success.
* |
* | method removeElem takes $TYPE$ value returns thistype
* | Removes first element that equals value from the list.
*
*
*****************************************************************************/
library ListT requires Table, Alloc
// Run here any global list types you want to be defined.
//! runtextmacro DEFINE_LIST("", "IntegerList", "integer")
//! textmacro_once DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// Cannot inherit methods via delegate due to limited array size
method operator data takes nothing returns $TYPE$
return IntegerListItem(this).data
endmethod
method operator data= takes $TYPE$ value returns nothing
set IntegerListItem(this).data = value
endmethod
method operator next takes nothing returns thistype
return IntegerListItem(this).next
endmethod
method operator next= takes thistype value returns nothing
set IntegerListItem(this).next = value
endmethod
method operator prev takes nothing returns thistype
return IntegerListItem(this).prev
endmethod
method operator prev= takes thistype value returns nothing
set IntegerListItem(this).prev = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
private delegate IntegerList parent
method front takes nothing returns $TYPE$
return parent.front()
endmethod
method back takes nothing returns $TYPE$
return parent.back()
endmethod
static method create takes nothing returns thistype
local thistype this = IntegerList.create()
set parent = this
return this
endmethod
endstruct
//! endtextmacro
//! textmacro_once DEFINE_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// No default ctor and dctor due to limited array size
method operator data takes nothing returns $TYPE$
return Table(this).$TYPE$[0] // hashtable[ node, 0 ] = data
endmethod
method operator data= takes $TYPE$ value returns nothing
set Table(this).$TYPE$[0] = value
endmethod
method operator next takes nothing returns thistype
return Table(this)[1] // hashtable[ node, 1 ] = next
endmethod
method operator next= takes thistype value returns nothing
set Table(this)[1] = value
endmethod
method operator prev takes nothing returns thistype
return Table(this)[-1] // hashtable[ node, -1 ] = prev
endmethod
method operator prev= takes thistype value returns nothing
set Table(this)[-1] = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
readonly $NAME$Item first
readonly $NAME$Item last
private integer count
implement Alloc
private static method setNodeOwner takes $NAME$Item node, $NAME$ owner returns nothing
set Table(node)[2] = owner
endmethod
private static method getNodeOwner takes $NAME$Item node returns thistype
return Table(node)[2]
endmethod
private method createNode takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = Table.create()
set node.data = value
call setNodeOwner(node, this) // ownership
return node
endmethod
private method deleteNode takes $NAME$Item node returns nothing
call Table(node).destroy() // also removes ownership
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set count = 0
return this
endmethod
method clear takes nothing returns nothing
local $NAME$Item node = first
local $NAME$Item temp
loop // recycle all Table indexes
exitwhen 0 == node
set temp = node.next
call deleteNode(node)
set node = temp
endloop
set first = 0
set last = 0
set count = 0
endmethod
method destroy takes nothing returns nothing
call clear()
call deallocate()
endmethod
method front takes nothing returns $TYPE$
return first.data
endmethod
method back takes nothing returns $TYPE$
return last.data
endmethod
method empty takes nothing returns boolean
return count == 0
endmethod
method size takes nothing returns integer
return count
endmethod
method push takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set last.next = node
set node.prev = last
else
set first = node
set node.prev = 0
endif
set last = node
set node.next = 0
set count = count + 1
return this
endmethod
method unshift takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set first.prev = node
set node.next = first
else
set last = node
set node.next = 0
endif
set first = node
set node.prev = 0
set count = count + 1
return this
endmethod
method pop takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = last
set last = last.prev
if last == 0 then
set first = 0
else
set last.next = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::pop failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
method shift takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = first
set first = first.next
if first == 0 then
set last = 0
else
set first.prev = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::shift failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
static method operator [] takes thistype other returns thistype
local thistype instance = create()
local $NAME$Item node = other.first
loop
exitwhen node == 0
call instance.push(node.data)
set node = node.next
endloop
return instance
endmethod
method find takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = first
loop
exitwhen node == 0 or node.data == value
set node = node.next
endloop
return node
endmethod
method erase takes $NAME$Item node returns boolean
if getNodeOwner(node) == this then // match ownership
if node == first then
call shift()
elseif node == last then
call pop()
else
set node.prev.next = node.next
set node.next.prev = node.prev
call deleteNode(node)
set count = count - 1
endif
return true
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::erase failed for instance "+I2S(this)+". Attempted to remove invalid node "+I2S(node)+".")
endif
return false
endmethod
method remove takes $NAME$Item node returns boolean
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"Method $NAME$::remove is obsolete, use $NAME$::erase instead.")
return erase(node)
endmethod
method removeElem takes $TYPE$ value returns thistype
local $NAME$Item node = find(value)
if node != 0 then
call remove(node)
endif
return this
endmethod
endstruct
//! endtextmacro
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Alloc /* v1.3.1.1
*************************************************************************************
*
* */ uses /*
*
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
* */ optional MemoryAnalysis /*
*
*************************************************************************************
*
* Minimizes code generation and global variables while maintaining
* excellent performance.
*
* local thistype this = recycler[0]
*
* if (recycler[this] == 0) then
* set recycler[0] = this + 1
* else
* set recycler[0] = recycler[this]
* endif
*
************************************************************************************
*
* module Alloc
*
* static method allocate takes nothing returns thistype
* method deallocate takes nothing returns nothing
*
* The Following Require Error Message To Be In The Map
* --------------------------------------------------------
*
* debug readonly boolean allocated
*
* The Following Require Memory Analysis To Be In The Map
* --------------------------------------------------------
*
* debug readonly integer monitorCount
* - the amount of global memory being monitored by this
* debug readonly integer monitorString
* - gets a string representation of all global memory being monitored by this
* debug readonly integer address
* - global memory address for debugging
* - used with monitor and stopMonitor
*
* debug static method calculateMemoryUsage takes nothing returns integer
* debug static method getAllocatedMemoryAsString takes nothing returns string
*
* debug method monitor takes string label, integer address returns nothing
* - monitor a global memory address with a label
* - used to identify memory leaks
* - should be memory that ought to be destroyed by the time this is destroyed
* debug method stopMonitor takes integer address returns nothing
* - stops monitoring global memory
* debug method stopMonitorValue takes handle monitoredHandle returns nothing
* - stops monitoring handle values
*
* The Following Are Used To Monitor Handle Values
*
* debug method monitor_widget takes string label, widget handleToTrack returns nothing
* debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
* debug method monitor_item takes string label, item handleToTrack returns nothing
* debug method monitor_unit takes string label, unit handleToTrack returns nothing
* debug method monitor_timer takes string label, timer handleToTrack returns nothing
* debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
* debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
* debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
* debug method monitor_force takes string label, force handleToTrack returns nothing
* debug method monitor_group takes string label, group handleToTrack returns nothing
* debug method monitor_location takes string label, location handleToTrack returns nothing
* debug method monitor_rect takes string label, rect handleToTrack returns nothing
* debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
* debug method monitor_effect takes string label, effect handleToTrack returns nothing
* debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
* debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
* debug method monitor_quest takes string label, quest handleToTrack returns nothing
* debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
* debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
* debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
* debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
* debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
* debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
* debug method monitor_button takes string label, button handleToTrack returns nothing
* debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
* debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
* debug method monitor_image takes string label, image handleToTrack returns nothing
* debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
* debug method monitor_region takes string label, region handleToTrack returns nothing
* debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
* debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
*
*
* Thanks to Ruke for the algorithm
************************************************************************************/
module Alloc
/*
* stack
*/
private static integer array recycler
static if LIBRARY_MemoryAnalysis then
debug private MemoryMonitor globalAddress
debug method operator address takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "address", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress
debug endmethod
endif
/*
* allocation
*/
static method allocate takes nothing returns thistype
local thistype this = recycler[0]
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 8192, "Alloc", "allocate", "thistype", 0, "Overflow.")
endif
if (recycler[this] == 0) then
set recycler[0] = this + 1
else
set recycler[0] = recycler[this]
endif
static if LIBRARY_ErrorMessage then
debug set recycler[this] = -1
endif
static if LIBRARY_MemoryAnalysis then
debug set globalAddress = MemoryMonitor.allocate("thistype")
endif
return this
endmethod
method deallocate takes nothing returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
endif
static if LIBRARY_MemoryAnalysis then
debug call globalAddress.deallocate()
debug set globalAddress = 0
endif
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
static if LIBRARY_MemoryAnalysis then
debug method monitor takes string label, integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor(label, address)
debug endmethod
debug method stopMonitor takes integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitor(address)
debug endmethod
debug method stopMonitorValue takes handle monitoredHandle returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitorValue", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitorValue(monitoredHandle)
debug endmethod
debug method operator monitorCount takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorCount", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorCount
debug endmethod
debug method operator monitorString takes nothing returns string
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorString", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorString
debug endmethod
debug method monitor_widget takes string label, widget handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_widget", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_widget(label, handleToTrack)
debug endmethod
debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_destructable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_destructable(label, handleToTrack)
debug endmethod
debug method monitor_item takes string label, item handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_item", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_item(label, handleToTrack)
debug endmethod
debug method monitor_unit takes string label, unit handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unit", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unit(label, handleToTrack)
debug endmethod
debug method monitor_timer takes string label, timer handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timer", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timer(label, handleToTrack)
debug endmethod
debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_trigger", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_trigger(label, handleToTrack)
debug endmethod
debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggercondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggercondition(label, handleToTrack)
debug endmethod
debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggeraction", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggeraction(label, handleToTrack)
debug endmethod
debug method monitor_force takes string label, force handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_force", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_force(label, handleToTrack)
debug endmethod
debug method monitor_group takes string label, group handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_group", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_group(label, handleToTrack)
debug endmethod
debug method monitor_location takes string label, location handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_location", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_location(label, handleToTrack)
debug endmethod
debug method monitor_rect takes string label, rect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_rect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_rect(label, handleToTrack)
debug endmethod
debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_boolexpr", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_boolexpr(label, handleToTrack)
debug endmethod
debug method monitor_effect takes string label, effect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_effect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_effect(label, handleToTrack)
debug endmethod
debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unitpool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unitpool(label, handleToTrack)
debug endmethod
debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_itempool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_itempool(label, handleToTrack)
debug endmethod
debug method monitor_quest takes string label, quest handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_quest", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_quest(label, handleToTrack)
debug endmethod
debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_defeatcondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_defeatcondition(label, handleToTrack)
debug endmethod
debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timerdialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timerdialog(label, handleToTrack)
debug endmethod
debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_leaderboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_leaderboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboarditem", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboarditem(label, handleToTrack)
debug endmethod
debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_dialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_dialog(label, handleToTrack)
debug endmethod
debug method monitor_button takes string label, button handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_button", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_button(label, handleToTrack)
debug endmethod
debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_texttag", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_texttag(label, handleToTrack)
debug endmethod
debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_lightning", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_lightning(label, handleToTrack)
debug endmethod
debug method monitor_image takes string label, image handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_image", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_image(label, handleToTrack)
debug endmethod
debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_ubersplat", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_ubersplat(label, handleToTrack)
debug endmethod
debug method monitor_region takes string label, region handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_region", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_region(label, handleToTrack)
debug endmethod
debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_fogmodifier", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_fogmodifier(label, handleToTrack)
debug endmethod
debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_hashtable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_hashtable(label, handleToTrack)
debug endmethod
static if DEBUG_MODE then
//! runtextmacro optional MEMORY_ANALYSIS_STATIC_FIELD_NEW("recycler")
static method calculateMemoryUsage takes nothing returns integer
return calculateAllocatedMemory__recycler()
endmethod
static method getAllocatedMemoryAsString takes nothing returns string
return allocatedMemoryString__recycler()
endmethod
endif
endif
/*
* analysis
*/
static if LIBRARY_ErrorMessage then
debug method operator allocated takes nothing returns boolean
debug return recycler[this] == -1
debug endmethod
endif
/*
* initialization
*/
private static method onInit takes nothing returns nothing
set recycler[0] = 1
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* RegisterNativeEvent v1.1.1.2
* by Bannar
*
* Storage of trigger handles for native events.
*
******************************************************************************
*
* Optional requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
******************************************************************************
*
* Important:
*
* Avoid using TriggerSleepAction within functions registered.
* Destroy native event trigger on your own responsibility.
*
******************************************************************************
*
* Core:
*
* function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
* Whether index whichIndex has already been attached to event whichEvent.
*
* function RegisterNativeEventTrigger takes integer whichIndex, integer eventId returns boolean
* Registers whichIndex within whichEvent scope and assigns new trigger handle for it.
*
* function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent specific to provided index whichIndex.
*
* function GetNativeEventTrigger takes integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent.
*
*
* Custom events:
*
* function CreateNativeEvent takes nothing returns integer
* Returns unique id for new event and registers it with RegisterNativeEvent.
*
* function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
* Registers new event handler func for event whichEvent specific to index whichIndex.
*
* function RegisterNativeEvent takes integer whichEvent, code func returns nothing
* Registers new event handler func for specified event whichEvent.
*
*****************************************************************************/
library RegisterNativeEvent uses optional Table
globals
private integer eventIndex = 500 // 0-499 reserved for Blizzard native events
endglobals
private module NativeEventInit
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set table = TableArray[0x2000]
endif
endmethod
endmodule
private struct NativeEvent extends array
static if LIBRARY_Table then
static TableArray table
else
static hashtable table = InitHashtable()
endif
implement NativeEventInit
endstruct
function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger.has(whichIndex)
else
return HaveSavedHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function RegisterNativeEventTrigger takes integer whichIndex, integer whichEvent returns boolean
if not IsNativeEventRegistered(whichIndex, whichEvent) then
static if LIBRARY_Table then
set NativeEvent.table[whichEvent].trigger[whichIndex] = CreateTrigger()
else
call SaveTriggerHandle(NativeEvent.table, whichEvent, whichIndex, CreateTrigger())
endif
return true
endif
return false
endfunction
function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger[whichIndex]
else
return LoadTriggerHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function GetNativeEventTrigger takes integer whichEvent returns trigger
return GetIndexNativeEventTrigger(bj_MAX_PLAYER_SLOTS, whichEvent)
endfunction
function CreateNativeEvent takes nothing returns integer
local integer eventId = eventIndex
call RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId)
set eventIndex = eventIndex + 1
return eventId
endfunction
function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
call RegisterNativeEventTrigger(whichIndex, whichEvent)
call TriggerAddCondition(GetIndexNativeEventTrigger(whichIndex, whichEvent), Condition(func))
endfunction
function RegisterNativeEvent takes integer whichEvent, code func returns nothing
call RegisterIndexNativeEvent(bj_MAX_PLAYER_SLOTS, whichEvent, func)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* RegisterPlayerUnitEvent v1.0.3.2
* by Bannar
*
* Register version of TriggerRegisterPlayerUnitEvent.
*
* Special thanks to Magtheridon96, Bribe, azlier and BBQ for the original library version.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent.
*
* function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
* Registers generic playerunitevent whichEvent adding code func as callback.
*
* function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
* Registers playerunitevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerUnitEvent requires RegisterNativeEvent
function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), whichEvent, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerUnitEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent, null)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
/*****************************************************************************
*
* RegisterPlayerEvent v1.0.2.2
* by Bannar
*
* Register version of TriggerRegisterPlayerEvent.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerEventTrigger takes playerevent whichEvent returns trigger
* Retrieves trigger handle for playerevent whichEvent.
*
* function GetPlayerEventTrigger takes player whichPlayer, playerevent whichEvent returns trigger
* Retrieves trigger handle for playerevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerEvent takes playerevent whichEvent, code func returns nothing
* Registers generic playerevent whichEvent adding code func as callback.
*
* function RegisterPlayerEvent takes player whichPlayer, playerevent whichEvent, code func returns nothing
* Registers playerevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerEvent requires RegisterNativeEvent
function GetAnyPlayerEventTrigger takes playerevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerEventTrigger takes player whichPlayer, playerevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerEvent takes playerevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerEvent(t, Player(index), whichEvent)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerEvent takes player whichPlayer, playerevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
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 RegisterAnyPlayerUnitEvent(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
//============================================================================
// OrderEvent by Bribe, special thanks to Nestharus and Azlier, version 3.0.1.1
//
// API
// ---
// RegisterOrderEvent(integer orderId, code eventFunc)
// RegisterAnyOrderEvent(code eventFunc) //Runs for point/target/instant for any order
//
// Requires
// --------
// RegisterPlayerUnitEvent: http://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
// Table: http://www.hiveworkshop.com/forums/showthread.php?t=188084
//
library OrderEvent requires RegisterPlayerUnitEvent, Table
globals
private Table t = 0
endglobals
//============================================================================
function RegisterAnyOrderEvent takes code c returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, c)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, c)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, c)
endfunction
//============================================================================
private function OnOrder takes nothing returns nothing
call TriggerEvaluate(t.trigger[GetIssuedOrderId()])
endfunction
//============================================================================
function RegisterOrderEvent takes integer orderId, code c returns nothing
local trigger trig
if integer(t) == 0 then
set t = Table.create()
call RegisterAnyOrderEvent(function OnOrder)
endif
set trig = t.trigger[orderId]
if trig == null then
set trig = CreateTrigger()
set t.trigger[orderId] = trig
endif
call TriggerAddCondition(trig, Filter(c))
set trig = null
endfunction
endlibrary
library BlizzardMessage /* v1.0.0.4
*************************************************************************************
*
* For creating Blizzard-like messages with a variety of sounds.
*
*************************************************************************************
*
* Credits
*
* Vexorian
* -----------------------
*
* The original SimError
*
*
* Maker
* -----------------------
*
* Sound label help
*
************************************************************************************
*
* Function
* -----------------------
*
* function BlizzardMessage takes string msg, string colorCode, integer soundLabel, player forPlayer returns nothing
*
* Description
* -----------------------
*
* This function creates a Blizzard-like message with a sound for a player.
*
* Takes
* -----------------------
*
* string msg
* - The message to be displayed.
*
* string colorCode
* - Adds a color for the message through an ARBG color code. A null value creates a normal white text.
*
* integer soundLabel
* - The sound to be played.
*
* player forPlayer
* - The player that receives the message.
*
* Returns
* -----------------------
*
* nothing
*
*************************************************************************************
*
* Function
* -----------------------
*
* function BlizzardMessageTimed takes string msg, string colorCode, real dur, integer soundLabel, player forPlayer returns nothing
*
* Description
* -----------------------
*
* This function creates a timed Blizzard-like message with a sound for a player.
*
* Takes
* -----------------------
*
* string msg
* - The message to be displayed.
*
* string colorCode
* - Adds a color for the message through an ARBG color code. A null value creates a normal white text.
*
* real dur
* - The duration of the message.
*
* integer soundLabel
* - The sound to be played.
*
* player forPlayer
* - The player that receives the message.
*
* Returns
* -----------------------
*
* nothing
*
*************************************************************************************
*
* SETTINGS
* -----------------------
*
*/
globals
constant real MSG_DURATION = 2.00 //Message Duration
/*************************************************************************************
*
* Sound Labels
*
*************************************************************************************/
constant integer ALLY_HERO_DIES_HUMAN = 0
constant integer ALLY_HERO_DIES_NAGA = 1
constant integer ALLY_HERO_DIES_NIGHTELF = 2
constant integer ALLY_HERO_DIES_ORC = 3
constant integer ALLY_HERO_DIES_UNDEAD = 4
constant integer ALLY_TOWN_UNDER_ATTACK_HUMAN = 5
constant integer ALLY_TOWN_UNDER_ATTACK_NAGA = 6
constant integer ALLY_TOWN_UNDER_ATTACK_NIGHTELF = 7
constant integer ALLY_TOWN_UNDER_ATTACK_ORC = 8
constant integer ALLY_TOWN_UNDER_ATTACK_UNDEAD = 9
constant integer ALLY_UNDER_ATTACK_HUMAN = 10
constant integer ALLY_UNDER_ATTACK_NAGA = 11
constant integer ALLY_UNDER_ATTACK_NIGHTELF = 12
constant integer ALLY_UNDER_ATTACK_ORC = 13
constant integer ALLY_UNDER_ATTACK_UNDEAD = 14
constant integer ARRANGED_TEAM_INVITATION = 15
constant integer AUTO_CAST_BUTTON_CLICK = 16
constant integer CANT_PLACE_HUMAN = 17
constant integer CANT_PLACE_NAGA = 18
constant integer CANT_PLACE_NIGHTELF = 19
constant integer CANT_PLACE_ORC = 20
constant integer CANT_PLACE_UNDEAD = 21
constant integer CANT_ROOT_NIGHTELF = 22
constant integer CHATROOM_TIMER_TICK = 23
constant integer CLAN_INVITATION = 24
constant integer CONSTRUCTING_BUILDING_DEFAULT = 25
constant integer CONSTRUCTING_BUILDING_NAGA = 26
constant integer CONSTRUCTING_BUILDING_NIGHTELF = 27
constant integer CONSTRUCTING_BUILDING_ORC = 28
constant integer CONSTRUCTING_BUILDING_UNDEAD = 29
constant integer CREEP_AGGRO = 30
constant integer ERROR_MESSAGE = 31
constant integer GAME_FOUND = 32
constant integer GLUE_SCREEN_CLICK = 33
constant integer GOLD_MINE_COLLAPSE_HUMAN = 34
constant integer GOLD_MINE_COLLAPSE_NAGA = 35
constant integer GOLD_MINE_COLLAPSE_NIGHTELF = 36
constant integer GOLD_MINE_COLLAPSE_ORC = 37
constant integer GOLD_MINE_COLLAPSE_UNDEAD = 38
constant integer GOLD_MINE_LOW_GENERIC = 39
constant integer GOLD_MINE_LOW_HUMAN = 40
constant integer GOLD_MINE_LOW_NAGA = 41
constant integer GOLD_MINE_LOW_NIGHTELF = 42
constant integer GOLD_MINE_LOW_ORC = 43
constant integer GOLD_MINE_LOW_UNDEAD = 44
constant integer GOOD_JOB = 45
constant integer HERO_DIES_GENERIC = 46
constant integer HERO_DIES_HUMAN = 47
constant integer HERO_DIES_NAGA = 48
constant integer HERO_DIES_NIGHTELF = 49
constant integer HERO_DIES_ORC = 50
constant integer HERO_DIES_UNDEAD = 51
constant integer HINT = 52
constant integer IN_GAME_CHAT_WHAT = 53
constant integer INTERFACE_CLICK = 54
constant integer INTERFACE_ERROR = 55
constant integer INVENTORY_FULL_HUMAN = 56
constant integer INVENTORY_FULL_NAGA = 57
constant integer INVENTORY_FULL_NIGHTELF = 58
constant integer INVENTORY_FULL_ORC = 59
constant integer INVENTORY_FULL_UNDEAD = 60
constant integer ITEM_DROP = 61
constant integer ITEM_GET = 62
constant integer ITEM_REWARD = 63
constant integer JOB_DONE_SOUND_HUMAN = 64
constant integer JOB_DONE_SOUND_NAGA = 65
constant integer JOB_DONE_SOUND_NIGHTELF = 66
constant integer JOB_DONE_SOUND_ORC = 67
constant integer JOB_DONE_SOUND_UNDEAD = 68
constant integer MAP_PING = 69
constant integer MENU_BUTTON_CLICK = 70
constant integer NEW_TOURNAMENT = 71
constant integer NO_FOOD_HUMAN = 72
constant integer NO_FOOD_NAGA = 73
constant integer NO_FOOD_NIGHTELF = 74
constant integer NO_FOOD_ORC = 75
constant integer NO_FOOD_UNDEAD = 76
constant integer NO_GOLD_GENERIC = 77
constant integer NO_GOLD_HUMAN = 78
constant integer NO_GOLD_NAGA = 79
constant integer NO_GOLD_NIGHTELF = 80
constant integer NO_GOLD_ORC = 81
constant integer NO_GOLD_UNDEAD = 82
constant integer NO_LUMBER_HUMAN = 83
constant integer NO_LUMBER_NAGA = 84
constant integer NO_LUMBER_NIGHTELF = 85
constant integer NO_LUMBER_ORC = 86
constant integer NO_LUMBER_UNDEAD = 87
constant integer NO_MANA_GENERIC = 88
constant integer NO_MANA_HUMAN = 89
constant integer NO_MANA_NAGA = 90
constant integer NO_MANA_NIGHTELF = 91
constant integer NO_MANA_ORC = 92
constant integer NO_MANA_UNDEAD = 93
constant integer OFF_BLIGHT_UNDEAD = 94
constant integer PAUSE_GAME = 95
constant integer PLACE_BUILDING_DEFAULT = 96
constant integer QUEST_COMPLETED = 97
constant integer QUEST_FAILED = 98
constant integer QUEST_LOG_MODIFIED = 99
constant integer QUEST_NEW = 100
constant integer QUEST_UPDATE = 101
constant integer RALLY_POINT_PLACE = 102
constant integer RESCUE = 103
constant integer RESEARCH_COMPLETE_GENERIC = 104
constant integer RESEARCH_COMPLETE_HUMAN = 105
constant integer RESEARCH_COMPLETE_NAGA = 106
constant integer RESEARCH_COMPLETE_NIGHTELF = 107
constant integer RESEARCH_COMPLETE_ORC = 108
constant integer RESEARCH_COMPLETE_UNDEAD = 109
constant integer SCORE_SCREEN_TAB_CLICK = 110
constant integer SECRET_FOUND = 111
constant integer SUB_GROUP_SELECTION_CHANGE = 112
constant integer TOWN_ATTACK_GENERIC = 113
constant integer TOWN_ATTACK_HUMAN = 114
constant integer TOWN_ATTACK_NAGA = 115
constant integer TOWN_ATTACK_NIGHTELF = 116
constant integer TOWN_ATTACK_ORC = 117
constant integer TOWN_ATTACK_UNDEAD = 118
constant integer UNDER_ATTACK_HUMAN = 119
constant integer UNDER_ATTACK_NAGA = 120
constant integer UNDER_ATTACK_NIGHTELF = 121
constant integer UNDER_ATTACK_ORC = 122
constant integer UNDER_ATTACK_UNDEAD = 123
constant integer UPGRADE_COMPLETE_GENERIC = 124
constant integer UPGRADE_COMPLETE_HUMAN = 125
constant integer UPGRADE_COMPLETE_NAGA = 126
constant integer UPGRADE_COMPLETE_NIGHTELF = 127
constant integer UPGRADE_COMPLETE_ORC = 128
constant integer UPGRADE_COMPLETE_UNDEAD = 129
constant integer UPKEEP_LEVEL = 130
constant integer WARNING = 131
constant integer WAYPOINT = 132
/* private */ string array SOUND_LABEL
endglobals
/*
************************************************************************************
*/
function BlizzardMessage takes string msg, string colorCode, integer soundLabel, player forPlayer returns nothing
local sound snd
local string s
debug if soundLabel >= 0 and soundLabel <= 132 and forPlayer != null then
set snd = CreateSoundFromLabel (SOUND_LABEL[soundLabel], false, false, false, 10, 10)
if colorCode != null then
set s = "|r"
else
set colorCode = ""
set s = ""
endif
if GetLocalPlayer () == forPlayer then
call ClearTextMessages ()
call DisplayTimedTextToPlayer (forPlayer, 0.52, 0.96, MSG_DURATION, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + colorCode + msg + s)
call StartSound (snd)
endif
if s != null then
set s = ""
endif
set snd = null
debug else
debug if msg == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null message")
debug endif
debug if forPlayer == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null forPlayer")
debug endif
debug if soundLabel < 0 or soundLabel > 132
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Invalid sound label")
debug endif
debug endif
endfunction
function BlizzardMessageTimed takes string msg, string colorCode, real dur, integer soundLabel, player forPlayer returns nothing
local sound snd
local string s
debug if soundLabel >= 0 and soundLabel <= 132 and forPlayer != null then
set snd = CreateSoundFromLabel (SOUND_LABEL[soundLabel], false, false, false, 10, 10)
if colorCode != null then
set s = "|r"
else
set colorCode = ""
set s = ""
endif
if GetLocalPlayer () == forPlayer then
call ClearTextMessages ()
call DisplayTimedTextToPlayer (forPlayer, 0.52, 0.96, dur, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + colorCode + msg + s)
call StartSound (snd)
endif
if s != null then
set s = ""
endif
set snd = null
debug else
debug if msg == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null message")
debug endif
debug if forPlayer == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null forPlayer")
debug endif
debug if soundLabel < 0 or soundLabel > 132
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Invalid sound label")
debug endif
debug endif
endfunction
private module BMInit
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
private struct InitStruct extends array
private static method init takes nothing returns nothing
set SOUND_LABEL[0] = "AllyHeroDiesHuman"
set SOUND_LABEL[1] = "AllyHeroDiesNaga"
set SOUND_LABEL[2] = "AllyHeroDiesNightElf"
set SOUND_LABEL[3] = "AllyHeroDiesOrc"
set SOUND_LABEL[4] = "AllyHeroDiesUndead"
set SOUND_LABEL[5] = "AllyTownUnderAttackHuman"
set SOUND_LABEL[6] = "AllyTownUnderAttackNaga"
set SOUND_LABEL[7] = "AllyTownUnderAttackNightElf"
set SOUND_LABEL[8] = "AllyTownUnderAttackOrc"
set SOUND_LABEL[9] = "AllyTownUnderAttackUndead"
set SOUND_LABEL[10] = "AllyUnderAttackHuman"
set SOUND_LABEL[11] = "AllyUnderAttackNaga"
set SOUND_LABEL[12] = "AllyUnderAttackNightElf"
set SOUND_LABEL[13] = "AllyUnderAttackOrc"
set SOUND_LABEL[14] = "AllyUnderAttackUndead"
set SOUND_LABEL[15] = "ArrangedTeamInvitation"
set SOUND_LABEL[16] = "AutoCastButtonClick"
set SOUND_LABEL[17] = "CantPlaceHuman"
set SOUND_LABEL[18] = "CantPlaceNaga"
set SOUND_LABEL[19] = "CantPlaceNightElf"
set SOUND_LABEL[20] = "CantPlaceOrc"
set SOUND_LABEL[21] = "CantPlaceUndead"
set SOUND_LABEL[22] = "CantRootNightElf"
set SOUND_LABEL[23] = "ChatroomTimerTick"
set SOUND_LABEL[24] = "ClanInvitation"
set SOUND_LABEL[25] = "ConstructingBuildingDefault"
set SOUND_LABEL[26] = "ConstructingBuildingNaga"
set SOUND_LABEL[27] = "ConstructingBuildingNightElf"
set SOUND_LABEL[28] = "ConstructingBuildingOrc"
set SOUND_LABEL[29] = "ConstructingBuildingUndead"
set SOUND_LABEL[30] = "CreepAggro"
set SOUND_LABEL[31] = "ErrorMessage"
set SOUND_LABEL[32] = "GameFound"
set SOUND_LABEL[33] = "GlueScreenClick"
set SOUND_LABEL[34] = "GoldMineCollapseHuman"
set SOUND_LABEL[35] = "GoldMineCollapseNaga"
set SOUND_LABEL[36] = "GoldMineCollapseNightElf"
set SOUND_LABEL[37] = "GoldMineCollapseOrc"
set SOUND_LABEL[38] = "GoldMineCollapseUndead"
set SOUND_LABEL[39] = "GoldMineLowGeneric"
set SOUND_LABEL[40] = "GoldMineLowHuman"
set SOUND_LABEL[41] = "GoldMineLowNaga"
set SOUND_LABEL[42] = "GoldMineLowNightElf"
set SOUND_LABEL[43] = "GoldMineLowOrc"
set SOUND_LABEL[44] = "GoldMineLowUndead"
set SOUND_LABEL[45] = "GoodJob"
set SOUND_LABEL[46] = "HeroDiesGeneric"
set SOUND_LABEL[47] = "HeroDiesHuman"
set SOUND_LABEL[48] = "HeroDiesNaga"
set SOUND_LABEL[49] = "HeroDiesNightElf"
set SOUND_LABEL[50] = "HeroDiesOrc"
set SOUND_LABEL[51] = "HeroDiesUndead"
set SOUND_LABEL[52] = "Hint"
set SOUND_LABEL[53] = "InGameChatWhat"
set SOUND_LABEL[54] = "InterfaceClick"
set SOUND_LABEL[55] = "InterfaceError"
set SOUND_LABEL[56] = "InventoryFullHuman"
set SOUND_LABEL[57] = "InventoryFullNaga"
set SOUND_LABEL[58] = "InventoryFullNightElf"
set SOUND_LABEL[59] = "InventoryFullOrc"
set SOUND_LABEL[60] = "InventoryFullUndead"
set SOUND_LABEL[61] = "ItemDrop"
set SOUND_LABEL[62] = "ItemGet"
set SOUND_LABEL[63] = "ItemReward"
set SOUND_LABEL[64] = "JobDoneSoundHuman"
set SOUND_LABEL[65] = "JobDoneSoundNaga"
set SOUND_LABEL[66] = "JobDoneSoundNightElf"
set SOUND_LABEL[67] = "JobDoneSoundOrc"
set SOUND_LABEL[68] = "JobDoneSoundUndead"
set SOUND_LABEL[69] = "MapPing"
set SOUND_LABEL[70] = "MenuButtonClick"
set SOUND_LABEL[71] = "NewTournament"
set SOUND_LABEL[72] = "NoFoodHuman"
set SOUND_LABEL[73] = "NoFoodNaga"
set SOUND_LABEL[74] = "NoFoodNightElf"
set SOUND_LABEL[75] = "NoFoodOrc"
set SOUND_LABEL[76] = "NoFoodUndead"
set SOUND_LABEL[77] = "NoGoldGeneric"
set SOUND_LABEL[78] = "NoGoldHuman"
set SOUND_LABEL[79] = "NoGoldNaga"
set SOUND_LABEL[80] = "NoGoldNightElf"
set SOUND_LABEL[81] = "NoGoldOrc"
set SOUND_LABEL[82] = "NoGoldUndead"
set SOUND_LABEL[83] = "NoLumberHuman"
set SOUND_LABEL[84] = "NoLumberNaga"
set SOUND_LABEL[85] = "NoLumberNightElf"
set SOUND_LABEL[86] = "NoLumberOrc"
set SOUND_LABEL[87] = "NoLumberUndead"
set SOUND_LABEL[88] = "NoManaGeneric"
set SOUND_LABEL[89] = "NoManaHuman"
set SOUND_LABEL[90] = "NoManaNaga"
set SOUND_LABEL[91] = "NoManaNightElf"
set SOUND_LABEL[92] = "NoManaOrc"
set SOUND_LABEL[93] = "NoManaUndead"
set SOUND_LABEL[94] = "OffBlightUndead"
set SOUND_LABEL[95] = "PauseGame"
set SOUND_LABEL[96] = "PlaceBuildingDefault"
set SOUND_LABEL[97] = "QuestCompleted"
set SOUND_LABEL[98] = "QuestFailed"
set SOUND_LABEL[99] = "QuestLogModified"
set SOUND_LABEL[100] = "QuestNew"
set SOUND_LABEL[101] = "QuestUpdate"
set SOUND_LABEL[102] = "RallyPointPlace"
set SOUND_LABEL[103] = "Rescue"
set SOUND_LABEL[104] = "ResearchCompleteGeneric"
set SOUND_LABEL[105] = "ResearchCompleteHuman"
set SOUND_LABEL[106] = "ResearchCompleteNaga"
set SOUND_LABEL[107] = "ResearchCompleteNightElf"
set SOUND_LABEL[108] = "ResearchCompleteOrc"
set SOUND_LABEL[109] = "ResearchCompleteUndead"
set SOUND_LABEL[110] = "ScoreScreenTabClick"
set SOUND_LABEL[111] = "SecretFound"
set SOUND_LABEL[112] = "SubGroupSelectionChange"
set SOUND_LABEL[113] = "TownAttackGeneric"
set SOUND_LABEL[114] = "TownAttackHuman"
set SOUND_LABEL[115] = "TownAttackNaga"
set SOUND_LABEL[116] = "TownAttackNightElf"
set SOUND_LABEL[117] = "TownAttackOrc"
set SOUND_LABEL[118] = "TownAttackUndead"
set SOUND_LABEL[119] = "UnderAttackHuman"
set SOUND_LABEL[120] = "UnderAttackNaga"
set SOUND_LABEL[121] = "UnderAttackNightElf"
set SOUND_LABEL[122] = "UnderAttackOrc"
set SOUND_LABEL[123] = "UnderAttackUndead"
set SOUND_LABEL[124] = "UpgradeCompleteGeneric"
set SOUND_LABEL[125] = "UpgradeCompleteHuman"
set SOUND_LABEL[126] = "UpgradeCompleteNaga"
set SOUND_LABEL[127] = "UpgradeCompleteNightElf"
set SOUND_LABEL[128] = "UpgradeCompleteOrc"
set SOUND_LABEL[129] = "UpgradeCompleteUndead"
set SOUND_LABEL[130] = "UpkeepLevel"
set SOUND_LABEL[131] = "Warning"
set SOUND_LABEL[132] = "WayPoint"
endmethod
implement BMInit
endstruct
endlibrary
//TESH.scrollpos=0
//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 = false
private constant boolean USE_FLEXIBLE_OFFSET = true
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
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DummyUtils uses DummyRecycler
globals
private unit DUMMY = null
endglobals
function DummyCaster takes real x, real y, real z, player p returns unit
set DUMMY = GetRecycledDummyAnyAngle(x, y, z)
call PauseUnit(DUMMY, false)
call SetUnitOwner(DUMMY, p, true)
return DUMMY
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SpellUtils
function HasAbility takes unit u, integer id returns boolean
return GetUnitAbilityLevel(u, id) > 0
endfunction
function IsResearched takes player p, integer id returns boolean
return GetPlayerTechCount(p, id, true) > 0
endfunction
function HasChance takes real chance returns boolean
return GetRandomInt(1, 100) <= chance
endfunction
function IsAlive takes unit u returns boolean
return GetWidgetLife(u) > 0.41
endfunction
function UnitHasMana takes unit u returns boolean
return GetUnitState(u, UNIT_STATE_MAX_MANA) > 0
endfunction
function CreateEffectOnUnit takes string fx, unit u, string attach returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx, u, attach))
endfunction
function CreateEffectOnLoc takes string fx, location loc returns nothing
call DestroyEffect(AddSpecialEffectLoc(fx, loc))
endfunction
function Distance takes real aX, real aY, real bX, real bY returns real
local real dx = bX - aX
local real dy = bY - aY
return SquareRoot(dx * dx + dy * dy)
endfunction
function IsPointWalkable takes real x, real y, real tollerance returns boolean
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hfoo', x, y, 0)
local real dist = (GetUnitX(u) - x)*(GetUnitX(u) - x) + (GetUnitY(u) - y)*(GetUnitY(u) - y)
call RemoveUnit(u)
set u = null
return dist < tollerance*tollerance
endfunction
function NextWalkablePoint takes real x, real y returns location
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hfoo', x, y, 0)
local location loc = GetUnitLoc(u)
call RemoveUnit(u)
set u = null
return loc
endfunction
function IsHero takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_HERO)
endfunction
function IsMechanical takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_MECHANICAL)
endfunction
function IsBuilding takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
function IsWorker takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_PEON)
endfunction
function IsMelee takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_MELEE_ATTACKER)
endfunction
function IsDead takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_DEAD)
endfunction
function IsFlying takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
function IsSummoned takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_SUMMONED)
endfunction
function IsMagicImmune takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils requires SpellUtils
globals
private constant real MAX_COLLISION = 64.
private real tempX = 0.
private real tempY = 0.
private real tempRange = 0.
endglobals
function FilterInRange takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), tempX, tempY, tempRange)
endfunction
function AliveFilter takes nothing returns boolean
return IsAlive(GetFilterUnit()) and not IsBuilding(GetFilterUnit()) and GetUnitTypeId(GetFilterUnit()) != 'npgr'
endfunction
function GroupEnumUnitsWithCollision takes group g, real x, real y, real radius returns group
set tempX = x
set tempY = y
set tempRange = radius
call GroupEnumUnitsInRange(g, x, y, radius+MAX_COLLISION, Filter(function FilterInRange))
return g
endfunction
function GroupEnumLivingUnitsOfPlayer takes group g, player p returns group
call GroupEnumUnitsOfPlayer(g, p, Filter(function AliveFilter))
return g
endfunction
function CopyGroup takes group g returns group
set bj_groupAddGroupDest = CreateGroup()
call ForGroup(g, function GroupAddGroupEnum)
return bj_groupAddGroupDest
endfunction
function GroupCountUnits takes group g returns integer
local integer count = 0
local group tempG = CopyGroup(g)
local unit u
loop
set u = FirstOfGroup(tempG)
exitwhen u == null
set count = count + 1
call GroupRemoveUnit(tempG, u)
set u = null
endloop
call DestroyGroup(tempG)
set tempG = null
return count
endfunction
function GroupRandomUnit takes group g returns unit
local integer t = 0
local integer curmax = 0
local unit chosen = null
local group tempG = CopyGroup(g)
local unit u
loop
set u = FirstOfGroup(tempG)
exitwhen u == null
if IsAlive(u) then
set t = GetRandomInt(1,100)
if t > curmax then
set curmax = t
set chosen = u
endif
endif
call GroupRemoveUnit(tempG, u)
set u = null
endloop
return chosen
endfunction
endlibrary
//===========================================================================
// Damage Engine lets you detect, amplify, block or nullify damage. It even
// lets you detect if the damage was physical or from a spell. Just reference
// DamageEventAmount/Source/Target or the boolean IsDamageSpell, to get the
// necessary damage event data.
//
// - Detect damage: use the event "DamageEvent Equal to 1.00"
// - To change damage before it's dealt: use the event "DamageModifierEvent Equal to 1.00"
// - Detect damage after it was applied, use the event "AfterDamageEvent Equal to 1.00"
// - Detect spell damage: use the condition "IsDamageSpell Equal to True"
// - Detect zero-damage: use the event "DamageEvent Equal to 2.00" (an AfterDamageEvent will not fire for this)
//
// You can specify the DamageEventType before dealing triggered damage. To prevent an already-improbable error, I recommend running the trigger "ClearDamageEvent (Checking Conditions)" after dealing triggered damage from within a damage event:
// - Set NextDamageType = DamageTypeWhatever
// - Unit - Cause...
// - Trigger - Run ClearDamageEvent (Checking Conditions)
//
// You can modify the DamageEventAmount and the DamageEventType from a "DamageModifierEvent Equal to 1.00" trigger.
// - If the amount is modified to negative, it will count as a heal.
// - If the amount is set to 0, no damage will be dealt.
//
// If you need to reference the original in-game damage, use the variable "DamageEventPrevAmt".
//
//===========================================================================
// Programming note about "integer i" and "udg_DmgEvRecursionN": integer i
// ranges from -1 upwards. "udg_DmgEvRecursionN" ranges from 0 upwards.
// "integer i" is always 1 less than "udg_DmgEvRecursionN"
//
function DmgEvResetVars takes nothing returns nothing
local integer i = udg_DmgEvRecursionN - 2
set udg_DmgEvRecursionN = i + 1
if i >= 0 then
set udg_DamageEventPrevAmt = udg_LastDmgPrevAmount[i]
set udg_DamageEventAmount = udg_LastDmgValue[i]
set udg_DamageEventSource = udg_LastDmgSource[i]
set udg_DamageEventTarget = udg_LastDmgTarget[i]
set udg_IsDamageSpell = udg_LastDmgWasSpell[i]
set udg_DamageEventType = udg_LastDmgPrevType[i]
endif
endfunction
function CheckDamagedLifeEvent takes boolean clear returns nothing
if clear then
set udg_NextDamageOverride = false
set udg_NextDamageType = 0
endif
if udg_DmgEvTrig != null then
call DestroyTrigger(udg_DmgEvTrig)
set udg_DmgEvTrig = null
if udg_IsDamageSpell then
call SetWidgetLife(udg_DamageEventTarget, RMaxBJ(udg_LastDamageHP, 0.41))
if udg_LastDamageHP <= 0.405 then
if udg_DamageEventType < 0 then
call SetUnitExploded(udg_DamageEventTarget, true)
endif
//Kill the unit
call DisableTrigger(udg_DamageEventTrigger)
call UnitDamageTarget(udg_DamageEventSource, udg_DamageEventTarget, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
call EnableTrigger(udg_DamageEventTrigger)
endif
elseif GetUnitAbilityLevel(udg_DamageEventTarget, udg_DamageBlockingAbility) > 0 then
call UnitRemoveAbility(udg_DamageEventTarget, udg_DamageBlockingAbility)
call SetWidgetLife(udg_DamageEventTarget, udg_LastDamageHP)
endif
if udg_DamageEventAmount != 0.00 and not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_AfterDamageEvent = 0.00
set udg_AfterDamageEvent = 1.00
set udg_AfterDamageEvent = 0.00
endif
call DmgEvResetVars()
endif
endfunction
function DmgEvOnAOEEnd takes nothing returns nothing
if udg_DamageEventAOE > 1 then
set udg_AOEDamageEvent = 0.00
set udg_AOEDamageEvent = 1.00
set udg_AOEDamageEvent = 0.00
set udg_DamageEventAOE = 1
endif
set udg_DamageEventLevel = 1
set udg_EnhancedDamageTarget = null
call GroupClear(udg_DamageEventAOEGroup)
endfunction
function DmgEvOnExpire takes nothing returns nothing
set udg_DmgEvStarted = false
call CheckDamagedLifeEvent(true)
//Reset things so they don't perpetuate for AoE/Level target detection
call DmgEvOnAOEEnd()
set udg_DamageEventTarget = null
set udg_DamageEventSource = null
endfunction
function PreCheckDamagedLifeEvent takes nothing returns boolean
call CheckDamagedLifeEvent(true)
return false
endfunction
function OnUnitDamage takes nothing returns boolean
local boolean override = udg_DamageEventOverride
local integer i
local integer e = udg_DamageEventLevel
local integer a = udg_DamageEventAOE
local string s
local real prevAmount
local real life
local real prevLife
local unit u
local unit f
call CheckDamagedLifeEvent(false) //in case the unit state event failed and the 0.00 second timer hasn't yet expired
set i = udg_DmgEvRecursionN - 1 //Had to be moved here due to false recursion tracking
if i < 0 then
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
set u = udg_DamageEventTarget
set f = udg_DamageEventSource
elseif i < 16 then
set udg_LastDmgPrevAmount[i]= udg_DamageEventPrevAmt
set udg_LastDmgValue[i] = udg_DamageEventAmount
set udg_LastDmgSource[i] = udg_DamageEventSource
set udg_LastDmgTarget[i] = udg_DamageEventTarget
set udg_LastDmgWasSpell[i] = udg_IsDamageSpell
set udg_LastDmgPrevType[i] = udg_DamageEventType
else
set s = "WARNING: Recursion error when dealing damage! Make sure when you deal damage from within a DamageEvent trigger, do it like this:\n\n"
set s = s + "Trigger - Turn off (This Trigger)\n"
set s = s + "Unit - Cause...\n"
set s = s + "Trigger - Turn on (This Trigger)"
//Delete the next couple of lines to disable the in-game recursion crash warnings
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 999.00, s)
return false
endif
set udg_DmgEvRecursionN = i + 2
set prevAmount = GetEventDamage()
set udg_DamageEventTarget = GetTriggerUnit()
set udg_DamageEventSource = GetEventDamageSource()
set udg_DamageEventAmount = prevAmount
set udg_DamageEventType = udg_NextDamageType
set udg_NextDamageType = 0
set udg_DamageEventOverride = udg_NextDamageOverride
set udg_NextDamageOverride = false
if i < 0 then
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
if udg_DamageEventType == 0 then
if f == udg_DamageEventSource then
//Source has damaged more than once
if IsUnitInGroup(udg_DamageEventTarget, udg_DamageEventAOEGroup) then
//Added 5 August 2017 to improve tracking of enhanced damage against, say, Pulverize
set udg_DamageEventLevel = udg_DamageEventLevel + 1
set udg_EnhancedDamageTarget = udg_DamageEventTarget
else
//Multiple targets hit by this source - flag as AOE
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
else
//New damage source - unflag everything
set u = udg_DamageEventSource
set udg_DamageEventSource = f
call DmgEvOnAOEEnd()
set udg_DamageEventSource = u
endif
call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
endif
if not udg_DmgEvStarted then
set udg_DmgEvStarted = true
call TimerStart(udg_DmgEvTimer, 0.00, false, function DmgEvOnExpire)
endif
endif
if prevAmount == 0.00 then
if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_DamageEventPrevAmt = 0.00
set udg_DamageEvent = 0.00
set udg_DamageEvent = 2.00
set udg_DamageEvent = 0.00
endif
call DmgEvResetVars()
else
set u = udg_DamageEventTarget
set udg_IsDamageSpell = prevAmount < 0.00
if udg_IsDamageSpell then
set prevAmount = -udg_DamageEventAmount
set life = 1.00
if IsUnitType(u, UNIT_TYPE_ETHEREAL) and not IsUnitType(u, UNIT_TYPE_HERO) then
set life = life*udg_DAMAGE_FACTOR_ETHEREAL //1.67
endif
if GetUnitAbilityLevel(u, 'Aegr') > 0 then
set life = life*udg_DAMAGE_FACTOR_ELUNES //0.80
endif
if udg_DmgEvBracers != 0 and IsUnitType(u, UNIT_TYPE_HERO) then
//Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
set i = 6
loop
set i = i - 1
if GetItemTypeId(UnitItemInSlot(u, i)) == udg_DmgEvBracers then
set life = life*udg_DAMAGE_FACTOR_BRACERS //0.67
exitwhen true
endif
exitwhen i == 0
endloop
endif
set udg_DamageEventAmount = prevAmount*life
endif
set udg_DamageEventPrevAmt = prevAmount
set udg_DamageModifierEvent = 0.00
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 1.00
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 2.00
set udg_DamageModifierEvent = 3.00
endif
endif
set udg_DamageEventOverride = override
if udg_DamageEventAmount > 0.00 then
set udg_DamageModifierEvent = 4.00
endif
set udg_DamageModifierEvent = 0.00
if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_DamageEvent = 0.00
set udg_DamageEvent = 1.00
set udg_DamageEvent = 0.00
endif
call CheckDamagedLifeEvent(true) //in case the unit state event failed from a recursive damage event
//All events have run and the damage amount is finalized.
set life = GetWidgetLife(u)
set udg_DmgEvTrig = CreateTrigger()
call TriggerAddCondition(udg_DmgEvTrig, Filter(function PreCheckDamagedLifeEvent))
if not udg_IsDamageSpell then
if udg_DamageEventAmount != prevAmount then
set life = life + prevAmount - udg_DamageEventAmount
if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
set udg_LastDamageHP = life - prevAmount
call UnitAddAbility(u, udg_DamageBlockingAbility)
endif
call SetWidgetLife(u, RMaxBJ(life, 0.42))
endif
call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
else
set udg_LastDamageHP = GetUnitState(u, UNIT_STATE_MAX_LIFE)
set prevLife = life
if life + prevAmount*0.75 > udg_LastDamageHP then
set life = RMaxBJ(udg_LastDamageHP - prevAmount/2.00, 1.00)
call SetWidgetLife(u, life)
set life = (life + udg_LastDamageHP)/2.00
else
set life = life + prevAmount*0.50
endif
set udg_LastDamageHP = prevLife - (prevAmount - (prevAmount - udg_DamageEventAmount))
call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, GREATER_THAN, life)
endif
endif
set u = null
set f = null
return false
endfunction
function CreateDmgEvTrg takes nothing returns nothing
set udg_DamageEventTrigger = CreateTrigger()
call TriggerAddCondition(udg_DamageEventTrigger, Filter(function OnUnitDamage))
endfunction
function SetupDmgEv takes nothing returns boolean
local integer i = udg_UDex
local unit u
if udg_UnitIndexEvent == 1.00 then
set u = udg_UDexUnits[i]
if GetUnitAbilityLevel(u, 'Aloc') == 0 and TriggerEvaluate(gg_trg_Damage_Engine_Config) then
set udg_UnitDamageRegistered[i] = true
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
call UnitAddAbility(u, udg_SpellDamageAbility)
call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
endif
set u = null
else
set udg_HideDamageFrom[i] = false
if udg_UnitDamageRegistered[i] then
set udg_UnitDamageRegistered[i] = false
set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
set udg_DamageEventsWasted = 0
//Rebuild the mass EVENT_UNIT_DAMAGED trigger:
call DestroyTrigger(udg_DamageEventTrigger)
call CreateDmgEvTrg()
set i = udg_UDexNext[0]
loop
exitwhen i == 0
if udg_UnitDamageRegistered[i] then
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
endif
set i = udg_UDexNext[i]
endloop
endif
endif
endif
return false
endfunction
//===========================================================================
function InitTrig_Damage_Engine takes nothing returns nothing
local unit u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), 'uloc', 0, 0, 0)
local integer i = bj_MAX_PLAYERS //Fixed in 3.8
//Create this trigger with UnitIndexEvents in order add and remove units
//as they are created or removed.
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Filter(function SetupDmgEv))
set t = null
//Run the configuration trigger to set all configurables:
if gg_trg_Damage_Engine_Config == null then
//It's possible this InitTrig_ function ran first, in which case use ExecuteFunc.
call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
else
call TriggerExecute(gg_trg_Damage_Engine_Config)
endif
//Create trigger for storing all EVENT_UNIT_DAMAGED events.
call CreateDmgEvTrg()
//Create GUI-friendly trigger for cleaning up after UnitDamageTarget.
set udg_ClearDamageEvent = CreateTrigger()
call TriggerAddCondition(udg_ClearDamageEvent, Filter(function PreCheckDamagedLifeEvent))
//Disable SpellDamageAbility for every player.
loop
set i = i - 1
call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
exitwhen i == 0
endloop
//Preload abilities.
call UnitAddAbility(u, udg_DamageBlockingAbility)
call UnitAddAbility(u, udg_SpellDamageAbility)
call RemoveUnit(u)
set u = null
endfunction
//==============================================================================
// Custom Race System by Archmage Owenalacaster
//==============================================================================
//
// Purpose:
// - Creates the starting units for custom races and replaces the standard
// Melee Initialization trigger. Custom races are selected by race and
// handicap.
//
// Usage:
// - Register a new custom race with CustomRace.create(name, RACE, handicap)
// Handicaps: Valid handicap values are 1.0, 0.9, 0.8, 0.7, 0.6 and 0.5.
// - Register a new custom race for all handicaps of a single race with
// CustomRace.createAll(name, RACE)
// - Extend the registration of a race with c.register(RACE, handicap)
// - Set the townhall type with c.setTownHall(unitid)
// - Add a new worker type with c.addWorkerType(unitid, priority, qty)
// Priorities: c.NEAR_MINE spawns workers near the mine, where workers
// typically spawn.
// c.NEAR_HALL spawns workers near the town hall, where
// Ghouls spawn.
// - Add a random hero type with c.addHeroType(unitid)
// - Set the ai script used by computer players with c.setAIScript(stringpath)
// - Set a callback function with c.setCallback(CustomRaceCall.function)
// Callbacks: The callback is executed after all the starting units for a
// player are created, and its purpose is to provide enhanced
// initial behaviour for a race. A good example of this with the
// standard races would be the Undead Goldmine Haunting and
// Night Elves Goldmine Entangling.
// The callback function passes as arguments all the units
// generated in addition to the nearest goldmine detected.
// Please note that if a random hero is not created, the last
// argument will have a null value, so always do a check.
// - Get a player's custom race name string with GetPlayerCustomRaceName(player)
//
// Notes:
// - Supports a maximum of 24 custom races.
// - Maximum for worker and hero types are configurable.
//
// Requirements:
// - JassHelper version 0.9.E.0 or newer (older versions may still work).
//
// Installation:
// - Create a new trigger called CustomRaceSystem.
// - Convert it to custom text and replace all the code with this code.
//
// Special Thanks:
// - Alevice: He practically co-wrote the code.
// - cosmicat: His formula for circular unit formation.
// Co-developing the single-array registry.
//
//==============================================================================
library CustomRaceSystem initializer Init
//===========================================================================
// CONFIGURATION SECTION
//===========================================================================
globals
// Unit Type Constants
private constant integer MAX_WORKERTYPES = 4
private constant integer MAX_HEROTYPES = 4
endglobals
//===========================================================================
// END CONFIGURATION SECTION
//===========================================================================
function interface CustomRaceCall takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
private function r2S takes race r returns string
if r == RACE_HUMAN then
return "Human"
elseif r == RACE_ORC then
return "Orc"
elseif r == RACE_UNDEAD then
return "Undead"
elseif r == RACE_NIGHTELF then
return "Night Elf"
endif
return "Unknown"
endfunction
private function r2I takes race r returns integer
if r == RACE_HUMAN then
return 1
elseif r == RACE_ORC then
return 2
elseif r == RACE_UNDEAD then
return 3
elseif r == RACE_NIGHTELF then
return 4
endif
return 5
endfunction
globals
// Victory Defeat Variables
private string array KEY_STRUCTURE
private integer KEY_STRUCTURE_COUNT = 0
endglobals
//===========================================================================
// STRUCT DATA
//===========================================================================
private keyword createStartingUnits
struct CustomRace
string name
// Town Hall Variable
integer townhallType = 0
//string townhallName
// Town Hall name is not currently supported.
// Worker Variables
integer totalWorkerTypes = 0
integer array workerType[MAX_WORKERTYPES]
integer array workerPriority[MAX_WORKERTYPES]
integer array workerQty[MAX_WORKERTYPES]
// Random Hero Variables
integer totalHeroTypes = 0
integer array heroType[MAX_HEROTYPES]
// AI Script Directory String Variable
string aiscript = ""
// Callback Variable
private CustomRaceCall c
// Registry Variable
static integer array REGISTRY
// Spawn Priority Variables
static integer NEAR_MINE = 0
static integer NEAR_HALL = 1
static method get takes race r, real h returns CustomRace
return CustomRace(.REGISTRY[((r2I(r)-1)*6)+(10-R2I(h*10.))])
endmethod
method register takes race r, real h returns boolean
local CustomRace c = CustomRace.get(r,h)
if c != 0 then
debug call BJDebugMsg("|cffff0000Registration of "+.name+" failed due to conflict with "+c.name+" registered for "+r2S(r)+" race Handicap "+R2S(h))
return false
endif
set .REGISTRY[((r2I(r)-1)*6)+(10-R2I(h*10.))] = integer(this)
return true
endmethod
static method create takes string name, race r, real h returns CustomRace
local CustomRace c = CustomRace.allocate()
set c.name = name
if not c.register(r,h) then
call c.destroy()
return 0
endif
return c
endmethod
static method createAll takes string name, race r returns CustomRace
local CustomRace c = CustomRace.allocate()
set c.name = name
if not c.register(r,1.0) and not c.register(r,0.9) and not c.register(r,0.8) and not c.register(r,0.7) and not c.register(r,0.6) and not c.register(r,0.5) then
call c.destroy()
return 0
endif
return c
endmethod
method setTownHall takes integer hallid returns nothing
set .townhallType = hallid
set KEY_STRUCTURE[KEY_STRUCTURE_COUNT] = UnitId2String(hallid)
set KEY_STRUCTURE_COUNT = KEY_STRUCTURE_COUNT+1
endmethod
method addWorkerType takes integer workerid, integer priority, integer quantity returns nothing
set .workerType[.totalWorkerTypes] = workerid
set .workerPriority[.totalWorkerTypes] = priority
set .workerQty[.totalWorkerTypes] = quantity
set .totalWorkerTypes = .totalWorkerTypes+1
endmethod
method addHeroType takes integer heroid returns nothing
local integer i = 0
set .heroType[.totalHeroTypes] = heroid
set .totalHeroTypes = .totalHeroTypes+1
loop
call SetPlayerTechMaxAllowed(Player(i),heroid,1)
set i = i+1
exitwhen i == bj_MAX_PLAYERS
endloop
endmethod
private method getRandomHeroType takes nothing returns integer
local integer randomindex = GetRandomInt(0,.totalHeroTypes-1)
return .heroType[randomindex]
endmethod
method setAIScript takes string s returns nothing
set .aiscript = s
endmethod
method setCallback takes CustomRaceCall callb returns nothing
set .c = callb
endmethod
private method createRandomHero takes player p, location loc returns unit
local unit h = CreateUnitAtLoc(p, .getRandomHeroType(), loc, bj_UNIT_FACING)
if bj_meleeGrantHeroItems then
call MeleeGrantItemsToHero(h)
endif
return h
endmethod
method createStartingUnits takes player p returns nothing
local location startLoc = GetPlayerStartLocationLoc(p)
local location nearMineLoc = startLoc
local location nearTownLoc = startLoc
local location spawnLoc = startLoc
local location heroLoc = startLoc
local unit nearestMine = MeleeFindNearestMine(startLoc, bj_MELEE_MINE_SEARCH_RADIUS)
local unit myTownhall = null
local unit myRandHero = null
local group workerGroup = CreateGroup()
local integer workertypeindex = 0
local integer workerqty = 0
local integer spawnPriority = 0
if nearestMine != null then
set nearMineLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine),startLoc,320,0)
set nearTownLoc = MeleeGetProjectedLoc(startLoc,GetUnitLoc(nearestMine),288,0)
set heroLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine),startLoc,384,45)
endif
set myTownhall = CreateUnitAtLoc(p,.townhallType,startLoc,bj_UNIT_FACING)
loop
exitwhen workertypeindex == .totalWorkerTypes
set spawnPriority = .workerPriority[workertypeindex]
if (spawnPriority==.NEAR_HALL) then
set spawnLoc = nearTownLoc
elseif(spawnPriority==.NEAR_MINE) then
set spawnLoc = nearMineLoc
endif
loop
call GroupAddUnit(workerGroup, CreateUnitAtLoc(p,.workerType[workertypeindex],PolarProjectionBJ(spawnLoc,65,(I2R(workerqty)*(360.00 / I2R(.workerQty[workertypeindex]))) + 90),bj_UNIT_FACING))
set workerqty = workerqty + 1
exitwhen workerqty >= .workerQty[workertypeindex]
endloop
call RemoveLocation(spawnLoc)
set workerqty = 0
set workertypeindex = workertypeindex+1
endloop
if (IsMapFlagSet(MAP_RANDOM_HERO) and .totalHeroTypes>0 ) then
set myRandHero = .createRandomHero(p,heroLoc)
else
call SetPlayerState(p,PLAYER_STATE_RESOURCE_HERO_TOKENS,bj_MELEE_STARTING_HERO_TOKENS)
endif
if(.c!=0) then
call .c.evaluate(p,workerGroup,nearestMine,myTownhall,myRandHero)
else
call DestroyGroup(workerGroup)
endif
if nearMineLoc != startLoc then
call RemoveLocation(nearMineLoc)
call RemoveLocation(nearTownLoc)
call RemoveLocation(heroLoc)
endif
call RemoveLocation(startLoc)
set startLoc = null
set nearMineLoc = null
set nearTownLoc = null
set spawnLoc = null
set heroLoc = null
set nearestMine = null
set myTownhall = null
set myRandHero = null
set workerGroup = null
endmethod
endstruct
globals
private string array PLAYER_RACE
endglobals
function GetPlayerCustomRaceName takes player p returns string
return PLAYER_RACE[GetPlayerId(p)]
endfunction
//===========================================================================
// UNIT CREATION SECTION
//===========================================================================
private function CreateStartingUnitsForAllPlayers takes nothing returns nothing
local integer index = 0
local player indexPlayer
local race playerRace
local CustomRace c
loop
set indexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
set playerRace = GetPlayerRace(indexPlayer)
set c = CustomRace.get(playerRace,GetPlayerHandicap(indexPlayer)+0.01)
if (GetPlayerController(indexPlayer) == MAP_CONTROL_USER or (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER and c.aiscript != "" )) and c != 0 then
set PLAYER_RACE[index] = c.name
call c.createStartingUnits(indexPlayer)
elseif playerRace == RACE_HUMAN then
call MeleeStartingUnitsHuman(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseif playerRace == RACE_ORC then
call MeleeStartingUnitsOrc(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseif playerRace == RACE_NIGHTELF then
call MeleeStartingUnitsNightElf(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseif playerRace == RACE_UNDEAD then
call MeleeStartingUnitsUndead(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
else
call MeleeStartingUnitsUnknownRace(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
endif
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
endfunction
//===========================================================================
// CUSTOM MELEE AI SECTION
//===========================================================================
private function CustomMeleeStartingAI takes nothing returns nothing
local integer index = 0
local player indexPlayer
local race indexRace
local CustomRace c
loop
set indexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
set indexRace = GetPlayerRace(indexPlayer)
set c = CustomRace.get(indexRace,GetPlayerHandicap(indexPlayer)+0.01)
call SetPlayerHandicap(indexPlayer,1.0)
if (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER) then
// Run a race-specific melee AI script.
if c != 0 and c.aiscript != "" then
call StartMeleeAI(indexPlayer, c.aiscript)
elseif (indexRace == RACE_HUMAN) then
call PickMeleeAI(indexPlayer, "human.ai", null, null)
elseif (indexRace == RACE_ORC) then
call PickMeleeAI(indexPlayer, "orc.ai", null, null)
elseif (indexRace == RACE_UNDEAD) then
call PickMeleeAI(indexPlayer, "undead.ai", null, null)
call RecycleGuardPosition(bj_ghoul[index])
elseif (indexRace == RACE_NIGHTELF) then
call PickMeleeAI(indexPlayer, "elf.ai", null, null)
else
// Unrecognized race.
endif
call ShareEverythingWithTeamAI(indexPlayer)
endif
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
endfunction
//===========================================================================
// VICTORY DEFEAT SECTION
//===========================================================================
private function CustomGetAllyKeyStructureCount takes player whichPlayer returns integer
local integer i = 0
local integer keyStructs = 0
local integer playerIndex = 0
local player indexPlayer
loop
set indexPlayer = Player(playerIndex)
if (PlayersAreCoAllied(whichPlayer, indexPlayer)) then
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "townhall", true, true)
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "greathall", true, true)
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "necropolis", true, true)
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "treeoflife", true, true)
loop
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, KEY_STRUCTURE[i], true, true)
set i = i+1
exitwhen i == KEY_STRUCTURE_COUNT
endloop
endif
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
return keyStructs
endfunction
private function CustomPlayerIsCrippled takes player whichPlayer returns boolean
local integer allyStructures = MeleeGetAllyStructureCount(whichPlayer)
local integer allyKeyStructures = CustomGetAllyKeyStructureCount(whichPlayer)
return (allyStructures > 0) and (allyKeyStructures <= 0)
endfunction
private function CustomCheckForCrippledPlayers takes nothing returns nothing
local integer playerIndex
local player indexPlayer
local boolean isNowCrippled
call MeleeCheckForLosersAndVictors()
if bj_finishSoonAllExposed then
return
endif
set playerIndex = 0
loop
set indexPlayer = Player(playerIndex)
set isNowCrippled = CustomPlayerIsCrippled(indexPlayer)
if (not bj_playerIsCrippled[playerIndex] and isNowCrippled) then
set bj_playerIsCrippled[playerIndex] = true
call TimerStart(bj_crippledTimer[playerIndex], bj_MELEE_CRIPPLE_TIMEOUT, false, function MeleeCrippledPlayerTimeout)
if (GetLocalPlayer() == indexPlayer) then
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], true)
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_WARNING_HUMAN"))
endif
elseif (bj_playerIsCrippled[playerIndex] and not isNowCrippled) then
set bj_playerIsCrippled[playerIndex] = false
call PauseTimer(bj_crippledTimer[playerIndex])
if (GetLocalPlayer() == indexPlayer) then
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)
if (MeleeGetAllyStructureCount(indexPlayer) > 0) then
if (bj_playerIsExposed[playerIndex]) then
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNREVEALED"))
else
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNCRIPPLED"))
endif
endif
endif
call MeleeExposePlayer(indexPlayer, false)
endif
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
endfunction
private function CustomInitVictoryDefeat takes nothing returns nothing
local trigger checker = CreateTrigger()
local trigger trig
local integer index
local player indexPlayer
set bj_finishSoonTimerDialog = CreateTimerDialog(null)
call TriggerAddAction(checker, function CustomCheckForCrippledPlayers)
set trig = CreateTrigger()
call TriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_SOON)
call TriggerAddAction(trig, function MeleeTriggerTournamentFinishSoon)
set trig = CreateTrigger()
call TriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_NOW)
call TriggerAddAction(trig, function MeleeTriggerTournamentFinishNow)
set index = 0
loop
set indexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
set bj_meleeDefeated[index] = false
set bj_meleeVictoried[index] = false
set bj_playerIsCrippled[index] = false
set bj_playerIsExposed[index] = false
set bj_crippledTimer[index] = CreateTimer()
set bj_crippledTimerWindows[index] = CreateTimerDialog(bj_crippledTimer[index])
call TimerDialogSetTitle(bj_crippledTimerWindows[index], MeleeGetCrippledTimerMessage(indexPlayer))
call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null)
call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
call TriggerRegisterPlayerAllianceChange(checker, indexPlayer, ALLIANCE_PASSIVE)
call TriggerRegisterPlayerStateEvent(checker, indexPlayer, PLAYER_STATE_ALLIED_VICTORY, EQUAL, 1)
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_DEFEAT)
call TriggerAddAction(trig, function MeleeTriggerActionPlayerDefeated)
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
call TriggerAddAction(trig, function MeleeTriggerActionPlayerLeft)
else
set bj_meleeDefeated[index] = true
set bj_meleeVictoried[index] = false
if (IsPlayerObserver(indexPlayer)) then
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
call TriggerAddAction(trig, function MeleeTriggerActionPlayerLeft)
endif
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
call TimerStart(CreateTimer(), 2.0, false, function CustomCheckForCrippledPlayers)
endfunction
private function TimerAction takes nothing returns nothing
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, bj_MELEE_STARTING_TOD)
call MeleeStartingHeroLimit()
call MeleeGrantHeroItems()
call MeleeStartingResources()
call MeleeClearExcessUnits()
call CreateStartingUnitsForAllPlayers()
call CustomMeleeStartingAI()
call CustomInitVictoryDefeat()
call DestroyTimer(GetExpiredTimer())
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(),0,false,function TimerAction)
endfunction
endlibrary
library OriginalRacesSetup initializer Init requires CustomRaceSystem
private function WorkerHideToggle takes nothing returns nothing
call ShowUnit(GetEnumUnit(),IsUnitHidden(GetEnumUnit()))
endfunction
private function HauntGoldMine takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
call ForGroup(workers,function WorkerHideToggle)
call BlightGoldMineForPlayerBJ(goldmine,play)
call ForGroup(workers,function WorkerHideToggle)
call DestroyGroup(workers)
endfunction
private function EntangleGoldMine takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
call SetUnitPosition(townhall,GetUnitX(goldmine),GetUnitY(goldmine))
call IssueTargetOrder(townhall, "entangleinstant", goldmine)
call DestroyGroup(workers)
endfunction
private function Init takes nothing returns nothing
local CustomRace c = CustomRace.create("Human",RACE_HUMAN,1.0)
call c.setTownHall('htow') // Town Hall
call c.addWorkerType('hpea',c.NEAR_MINE,5) // Peasant
call c.addHeroType('Hpal') // Paladin
call c.addHeroType('Hamg') // Archmage
call c.addHeroType('Hmkg') // Mountain King
call c.addHeroType('Hblm') // Blood Mage
call c.setAIScript("human.ai")
set c = CustomRace.create("Orc",RACE_ORC,1.0)
call c.setTownHall('ogre') // Great Hall
call c.addWorkerType('opeo',c.NEAR_MINE,5) // Peon
call c.addHeroType('Obla') // Blademaster
call c.addHeroType('Ofar') // Far Seer
call c.addHeroType('Otch') // Tauren Chieftain
call c.addHeroType('Oshd') // Shadow Hunter
call c.setAIScript("orc.ai")
set c = CustomRace.create("Undead",RACE_UNDEAD,1.0)
call c.setTownHall('unpl') // Necropolis
call c.addWorkerType('uaco',c.NEAR_MINE,3) // Acolyte
call c.addWorkerType('ugho',c.NEAR_HALL,1) // Ghoul
call c.addHeroType('Udea') // Death Knight
call c.addHeroType('Ulic') // Lich
call c.addHeroType('Udre') // Dreadlord
call c.addHeroType('Ucrl') // Crypt Lord
call c.setCallback(CustomRaceCall.HauntGoldMine)
call c.setAIScript("undead.ai")
set c = CustomRace.create("Night Elf",RACE_NIGHTELF,1.0)
call c.setTownHall('etol') // Tree of Life
call c.addWorkerType('ewsp',c.NEAR_MINE,5) // Wisp
call c.addHeroType('Ekee') // Keeper of the Grove
call c.addHeroType('Emoo') // Priestess of the Moon
call c.addHeroType('Edem') // Demon Hunter
call c.addHeroType('Ewar') // Warden
call c.setCallback(CustomRaceCall.EntangleGoldMine)
call c.setAIScript("elf.ai")
endfunction
endlibrary
library BloodTrollSetup initializer Init requires CustomRaceSystem
private function TrollConfig takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
local string playlist
if (GetLocalPlayer() == play) then
call ClearMapMusic()
call StopMusic(false)
set playlist = "Music\\Troll01.mp3;Music\\Troll02.mp3;Music\\Troll03.mp3;Music\\Troll04.mp3;Music\\Troll05.mp3"
call PlayMusic(playlist)
endif
endfunction
private function Init takes nothing returns nothing
local CustomRace c = CustomRace.create("Blood Troll",RACE_ORC,0.9)
call c.setTownHall('btb0') // Sanctum of Sorrows
call c.addWorkerType('bt00',c.NEAR_MINE,5) // Faithful
call c.addHeroType('BTH0') // Blood Witch
call c.addHeroType('BTH1') // Corpse Monger
call c.addHeroType('BTH3') // Warmother
call c.setCallback(CustomRaceCall.TrollConfig)
call c.setAIScript("troll.ai")
endfunction
endlibrary
scope onDamage initializer Init
private function TorchlightFilter takes unit a, unit t, player p returns boolean
return IsAlive(t) and HasAbility(a, 'A000') and (IsBuilding(t) or IsMechanical(t)) and IsResearched(p, 'R00R') and IsUnitEnemy(t, p)
endfunction
private function BloodBeastFilter takes unit u returns boolean
return IsAlive(u) and GetUnitTypeId(u) == 'btx1' and GetUnitAbilityLevel(u, 'A00C') < 11
endfunction
private function TribalHunterFilter takes unit a, unit t, player p returns boolean
return IsAlive(t) and HasAbility(a, 'A00N') and IsWorker(t) and IsResearched(p, UPG_RIPPER) and IsUnitEnemy(t, p)
endfunction
private function GrimCoilFilter takes unit a, unit t, player p returns boolean
return IsAlive(t) and HasAbility(a, 'A00Q') and IsUnitEnemy(t, p)
endfunction
private function FeastFilter takes unit u, player p returns boolean
return IsAlive(u) and HasAbility(u, 'A002') and IsResearched(p, UPG2_SHYLAZZI) and not IsMelee(u)
endfunction
private function UndyingTranceFilter takes unit u returns boolean
return IsAlive(u) and HasAbility(u, 'B00E') and GetUnitStatePercent(u, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE) <= 40.
endfunction
private function SanguineMistFilter takes unit u returns boolean
return IsAlive(u) and HasAbility(u, 'B00F')
endfunction
private function Actions takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local real damageAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
if BloodBeastFilter(attacker) then
call BloodBeastStats(attacker)
endif
if GrimCoilFilter(attacker, target, attackerOwner) then
if IsMelee(target) then
set damageAmount = damageAmount + 6
else
set damageAmount = damageAmount * 0.8
endif
endif
if HasAbility(attacker, 'B005') then
set damageAmount = damageAmount * 0.85
endif
if TorchlightFilter(attacker, target, attackerOwner) then
set damageAmount = damageAmount * 2
endif
if TribalHunterFilter(attacker, target, attackerOwner) then
set damageAmount = damageAmount * ((GetUnitAbilityLevel(attacker, 'A00N') / 2) + 2)
call CreateEffectOnUnit("Objects\\Spawnmodels\\Orc\\Orcblood\\OrcBloodPeon.mdl", target, "chest")
endif
if HasAbility(target, 'B001') and not isSpell then
set damageAmount = damageAmount * 1.15
endif
if FeastFilter(attacker, attackerOwner) then
call SetUnitState(attacker, UNIT_STATE_LIFE, GetUnitState(attacker, UNIT_STATE_LIFE) + (damageAmount * .15))
call CreateEffectOnUnit("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", attacker, "origin")
endif
if SanguineMistFilter(target) and isSpell then
if HasAbility(target, 'A01B') then
set damageAmount = damageAmount * .4
else
set damageAmount = damageAmount * .6
endif
endif
if UndyingTranceFilter(target) then
set damageAmount = 0
endif
set udg_DamageEventAmount = damageAmount
set attacker = null
set attackerOwner = null
set target = null
endfunction
private function Init takes nothing returns nothing
local trigger onDamage = CreateTrigger()
call TriggerRegisterVariableEvent(onDamage, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(onDamage, function Actions)
endfunction
endscope
scope onAttack initializer Init
globals
private constant integer PACK_MAX = 4
private constant integer PACK_MAX_UP = 6
private constant real PACK_AOE = 700.
endglobals
private function HuntingPackFilter takes unit u, player p returns boolean
return IsAlive(u) and HasAbility(u, 'A00F') and IsResearched(p, 'R00S') and IsUnitAlly(u, p)
endfunction
private function BlazingShardsFilter takes unit a, unit t, player p returns boolean
return IsAlive(t) and HasAbility(a, 'A00R') and IsResearched(p, 'R00U') and IsUnitEnemy(t, p)
endfunction
private function MayhemFilter takes unit u returns boolean
return IsAlive(u) and (HasAbility(u, 'B00A') or HasAbility(u, 'B00B') or HasAbility(u, 'B00C'))
endfunction
private function CrimsonHuntressFilter takes unit u returns boolean
return IsAlive(u) and HasAbility(u, 'A01A')
endfunction
private function RampageFilter takes unit u returns boolean
return IsAlive(u) and HasAbility(u, 'A01B')
endfunction
private function Actions takes nothing returns nothing
local unit attacker = GetAttacker()
local real attackerX = GetUnitX(attacker)
local real attackerY = GetUnitY(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = GetTriggerUnit()
local group allies = CreateGroup()
local integer count = 0
local real damageAmount = 0
local unit picked
local integer packLvl
local integer packMax
local Hemorrhage hem
if HasAbility(attacker, 'A00F') and IsResearched(attackerOwner, 'R00S') then
if IsResearched(attackerOwner, UPG1_ZWAMBADI) then
set packMax = PACK_MAX_UP
else
set packMax = PACK_MAX
endif
call GroupEnumUnitsWithCollision(allies, attackerX, attackerY, PACK_AOE)
loop
set picked = FirstOfGroup(allies)
exitwhen picked == null or count == packMax
if HuntingPackFilter(picked, attackerOwner) and picked != attacker then
set count = count + 1
endif
call GroupRemoveUnit(allies, picked)
set picked = null
endloop
call HuntingPacksStats(attacker, count)
endif
if CrimsonHuntressFilter(attacker) then
call CrimsonHuntressStats(attacker, target)
endif
if HasAbility(attacker, 'A00K') then
set damageAmount = GetRandomReal(8, 12)
call TormentedSoulOnHit(attacker, target, damageAmount)
endif
if BlazingShardsFilter(attacker, target, attackerOwner) then
call BlazingShardsOnHit(attacker, target)
endif
if MayhemFilter(attacker) then
if HasAbility(attacker, 'B00A') then
if HasChance(7) then
call CastMayhem(attacker, 1)
endif
elseif HasAbility(attacker, 'B00B') then
if HasChance(10) then
call CastMayhem(attacker, 2)
endif
elseif HasAbility(attacker, 'B00C') then
if HasChance(13) then
call CastMayhem(attacker, 3)
endif
endif
endif
if RampageFilter(attacker) and HasChance(8)then
set hem = Hemorrhage.create()
call hem.castHemorrhage(attacker, target)
endif
call DestroyGroup(allies)
set attacker = null
set attackerOwner = null
set target = null
set allies = null
endfunction
private function Init takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function Actions)
endfunction
endscope
scope onDeath initializer Init
private function RampageFilter takes unit u returns boolean
return IsAlive(u) and HasAbility(u, 'A01B')
endfunction
private function Actions takes nothing returns nothing
local unit killer = GetKillingUnit()
if RampageFilter(killer) then
call SetUnitState(killer, UNIT_STATE_LIFE, GetUnitState(killer, UNIT_STATE_LIFE) + (GetUnitState(killer, UNIT_STATE_MAX_LIFE) * .1))
endif
set killer = null
endfunction
private function Init takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function Actions)
endfunction
endscope
scope Transfusion
globals
private constant integer SPELL_ID = 'A003'
private constant integer PLAGUE_BUFF = 'B000'
private constant string ON_HIT_FX = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
private constant string MISSILE_MODEL = "war3mapImported\\RedManaBurst.mdx"
private constant real DMG = 20.
private constant real DMG_PLAGUE = 30.
private constant real OWN_DMG = 15.
private constant real AOE = 600.
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsMechanical(u) and not IsBuilding(u) and not IsMagicImmune(u) and IsUnitEnemy(u, p)
endfunction
private struct Transfusion extends Missiles
method onHit takes unit hit returns boolean
if hit == target then
call SetUnitState(hit, UNIT_STATE_LIFE, GetUnitState(hit, UNIT_STATE_LIFE) + damage)
call CreateEffectOnUnit(ON_HIT_FX, hit, "origin")
endif
return false
endmethod
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local player casterOwner = GetOwningPlayer(caster)
local unit targetU = GetSpellTargetUnit()
local real targetX = GetUnitX(targetU)
local real targetY = GetUnitY(targetU)
local group tempGroup = CreateGroup()
local group enemiesGroup = CreateGroup()
local boolean groupHasUnits = false
local boolean randomPicked = false
local unit picked
local real pickedX
local real pickedY
local thistype this
call GroupEnumUnitsWithCollision(tempGroup, casterX, casterY, AOE)
set enemiesGroup = CopyGroup(tempGroup)
loop
set picked = FirstOfGroup(tempGroup)
exitwhen picked == null
if UnitFilter(picked, casterOwner) then
if not groupHasUnits then
set groupHasUnits = true
endif
endif
call GroupRemoveUnit(tempGroup, picked)
set picked = null
endloop
if groupHasUnits then
loop
set picked = GroupRandomUnit(enemiesGroup)
exitwhen picked == null or randomPicked
if UnitFilter(picked, casterOwner) then
set randomPicked = true
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set this = thistype.create(pickedX, pickedY, 100., targetX, targetY, 100.)
if HasAbility(picked, PLAGUE_BUFF) then
set damage = DMG_PLAGUE
else
set damage = DMG
endif
call UnitDamageTarget(caster, picked, damage, true, false, ATT_TYPE, DMG_TYPE, null)
set source = caster
set target = targetU
set model = MISSILE_MODEL
set speed = 850.
set collision = 15.
set curve = GetRandomReal(0, 12)
call launch()
endif
call GroupRemoveUnit(enemiesGroup, picked)
set picked = null
endloop
else
set this = thistype.create(casterX, casterY, 100., targetX, targetY, 100.)
if GetUnitTypeId(targetU) != 'bt04' then
call UnitDamageTarget(caster, caster, OWN_DMG, true, false, ATT_TYPE, DMG_TYPE, null)
endif
set damage = DMG
set source = caster
set target = targetU
set model = MISSILE_MODEL
set speed = 1000.
set collision = 15.
set curve = GetRandomReal(0, 12)
call launch()
endif
call DestroyGroup(tempGroup)
call DestroyGroup(enemiesGroup)
set caster = null
set casterOwner = null
set targetU = null
set tempGroup = null
set enemiesGroup = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope BloodPact
globals
private constant integer SPELL_ID = 'A004'
private constant string ON_TARGET_FX = "Abilities\\Spells\\Human\\Feedback\\SpellBreakerAttack.mdl"
private constant real DMG_PERC = .1
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant integer DUMMY_SPELL_ID = 'A009'
private constant string DUMMY_SPELL_ORDER = "purge"
endglobals
struct BloodPact
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local player casterOwner = GetOwningPlayer(caster)
local real casterCurrentHP = GetUnitState(caster, UNIT_STATE_LIFE)
local unit target = GetSpellTargetUnit()
local real targetCurrentHP = GetUnitState(target, UNIT_STATE_LIFE)
local unit dummy
if caster != target then
call UnitDamageTarget(caster, target, targetCurrentHP*DMG_PERC, true, false, ATT_TYPE, DMG_TYPE, null)
call CreateEffectOnUnit(ON_TARGET_FX, target, "chest")
endif
call UnitDamageTarget(caster, caster, casterCurrentHP*DMG_PERC, true, false, ATT_TYPE, DMG_TYPE, null)
call CreateEffectOnUnit(ON_TARGET_FX, caster, "chest")
set dummy = DummyCaster(casterX, casterY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, caster)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
set caster = null
set casterOwner = null
set target = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Defile
globals
private constant integer SPELL_ID = 'A006'
private constant integer DUMMY_SPELL_ID = 'A00A'
private constant string DUMMY_SPELL_ORDER = "cripple"
endglobals
struct Defile
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local unit dummy
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
set caster = null
set casterOwner = null
set target = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope NullWard
globals
private constant integer SPELL_ID = 'A00B'
private constant string ON_CAST_FX = "war3mapImported\\ArcaneExplosion.mdx"
private constant string ON_TARGET_FX = "Abilities\\Spells\\Undead\\AbsorbMana\\AbsorbManaBirthMissile.mdl"
private constant real MANA_BURNED = 15.
private constant real AOE = 500.
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsBuilding(u) and UnitHasMana(u) and IsUnitEnemy(u, p)
endfunction
struct NullWard
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local location casterLoc = GetUnitLoc(caster)
local player casterOwner = GetOwningPlayer(caster)
local group enemies = CreateGroup()
local real manaBurned
local unit picked
local real pickedMana
call CreateEffectOnLoc(ON_CAST_FX, casterLoc)
call GroupEnumUnitsWithCollision(enemies, casterX, casterY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, casterOwner) then
set pickedMana = GetUnitState(picked, UNIT_STATE_MANA)
call CreateEffectOnUnit(ON_TARGET_FX, picked, "chest")
if pickedMana > MANA_BURNED then
set manaBurned = MANA_BURNED
else
set manaBurned = pickedMana
endif
call SetUnitState(picked, UNIT_STATE_MANA, pickedMana - manaBurned)
call UnitDamageTarget(caster, picked, manaBurned, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
call RemoveLocation(casterLoc)
call DestroyGroup(enemies)
set caster = null
set casterLoc = null
set casterOwner = null
set enemies = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope BloodBeast
globals
private constant integer SPELL_ID = 'A008'
private constant integer SUMMON_ID = 'btx1'
private constant integer BUFF_ID = 'BTLF'
private constant string ON_SUMMON_FX = "Abilities\\Spells\\Orc\\FeralSpirit\\feralspirittarget.mdl"
private constant real DURATION = 25.
endglobals
struct BloodBeast
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local location casterLoc = GetUnitLoc(caster)
local player casterOwner = GetOwningPlayer(caster)
local real casterFacing = GetUnitFacing(caster)
local unit summoned
local location summonedLoc
set summoned = CreateUnitAtLoc(casterOwner, SUMMON_ID, PolarProjectionBJ(casterLoc, 120.00, casterFacing), casterFacing)
set summonedLoc = GetUnitLoc(summoned)
call UnitApplyTimedLife(summoned, BUFF_ID, DURATION)
call CreateEffectOnLoc(ON_SUMMON_FX, summonedLoc)
call RemoveLocation(casterLoc)
call RemoveLocation(summonedLoc)
set caster = null
set casterLoc = null
set casterOwner = null
set summoned = null
set summonedLoc = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
library BloodBeastStats
function BloodBeastStats takes unit u returns nothing
local integer level = GetUnitAbilityLevel(u, 'A00C')
call SetUnitAbilityLevel(u, 'A00C', level + 1)
call SetUnitAbilityLevel(u, 'A00D', level + 1)
endfunction
endlibrary
library HuntingPacks
function HuntingPacksStats takes unit u, integer n returns nothing
local integer i = 0
loop
exitwhen i > 6
call UnitRemoveAbility(u, HUNTING_PACK_LVL[i])
set i = i + 1
endloop
call SetUnitAbilityLevel(u, 'A00I', n + 1)
call SetUnitAbilityLevel(u, 'A00J', n + 1)
call UnitAddAbility(u, HUNTING_PACK_LVL[n])
endfunction
endlibrary
library TormentedSouls requires Missiles, SpellUtils, DummyUtils
globals
private constant string NML_MISSILE_MODEL = "Abilities\\Weapons\\SkeletalMageMissile\\SkeletalMageMissile.mdl"
private constant string SPC_MISSILE_MODEL = "war3mapImported\\NecroMagusMissile.mdx"
private constant integer DUMMY_SPELL_ID = 'A00M'
private constant string DUMMY_SPELL_ORDER = "drunkenhaze"
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and UnitHasMana(u) and not IsMechanical(u) and not IsBuilding(u) and not IsMagicImmune(u) and IsUnitEnemy(u, p)
endfunction
private struct TormentedSoul extends Missiles
method onHit takes unit hit returns boolean
local unit dummy
if hit == .target then
call UnitDamageTarget(.source, hit, .damage, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
set dummy = DummyCaster(GetUnitX(.target), GetUnitY(.target), 0., GetOwningPlayer(.source))
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, .target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
endif
set dummy = null
return false
endmethod
endstruct
private struct NormalMissile extends Missiles
method onHit takes unit hit returns boolean
local unit dummy
if hit == .target then
call UnitDamageTarget(.source, hit, .damage, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
set dummy = null
return false
endmethod
endstruct
function TormentedSoulOnHit takes unit attacker, unit target, real dmgAmount returns nothing
local real attackerX = GetUnitX(attacker)
local real attackerY = GetUnitY(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local TormentedSoul soul
local NormalMissile missile
if UnitFilter(target, attackerOwner) then
set soul = TormentedSoul.create(attackerX, attackerY, 50, targetX, targetY, 50)
set soul.source = attacker
set soul.target = target
set soul.model = SPC_MISSILE_MODEL
set soul.damage = dmgAmount
set soul.speed = 1000.
set soul.collision = 15.
call soul.launch()
else
set missile = NormalMissile.create(attackerX, attackerY, 50, targetX, targetY, 50)
set missile.source = attacker
set missile.target = target
set missile.model = NML_MISSILE_MODEL
set missile.damage = 0
set missile.speed = 1000.
set missile.collision = 15.
call missile.launch()
endif
set attackerOwner = null
endfunction
endlibrary
library BlazingShards requires Missiles, SpellUtils, GroupUtils
globals
private constant string MISSILE_MODEL = "war3mapImported\\Firebolt Minor.mdx"
private constant real MIN_DMG = 12.
private constant real MAX_DMG = 16.
private constant real DMG_UPG = 2.
private constant real AOE = 500.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsMagicImmune(u) and not IsFlying(u) and IsUnitEnemy(u, p)
endfunction
private struct Shard extends Missiles
method onHit takes unit hit returns boolean
if hit == .target then
call UnitDamageTarget(.source, hit, .damage, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
return false
endmethod
endstruct
function BlazingShardsOnHit takes unit attacker, unit target returns nothing
local real attackerX = GetUnitX(attacker)
local real attackerY = GetUnitY(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local group enemies = CreateGroup()
local boolean unitHit = false
local real dmgAmount = 0
local unit picked
local real pickedX
local real pickedY
local Shard shard
call GroupEnumUnitsWithCollision(enemies, targetX, targetY, AOE)
loop
set picked = GroupRandomUnit(enemies)
exitwhen picked == null or unitHit
if UnitFilter(picked, attackerOwner) then
set unitHit = true
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set dmgAmount = GetRandomReal(MIN_DMG, MAX_DMG) + (GetPlayerTechCount(attackerOwner, 'R00O', true) * DMG_UPG)
set shard = Shard.create(attackerX, attackerY, 240, pickedX, pickedY, 50)
set shard.source = attacker
set shard.target = picked
set shard.model = MISSILE_MODEL
set shard.damage = dmgAmount
set shard.speed = 1050.
set shard.arc = GetRandomReal(10, 45)
set shard.curve = GetRandomReal(5, 20)
set shard.collision = 15.
call shard.launch()
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
call DestroyGroup(enemies)
set attackerOwner = null
set enemies = null
endfunction
endlibrary
scope Decimate
globals
private constant integer SPELL_ID = 'A012'
private constant string ON_CAST_FX = "war3mapImported\\Culling Cleave Red.mdx"
private constant real AOE = 225.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsMechanical(u) and not IsBuilding(u) and not IsMagicImmune(u) and not IsFlying(u) and IsUnitEnemy(u, p)
endfunction
struct Decimate
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local integer strength = GetHeroStr(caster, true)
local real dmgAmount = (1 + lvl) * strength
local group enemies = CreateGroup()
local unit picked
call CreateEffectOnUnit(ON_CAST_FX, caster, "chest")
call GroupEnumUnitsWithCollision(enemies, targetX, targetY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, casterOwner) then
call UnitDamageTarget(caster, picked, dmgAmount, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
call DestroyGroup(enemies)
set caster = null
set casterOwner = null
set target = null
set enemies = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Guillotine
globals
private constant integer SPELL_ID = 'A013'
private constant string ON_CAST_FX = "war3mapImported\\Coup de Grace.mdx"
private constant integer DUMMY_SPELL_ID = 'A016'
private constant string DUMMY_SPELL_ORDER = "creepthunderclap"
private constant real AOE = 250.
private constant real DMG_PER_LVL = 100.
private constant real CHECK_UNIT = .05
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
struct Guillotine
private unit caster
private player casterOwner
private unit target
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .casterOwner = null
set .target = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real targetX = GetUnitX(.target)
local real targetY = GetUnitY(.target)
local integer lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
local unit dummy
if not IsAlive(.target) then
set dummy = DummyCaster(targetX, targetY, 0., .casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, lvl)
call IssueImmediateOrder(dummy, DUMMY_SPELL_ORDER)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
endif
call .destroy()
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local unit caster = GetSpellAbilityUnit()
local unit target = GetSpellTargetUnit()
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local real dmgAmount = lvl * DMG_PER_LVL
set this = allocate()
set .caster = caster
set .casterOwner = GetOwningPlayer(.caster)
set .target = target
set .loopTimer = NewTimerEx(this)
call CreateEffectOnUnit(ON_CAST_FX, target, "chest")
call UnitDamageTarget(caster, target, dmgAmount, true, false, ATT_TYPE, DMG_TYPE, null)
call TimerStart(.loopTimer, CHECK_UNIT, false, function thistype.onInterval)
set caster = null
set target = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
library MayhemAura requires DummyUtils
globals
private constant integer DUMMY_SPELL_ID = 'A017'
private constant string DUMMY_SPELL_ORDER = "innerfire"
endglobals
function CastMayhem takes unit u, integer lvl returns nothing
local real unitX = GetUnitX(u)
local real unitY = GetUnitY(u)
local player owner = GetOwningPlayer(u)
local unit dummy = DummyCaster(unitX, unitY, 0., owner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, lvl)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, u)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
set owner = null
endfunction
endlibrary
library Hemorrhage requires SpellUtils, DummyUtils, TimerUtils, SpellEffectEvent
globals
private constant integer SPELL_ID = 'A018'
private constant integer DUMMY_SPELL_ID = 'A01D'
private constant string DUMMY_SPELL_ORDER = "parasite"
private constant real BASE_DMG = 3.
private constant real DMG_PER_LVL = 1.
private constant real DURATION = 30.
private constant real INTERVAL = 1.
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
endglobals
struct Hemorrhage
private unit caster
private unit target
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .target = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
local real dmgAmount = BASE_DMG + (DMG_PER_LVL * lvl)
if .remainingDuration > 0 and IsAlive(.target) and HasAbility(.target, 'B00H') then
set .remainingDuration = .remainingDuration - INTERVAL
call UnitDamageTarget(.caster, .target, dmgAmount, true, false, ATT_TYPE, DMG_TYPE, null)
else
call .destroy()
endif
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local unit dummy
set this = allocate()
set .caster = caster
set .target = target
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
set caster = null
set casterOwner = null
set target = null
endmethod
method castHemorrhage takes unit caster, unit target returns nothing
local player casterOwner = GetOwningPlayer(caster)
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local unit dummy
set .caster = caster
set .target = target
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
set caster = null
set casterOwner = null
set target = null
endmethod
static method create takes nothing returns Hemorrhage
local Hemorrhage hem = Hemorrhage.allocate()
return hem
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
scope SanguineMist
globals
private constant integer SPELL_ID = 'A019'
private constant integer DUMMY_SPELL_ID = 'A01C'
private constant string DUMMY_SPELL_ORDER = "silence"
private constant real DURATION = 12.
private constant real INTERVAL = 1.
endglobals
struct SanguineMist
private unit caster
private player casterOwner
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .casterOwner = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real casterX = GetUnitX(.caster)
local real casterY = GetUnitY(.caster)
local integer lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
local unit dummy
if .remainingDuration > 0 and IsAlive(.caster) then
set .remainingDuration = .remainingDuration - INTERVAL
set dummy = DummyCaster(casterX, casterY, 0., .casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, lvl)
call IssuePointOrder(dummy, DUMMY_SPELL_ORDER, casterX, casterY)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
else
call .destroy()
endif
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local unit dummy
set this = allocate()
set .caster = caster
set .casterOwner = GetOwningPlayer(.caster)
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
set dummy = DummyCaster(casterX, casterY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, lvl)
call IssuePointOrder(dummy, DUMMY_SPELL_ORDER, casterX, casterY)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
set caster = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
library CrimsonHuntress requires SpellUtils
globals
private constant integer BONUS_ID = 'A01F'
private constant integer HUNTRESS_ID = 'A01A'
private constant integer RAMPAGE_ID = 'A01B'
private constant integer HEMORRHAGE = 'B00H'
endglobals
function CrimsonHuntressStats takes unit attacker, unit target returns nothing
local real lvl = GetUnitAbilityLevel(attacker, HUNTRESS_ID)
local real targetHP = GetUnitStatePercent(target, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE)
local integer n
if lvl == 1 then
if targetHP == 100 then
set n = 1
elseif targetHP < 100 and targetHP >= 90 then
set n = 2
elseif targetHP < 90 and targetHP >= 80 then
set n = 3
elseif targetHP < 80 and targetHP >= 70 then
set n = 4
elseif targetHP < 70 and targetHP >= 60 then
set n = 5
elseif targetHP < 60 and targetHP >= 50 then
set n = 6
elseif targetHP < 50 and targetHP >= 40 then
set n = 7
elseif targetHP < 40 and targetHP >= 30 then
set n = 8
elseif targetHP < 30 and targetHP >= 20 then
set n = 9
elseif targetHP < 20 and targetHP >= 10 then
set n = 10
elseif targetHP < 10 then
set n = 11
endif
elseif lvl == 2 then
if targetHP == 100 then
set n = 1
elseif targetHP < 100 and targetHP >= 92 then
set n = 2
elseif targetHP < 92 and targetHP >= 84 then
set n = 3
elseif targetHP < 84 and targetHP >= 76 then
set n = 4
elseif targetHP < 76 and targetHP >= 68 then
set n = 5
elseif targetHP < 68 and targetHP >= 60 then
set n = 6
elseif targetHP < 60 and targetHP >= 52 then
set n = 7
elseif targetHP < 52 and targetHP >= 44 then
set n = 8
elseif targetHP < 44 and targetHP >= 36 then
set n = 9
elseif targetHP < 36 and targetHP >= 28 then
set n = 10
elseif targetHP < 28 and targetHP >= 20 then
set n = 11
elseif targetHP < 20 and targetHP >= 12 then
set n = 12
elseif targetHP < 12 then
set n = 13
endif
elseif lvl == 3 then
if targetHP == 100 then
set n = 1
elseif targetHP < 100 and targetHP >= 94 then
set n = 2
elseif targetHP < 94 and targetHP >= 88 then
set n = 3
elseif targetHP < 88 and targetHP >= 82 then
set n = 4
elseif targetHP < 82 and targetHP >= 76 then
set n = 5
elseif targetHP < 76 and targetHP >= 70 then
set n = 6
elseif targetHP < 70 and targetHP >= 64 then
set n = 7
elseif targetHP < 64 and targetHP >= 58 then
set n = 8
elseif targetHP < 58 and targetHP >= 52 then
set n = 9
elseif targetHP < 52 and targetHP >= 46 then
set n = 10
elseif targetHP < 46 and targetHP >= 40 then
set n = 11
elseif targetHP < 40 and targetHP >= 34 then
set n = 12
elseif targetHP < 34 and targetHP >= 28 then
set n = 13
elseif targetHP < 28 and targetHP >= 22 then
set n = 14
elseif targetHP < 16 and targetHP >= 10 then
set n = 15
elseif targetHP < 10 then
set n = 16
endif
endif
call SetUnitAbilityLevel(attacker, BONUS_ID, n)
if HasAbility(target, HEMORRHAGE) then
if lvl == 1 then
call SetUnitState(attacker, UNIT_STATE_LIFE, GetUnitState(attacker, UNIT_STATE_LIFE) + 8)
call SetUnitState(attacker, UNIT_STATE_MANA, GetUnitState(attacker, UNIT_STATE_MANA) + 3)
elseif lvl == 2 then
call SetUnitState(attacker, UNIT_STATE_LIFE, GetUnitState(attacker, UNIT_STATE_LIFE) + 12)
call SetUnitState(attacker, UNIT_STATE_MANA, GetUnitState(attacker, UNIT_STATE_MANA) + 6)
elseif lvl == 3 then
call SetUnitState(attacker, UNIT_STATE_LIFE, GetUnitState(attacker, UNIT_STATE_LIFE) + 16)
call SetUnitState(attacker, UNIT_STATE_MANA, GetUnitState(attacker, UNIT_STATE_MANA) + 9)
endif
else
if HasAbility(attacker, RAMPAGE_ID) then
if lvl == 1 then
call SetUnitState(attacker, UNIT_STATE_LIFE, GetUnitState(attacker, UNIT_STATE_LIFE) + 4)
elseif lvl == 2 then
call SetUnitState(attacker, UNIT_STATE_LIFE, GetUnitState(attacker, UNIT_STATE_LIFE) + 6)
elseif lvl == 3 then
call SetUnitState(attacker, UNIT_STATE_LIFE, GetUnitState(attacker, UNIT_STATE_LIFE) + 8)
endif
endif
endif
endfunction
endlibrary
scope Malefice
globals
private constant integer SPELL_ID = 'A00S'
private constant string ON_LOOP_FX = "war3mapImported\\Arcane Bomb.mdx"
private constant string ON_LOOP_FX_2 = "war3mapImported\\VoidboltRoughMedium.mdx"
private constant real AOE = 250.
private constant real BASE_DMG = 15.
private constant real DMG_PER_LVL = 15.
private constant real DURATION = 6.
private constant real INTERVAL = 2.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsMechanical(u) and not IsBuilding(u) and not IsMagicImmune(u) and IsUnitEnemy(u, p)
endfunction
struct Malefice
private unit caster
private player casterOwner
private unit target
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .casterOwner = null
set .target = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real targetX = GetUnitX(.target)
local real targetY = GetUnitY(.target)
local location targetLoc = GetUnitLoc(.target)
local group enemies = CreateGroup()
local integer lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
local real dmgAmount = BASE_DMG + (DMG_PER_LVL * lvl)
local unit picked
if .remainingDuration > 0 and IsAlive(.target) then
set .remainingDuration = .remainingDuration - INTERVAL
call CreateEffectOnLoc(ON_LOOP_FX, targetLoc)
call CreateEffectOnUnit(ON_LOOP_FX_2, .target, "chest")
call GroupEnumUnitsWithCollision(enemies, targetX, targetY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, .casterOwner) then
call UnitDamageTarget(.caster, picked, dmgAmount, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
else
call .destroy()
endif
call RemoveLocation(targetLoc)
call DestroyGroup(enemies)
set targetLoc = null
set enemies = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local location targetLoc = GetUnitLoc(target)
local group enemies = CreateGroup()
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local real dmgAmount = BASE_DMG + (DMG_PER_LVL * lvl)
local unit picked
set this = allocate()
set .caster = caster
set .casterOwner = casterOwner
set .target = GetSpellTargetUnit()
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call CreateEffectOnLoc(ON_LOOP_FX, targetLoc)
call CreateEffectOnUnit(ON_LOOP_FX_2, target, "chest")
call GroupEnumUnitsWithCollision(enemies, targetX, targetY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, casterOwner) then
call UnitDamageTarget(caster, picked, dmgAmount, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
call RemoveLocation(targetLoc)
call DestroyGroup(enemies)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
set caster = null
set casterOwner = null
set target = null
set targetLoc = null
set enemies = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope SoulHarvest
globals
private constant integer SPELL_ID = 'A010'
private constant string ON_CAST_FX = "Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl"
private constant real BASE_MANA = 6.
private constant real MANA_PER_LVL = 4.
endglobals
struct SoulHarvest
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterMana = GetUnitState(caster, UNIT_STATE_MAX_MANA)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local real manaRestored = (casterMana * (BASE_MANA + (MANA_PER_LVL * lvl))) / 100
call CreateEffectOnUnit(ON_CAST_FX, caster, "origin")
call SetUnitState(caster, UNIT_STATE_MANA, GetUnitState(caster, UNIT_STATE_MANA) + manaRestored)
set caster = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope RainOfBlood
globals
private constant integer SPELL_ID = 'A011'
private constant string SPELL_ORDER = "tranquility"
private constant real AOE = 500.
private constant real BASE_DMG = 6.
private constant real DMG_PERC = .07
private constant real DURATION = 35.
private constant real INTERVAL = .5
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsMechanical(u) and not IsBuilding(u) and IsUnitEnemy(u, p)
endfunction
struct RainOfBlood
private unit caster
private player casterOwner
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .casterOwner = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real casterX = GetUnitX(.caster)
local real casterY = GetUnitY(.caster)
local integer casterOrder = GetUnitCurrentOrder(.caster)
local group enemies = CreateGroup()
local unit picked
local real pickedMissingHP
local real dmgAmount
if .remainingDuration > 0 and IsAlive(.caster) and casterOrder == OrderId(SPELL_ORDER) then
set .remainingDuration = .remainingDuration - INTERVAL
call GroupEnumUnitsWithCollision(enemies, casterX, casterY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, .casterOwner) then
set pickedMissingHP = GetUnitState(picked, UNIT_STATE_MAX_LIFE) - GetUnitState(picked, UNIT_STATE_LIFE)
set dmgAmount = (BASE_DMG / 2) + ((pickedMissingHP * DMG_PERC) / 2)
call UnitDamageTarget(.caster, picked, dmgAmount, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
else
call .destroy()
endif
call DestroyGroup(enemies)
set enemies = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
set .caster = GetSpellAbilityUnit()
set .casterOwner = GetOwningPlayer(.caster)
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Upgrades initializer Init
private function onStart takes nothing returns nothing
local unit building = GetResearchingUnit()
local player owner = GetOwningPlayer(building)
local integer research = GetResearched()
if research == LOA_PTYAS then
if IsResearched(owner, LOA_ALTHEKK) then
call IssueImmediateOrderById(building, 851976)
else
call SetPlayerTechMaxAllowed(owner, LOA_ALTHEKK, 0)
endif
elseif research == LOA_ALTHEKK then
if IsResearched(owner, LOA_PTYAS) then
call IssueImmediateOrderById(building, 851976)
else
call SetPlayerTechMaxAllowed(owner, LOA_PTYAS, 0)
endif
elseif research == LOA_MAQTANS then
if IsResearched(owner, LOA_NANZLUM) then
call IssueImmediateOrderById(building, 851976)
else
call SetPlayerTechMaxAllowed(owner, LOA_NANZLUM, 0)
endif
elseif research == LOA_NANZLUM then
if IsResearched(owner, LOA_MAQTANS) then
call IssueImmediateOrderById(building, 851976)
else
call SetPlayerTechMaxAllowed(owner, LOA_MAQTANS, 0)
endif
elseif research == LOA_SHYLAZZI then
if IsResearched(owner, LOA_ZWAMBADI) then
call IssueImmediateOrderById(building, 851976)
else
call SetPlayerTechMaxAllowed(owner, LOA_ZWAMBADI, 0)
endif
elseif research == LOA_ZWAMBADI then
if IsResearched(owner, LOA_SHYLAZZI) then
call IssueImmediateOrderById(building, 851976)
else
call SetPlayerTechMaxAllowed(owner, LOA_SHYLAZZI, 0)
endif
endif
set building = null
set owner = null
endfunction
private function onFinish takes nothing returns nothing
local unit building = GetResearchingUnit()
local player owner = GetOwningPlayer(building)
local integer research = GetResearched()
if research == LOA_PTYAS or research == LOA_ALTHEKK then
call SetPlayerTechMaxAllowed(owner, LOA_MAQTANS, 1)
call SetPlayerTechMaxAllowed(owner, LOA_NANZLUM, 1)
call SetPlayerTechResearched(owner, FIRST_LOA, 1)
elseif research == LOA_MAQTANS or research == LOA_NANZLUM then
call SetPlayerTechMaxAllowed(owner, LOA_SHYLAZZI, 1)
call SetPlayerTechMaxAllowed(owner, LOA_ZWAMBADI, 1)
call SetPlayerTechResearched(owner, SECOND_LOA, 1)
elseif research == LOA_SHYLAZZI or research == LOA_ZWAMBADI then
call SetPlayerTechResearched(owner, THIRD_LOA, 1)
endif
if research == LOA_PTYAS then
call SetPlayerTechMaxAllowed(owner, UNIT_MEDIUM, -1)
call SetPlayerTechMaxAllowed(owner, UPG_MEDIUM, 1)
call SetPlayerTechMaxAllowed(owner, UPG1_PTYAS, 1)
call SetPlayerTechMaxAllowed(owner, UPG2_PTYAS, 1)
elseif research == LOA_ALTHEKK then
call SetPlayerTechMaxAllowed(owner, UNIT_RIPPER, -1)
call SetPlayerTechMaxAllowed(owner, UPG_RIPPER, 1)
call SetPlayerTechMaxAllowed(owner, UPG1_ALTHEKK, 1)
call SetPlayerTechMaxAllowed(owner, UPG2_ALTHEKK, 1)
elseif research == LOA_MAQTANS then
call SetPlayerTechMaxAllowed(owner, UNIT_SHDWEAVER, -1)
call SetPlayerTechMaxAllowed(owner, UPG_SHDWEAVER, 1)
call SetPlayerTechMaxAllowed(owner, UPG1_MAQTANS, 1)
call SetPlayerTechMaxAllowed(owner, UPG2_MAQTANS, 1)
elseif research == LOA_NANZLUM then
call SetPlayerTechMaxAllowed(owner, UNIT_BAT, -1)
call SetPlayerTechMaxAllowed(owner, UPG_BAT, 1)
call SetPlayerTechMaxAllowed(owner, UPG1_NANZLUM, 1)
call SetPlayerTechMaxAllowed(owner, UPG2_NANZLUM, 1)
elseif research == LOA_SHYLAZZI then
call SetPlayerTechMaxAllowed(owner, UNIT_MANTICORE, -1)
call SetPlayerTechMaxAllowed(owner, UPG_MANTICORE, 1)
call SetPlayerTechMaxAllowed(owner, UPG1_SHYLAZZI, 1)
call SetPlayerTechMaxAllowed(owner, UPG2_SHYLAZZI, 1)
elseif research == LOA_ZWAMBADI then
call SetPlayerTechMaxAllowed(owner, UNIT_DIREHORN, -1)
call SetPlayerTechMaxAllowed(owner, UPG_DIREHORN, 1)
call SetPlayerTechMaxAllowed(owner, UPG1_ZWAMBADI, 1)
call SetPlayerTechMaxAllowed(owner, UPG2_ZWAMBADI, 1)
endif
if research == UPG_BAT then
call SetPlayerTechMaxAllowed(owner, UNIT_BAT, 0)
call SetPlayerTechMaxAllowed(owner, UNIT_BAT_2, -1)
endif
set building = null
set owner = null
endfunction
private function onCancel takes nothing returns nothing
local unit building = GetResearchingUnit()
local player owner = GetOwningPlayer(building)
local integer research = GetResearched()
if research == LOA_PTYAS then
if not IsResearched(owner, LOA_ALTHEKK) then
call SetPlayerTechMaxAllowed(owner, LOA_ALTHEKK, 1)
endif
elseif research == LOA_ALTHEKK then
if not IsResearched(owner, LOA_PTYAS) then
call SetPlayerTechMaxAllowed(owner, LOA_PTYAS, 1)
endif
elseif research == LOA_MAQTANS then
if not IsResearched(owner, LOA_NANZLUM) then
call SetPlayerTechMaxAllowed(owner, LOA_NANZLUM, 1)
endif
elseif research == LOA_NANZLUM then
if not IsResearched(owner, LOA_MAQTANS) then
call SetPlayerTechMaxAllowed(owner, LOA_MAQTANS, 1)
endif
elseif research == LOA_SHYLAZZI then
if not IsResearched(owner, LOA_ZWAMBADI) then
call SetPlayerTechMaxAllowed(owner, LOA_ZWAMBADI, 1)
endif
elseif research == LOA_ZWAMBADI then
if not IsResearched(owner, LOA_SHYLAZZI) then
call SetPlayerTechMaxAllowed(owner, LOA_SHYLAZZI, 1)
endif
endif
set building = null
set owner = null
endfunction
private function Init takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_RESEARCH_START, function onStart)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_RESEARCH_FINISH, function onFinish)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_RESEARCH_CANCEL, function onCancel)
endfunction
endscope
library MapSetup initializer Init
globals
constant integer FIRST_LOA = 'RX00'
constant integer SECOND_LOA = 'RX01'
constant integer THIRD_LOA = 'RX02'
constant integer LOA_PTYAS = 'R000'
constant integer LOA_ALTHEKK = 'R001'
constant integer LOA_MAQTANS = 'R002'
constant integer LOA_NANZLUM = 'R003'
constant integer LOA_SHYLAZZI = 'R004'
constant integer LOA_ZWAMBADI = 'R005'
constant integer UNIT_MEDIUM = 'bt08'
constant integer UNIT_RIPPER = 'bt09'
constant integer UNIT_BAT = 'bt10'
constant integer UNIT_BAT_2 = 'bt14'
constant integer UNIT_SHDWEAVER = 'bt11'
constant integer UNIT_DIREHORN = 'bt12'
constant integer UNIT_MANTICORE = 'bt13'
constant integer UPG1_PTYAS = 'R006'
constant integer UPG2_PTYAS = 'R007'
constant integer UPG1_ALTHEKK = 'R008'
constant integer UPG2_ALTHEKK = 'R009'
constant integer UPG1_MAQTANS = 'R00A'
constant integer UPG2_MAQTANS = 'R00B'
constant integer UPG1_NANZLUM = 'R00C'
constant integer UPG2_NANZLUM = 'R00D'
constant integer UPG1_SHYLAZZI = 'R00E'
constant integer UPG2_SHYLAZZI = 'R00F'
constant integer UPG1_ZWAMBADI = 'R00G'
constant integer UPG2_ZWAMBADI = 'R00H'
constant integer UPG_RIPPER = 'R00P'
constant integer UPG_MEDIUM = 'R00Q'
constant integer UPG_BAT = 'R00W'
constant integer UPG_SHDWEAVER = 'R00X'
constant integer UPG_MANTICORE = 'R00U'
constant integer UPG_DIREHORN = 'R00V'
integer array HUNTING_PACK_LVL
endglobals
private function Initialization takes nothing returns nothing
local integer i = 0
loop
exitwhen i == bj_MAX_PLAYERS
//Books
call SetPlayerAbilityAvailable(Player(i), 'A00G', false)
call SetPlayerAbilityAvailable(Player(i), 'A00H', false)
call SetPlayerAbilityAvailable(Player(i), 'A01E', false)
//Tiers
call SetPlayerTechMaxAllowed(Player(i), LOA_MAQTANS, 0)
call SetPlayerTechMaxAllowed(Player(i), LOA_NANZLUM, 0)
call SetPlayerTechMaxAllowed(Player(i), LOA_SHYLAZZI, 0)
call SetPlayerTechMaxAllowed(Player(i), LOA_ZWAMBADI, 0)
//Units
call SetPlayerTechMaxAllowed(Player(i), UNIT_MEDIUM, 0)
call SetPlayerTechMaxAllowed(Player(i), UNIT_RIPPER, 0)
call SetPlayerTechMaxAllowed(Player(i), UNIT_BAT, 0)
call SetPlayerTechMaxAllowed(Player(i), UNIT_BAT_2, 0)
call SetPlayerTechMaxAllowed(Player(i), UNIT_SHDWEAVER, 0)
call SetPlayerTechMaxAllowed(Player(i), UNIT_DIREHORN, 0)
call SetPlayerTechMaxAllowed(Player(i), UNIT_MANTICORE, 0)
//Upgrades
call SetPlayerTechMaxAllowed(Player(i), UPG1_PTYAS, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG2_PTYAS, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG1_ALTHEKK, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG2_ALTHEKK, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG1_MAQTANS, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG2_MAQTANS, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG1_NANZLUM, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG2_NANZLUM, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG1_SHYLAZZI, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG2_SHYLAZZI, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG1_ZWAMBADI, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG2_ZWAMBADI, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG_MEDIUM, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG_RIPPER, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG_BAT, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG_SHDWEAVER, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG_MANTICORE, 0)
call SetPlayerTechMaxAllowed(Player(i), UPG_DIREHORN, 0)
set i = i + 1
endloop
set HUNTING_PACK_LVL[0] = 'A01G'
set HUNTING_PACK_LVL[1] = 'A01H'
set HUNTING_PACK_LVL[2] = 'A01I'
set HUNTING_PACK_LVL[3] = 'A01J'
set HUNTING_PACK_LVL[4] = 'A01K'
set HUNTING_PACK_LVL[5] = 'A01L'
set HUNTING_PACK_LVL[6] = 'A01M'
endfunction
private function Init takes nothing returns nothing
call Initialization()
endfunction
endlibrary