Name | Type | is_array | initial_value |
a | location | No | |
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 | |
DeathEvent | real | No | |
DmgEvBracers | itemcode | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
EnhancedDamageTarget | unit | No | |
heal_amount | real | No | |
heal_check | boolean | No | |
HEAL_CHECK_INTERVAL | real | No | |
heal_count | integer | No | |
heal_diff | real | No | |
heal_exitwhen | integer | No | |
heal_indexRef | integer | Yes | |
heal_indices | integer | Yes | |
heal_inSys | boolean | Yes | |
heal_integer | integervar | No | |
heal_lastLife | real | Yes | |
heal_life | real | No | |
heal_regen | real | Yes | |
heal_source | unit | No | |
heal_target | unit | No | |
HEAL_THRESHOLD | real | No | |
heal_timer | timer | No | |
HealEvent | real | 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 | |
NextHealAmount | real | No | |
NextHealSource | unit | No | |
NextHealTarget | unit | No | |
regen_buildup | real | Yes | |
REGEN_EVENT_INTERVAL | real | No | |
REGEN_STRENGTH_VALUE | real | No | |
REGEN_THRESHOLD | real | No | |
regen_timeleft | real | Yes | |
SpellDamageAbility | abilcode | No | |
TempInteger | integer | 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 HasBuff 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 GetUnitMissingLife takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_LIFE) - GetUnitState(u, UNIT_STATE_LIFE)
endfunction
function GetUnitMissingMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_MANA) - GetUnitState(u, UNIT_STATE_MANA)
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 IsDestTree takes destructable d returns boolean
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hpea', 0, 0, 0)
local boolean isTree = false
if IssueTargetOrderById(u,ORDER_harvest,d) then
set isTree = true
endif
call RemoveUnit(u)
set u = null
return isTree
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 InRangeFilter 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 InRangeFilter))
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
library FloatingText
globals
private player Play
endglobals
private function ForceFilter takes nothing returns boolean
return GetFilterPlayer() != Play
endfunction
struct FloatingText
private texttag text
private method onDestroy takes nothing returns nothing
set .text = null
endmethod
method runText takes nothing returns nothing
local real vel = 64. * 0.071 / 128
local real xvel = vel * Cos(90. * bj_DEGTORAD)
local real yvel = vel * Sin(90. * bj_DEGTORAD)
call SetTextTagPermanent(.text, false)
call SetTextTagVelocity(.text, xvel, yvel)
call SetTextTagLifespan(.text, 2.)
call SetTextTagFadepoint(.text, 1.)
call .destroy()
endmethod
method showOnlyToPlayer takes player p returns nothing
local force f = CreateForce()
set Play = p
call ForceEnumPlayers(f, Condition(function ForceFilter))
if IsPlayerInForce(GetLocalPlayer(), f) then
call SetTextTagVisibility(.text, false)
endif
call DestroyForce(f)
set f = null
set Play = null
endmethod
method overUnit takes unit u returns nothing
call SetTextTagPosUnit(.text, u, 20.)
endmethod
method overLoc takes real x, real y returns nothing
call SetTextTagPos(.text, x, y, 20.)
endmethod
method msgSetup takes string msg, integer red, integer green, integer blue, integer alpha returns nothing
call SetTextTagText(.text, msg, 10 * 0.023 / 10)
call SetTextTagColor(.text, red, green, blue, 255-alpha)
endmethod
static method create takes nothing returns FloatingText
local FloatingText new = FloatingText.allocate()
set new.text = CreateTextTag()
return new
endmethod
endstruct
endlibrary
library OrderIds //v1.0 hiveworkshop.com/threads/orders-repo.290882/
globals
constant integer ORDER_wandillusion=852274
constant integer ORDER_absorb=852529
constant integer ORDER_acidbomb=852662
constant integer ORDER_acolyteharvest=852185
constant integer ORDER_AImove=851988
constant integer ORDER_ambush=852131
constant integer ORDER_ancestralspirit=852490
constant integer ORDER_ancestralspirittarget=852491
constant integer ORDER_animatedead=852217
constant integer ORDER_antimagicshell=852186
constant integer ORDER_attack=851983
constant integer ORDER_attackground=851984
constant integer ORDER_attackonce=851985
constant integer ORDER_attributemodskill=852576
constant integer ORDER_auraunholy=852215
constant integer ORDER_auravampiric=852216
constant integer ORDER_autodispel=852132
constant integer ORDER_autodispeloff=852134
constant integer ORDER_autodispelon=852133
constant integer ORDER_autoentangle=852505
constant integer ORDER_autoentangleinstant=852506
constant integer ORDER_autoharvestgold=852021
constant integer ORDER_autoharvestlumber=852022
constant integer ORDER_avatar=852086
constant integer ORDER_avengerform=852531
constant integer ORDER_awaken=852466
constant integer ORDER_banish=852486
constant integer ORDER_barkskin=852135
constant integer ORDER_barkskinoff=852137
constant integer ORDER_barkskinon=852136
constant integer ORDER_battleroar=852099
constant integer ORDER_battlestations=852099
constant integer ORDER_bearform=852138
constant integer ORDER_berserk=852100
constant integer ORDER_blackarrow=852577
constant integer ORDER_blackarrowoff=852579
constant integer ORDER_blackarrowon=852578
constant integer ORDER_blight=852187
constant integer ORDER_blink=852525
constant integer ORDER_blizzard=852089
constant integer ORDER_bloodlust=852101
constant integer ORDER_bloodlustoff=852103
constant integer ORDER_bloodluston=852102
constant integer ORDER_board=852043
constant integer ORDER_breathoffire=852580
constant integer ORDER_breathoffrost=852560
constant integer ORDER_build=851994
constant integer ORDER_burrow=852533
constant integer ORDER_cannibalize=852188
constant integer ORDER_carrionscarabs=852551
constant integer ORDER_carrionscarabsinstant=852554
constant integer ORDER_carrionscarabsoff=852553
constant integer ORDER_carrionscarabson=852552
constant integer ORDER_carrionswarm=852218
constant integer ORDER_chainlightning=852119
constant integer ORDER_channel=852600
constant integer ORDER_charm=852581
constant integer ORDER_chemicalrage=852663
constant integer ORDER_cloudoffog=852473
constant integer ORDER_clusterrockets=852652
constant integer ORDER_coldarrows=852244
constant integer ORDER_coldarrowstarg=852243
constant integer ORDER_controlmagic=852474
constant integer ORDER_corporealform=852493
constant integer ORDER_corrosivebreath=852140
constant integer ORDER_coupleinstant=852508
constant integer ORDER_coupletarget=852507
constant integer ORDER_creepanimatedead=852246
constant integer ORDER_creepdevour=852247
constant integer ORDER_creepheal=852248
constant integer ORDER_creephealoff=852250
constant integer ORDER_creephealon=852249
constant integer ORDER_creepthunderbolt=852252
constant integer ORDER_creepthunderclap=852253
constant integer ORDER_cripple=852189
constant integer ORDER_curse=852190
constant integer ORDER_curseoff=852192
constant integer ORDER_curseon=852191
constant integer ORDER_cyclone=852144
constant integer ORDER_darkconversion=852228
constant integer ORDER_darkportal=852229
constant integer ORDER_darkritual=852219
constant integer ORDER_darksummoning=852220
constant integer ORDER_deathanddecay=852221
constant integer ORDER_deathcoil=852222
constant integer ORDER_deathpact=852223
constant integer ORDER_decouple=852509
constant integer ORDER_defend=852055
constant integer ORDER_detectaoe=852015
constant integer ORDER_detonate=852145
constant integer ORDER_devour=852104
constant integer ORDER_devourmagic=852536
constant integer ORDER_disassociate=852240
constant integer ORDER_disenchant=852495
constant integer ORDER_dismount=852470
constant integer ORDER_dispel=852057
constant integer ORDER_divineshield=852090
constant integer ORDER_doom=852583
constant integer ORDER_drain=852487
constant integer ORDER_dreadlordinferno=852224
constant integer ORDER_dropitem=852001
constant integer ORDER_drunkenhaze=852585
constant integer ORDER_earthquake=852121
constant integer ORDER_eattree=852146
constant integer ORDER_elementalfury=852586
constant integer ORDER_ensnare=852106
constant integer ORDER_ensnareoff=852108
constant integer ORDER_ensnareon=852107
constant integer ORDER_entangle=852147
constant integer ORDER_entangleinstant=852148
constant integer ORDER_entanglingroots=852171
constant integer ORDER_etherealform=852496
constant integer ORDER_evileye=852105
constant integer ORDER_faeriefire=852149
constant integer ORDER_faeriefireoff=852151
constant integer ORDER_faeriefireon=852150
constant integer ORDER_fanofknives=852526
constant integer ORDER_farsight=852122
constant integer ORDER_fingerofdeath=852230
constant integer ORDER_firebolt=852231
constant integer ORDER_flamestrike=852488
constant integer ORDER_flamingarrows=852174
constant integer ORDER_flamingarrowstarg=852173
constant integer ORDER_flamingattack=852540
constant integer ORDER_flamingattacktarg=852539
constant integer ORDER_flare=852060
constant integer ORDER_forceboard=852044
constant integer ORDER_forceofnature=852176
constant integer ORDER_forkedlightning=852586
constant integer ORDER_freezingbreath=852195
constant integer ORDER_frenzy=852561
constant integer ORDER_frenzyoff=852563
constant integer ORDER_frenzyon=852562
constant integer ORDER_frostarmor=852225
constant integer ORDER_frostarmoroff=852459
constant integer ORDER_frostarmoron=852458
constant integer ORDER_frostnova=852226
constant integer ORDER_getitem=851981
constant integer ORDER_gold2lumber=852233
constant integer ORDER_grabtree=852511
constant integer ORDER_harvest=852018
constant integer ORDER_heal=852063
constant integer ORDER_healingspray=852664
constant integer ORDER_healingward=852109
constant integer ORDER_healingwave=852501
constant integer ORDER_healoff=852065
constant integer ORDER_healon=852064
constant integer ORDER_hex=852502
constant integer ORDER_holdposition=851993
constant integer ORDER_holybolt=852092
constant integer ORDER_howlofterror=852588
constant integer ORDER_humanbuild=851995
constant integer ORDER_immolation=852177
constant integer ORDER_impale=852555
constant integer ORDER_incineratearrow=852670
constant integer ORDER_incineratearrowoff=852672
constant integer ORDER_incineratearrowon=852671
constant integer ORDER_inferno=852232
constant integer ORDER_innerfire=852066
constant integer ORDER_innerfireoff=852068
constant integer ORDER_innerfireon=852067
constant integer ORDER_instant=852200
constant integer ORDER_invisibility=852069
constant integer ORDER_lavamonster=852667
constant integer ORDER_lightningshield=852110
constant integer ORDER_load=852046
constant integer ORDER_loadarcher = 852142
constant integer ORDER_loadcorpse=852050
constant integer ORDER_loadcorpseinstant=852053
constant integer ORDER_locustswarm=852556
constant integer ORDER_lumber2gold=852234
constant integer ORDER_magicdefense=852478
constant integer ORDER_magicleash=852480
constant integer ORDER_magicundefense=852479
constant integer ORDER_manaburn=852179
constant integer ORDER_manaflareoff=852513
constant integer ORDER_manaflareon=852512
constant integer ORDER_manashieldoff=852590
constant integer ORDER_manashieldon=852589
constant integer ORDER_massteleport=852093
constant integer ORDER_mechanicalcritter=852564
constant integer ORDER_metamorphosis=852180
constant integer ORDER_militia=852072
constant integer ORDER_militiaconvert=852071
constant integer ORDER_militiaoff=852073
constant integer ORDER_militiaunconvert=852651
constant integer ORDER_mindrot=852565
constant integer ORDER_mirrorimage=852123
constant integer ORDER_monsoon=852591
constant integer ORDER_mount=852469
constant integer ORDER_mounthippogryph=852143
constant integer ORDER_move=851986
constant integer ORDER_nagabuild=852467
constant integer ORDER_neutraldetectaoe=852023
constant integer ORDER_neutralinteract=852566
constant integer ORDER_neutralspell=852630
constant integer ORDER_nightelfbuild=851997
constant integer ORDER_orcbuild=851996
constant integer ORDER_parasite=852601
constant integer ORDER_parasiteoff=852603
constant integer ORDER_parasiteon=852602
constant integer ORDER_patrol=851990
constant integer ORDER_phaseshift=852514
constant integer ORDER_phaseshiftinstant=852517
constant integer ORDER_phaseshiftoff=852516
constant integer ORDER_phaseshifton=852515
constant integer ORDER_phoenixfire=852481
constant integer ORDER_phoenixmorph=852482
constant integer ORDER_poisonarrows=852255
constant integer ORDER_poisonarrowstarg=852254
constant integer ORDER_polymorph=852074
constant integer ORDER_possession=852196
constant integer ORDER_preservation=852568
constant integer ORDER_purge=852111
constant integer ORDER_rainofchaos=852237
constant integer ORDER_rainoffire=852238
constant integer ORDER_raisedead=852197
constant integer ORDER_raisedeadoff=852199
constant integer ORDER_raisedeadon=852198
constant integer ORDER_ravenform=852155
constant integer ORDER_recharge=852157
constant integer ORDER_rechargeoff=852159
constant integer ORDER_rechargeon=852158
constant integer ORDER_rejuvination=852160
constant integer ORDER_renew=852161
constant integer ORDER_renewoff=852163
constant integer ORDER_renewon=852162
constant integer ORDER_repair=852024
constant integer ORDER_repairoff=852026
constant integer ORDER_repairon=852025
constant integer ORDER_replenish=852542
constant integer ORDER_replenishlife=852545
constant integer ORDER_replenishlifeoff=852547
constant integer ORDER_replenishlifeon=852546
constant integer ORDER_replenishmana=852548
constant integer ORDER_replenishmanaoff=852550
constant integer ORDER_replenishmanaon=852549
constant integer ORDER_replenishoff=852544
constant integer ORDER_replenishon=852543
constant integer ORDER_request_hero=852239
constant integer ORDER_requestsacrifice=852201
constant integer ORDER_restoration=852202
constant integer ORDER_restorationoff=852204
constant integer ORDER_restorationon=852203
constant integer ORDER_resumebuild=851999
constant integer ORDER_resumeharvesting=852017
constant integer ORDER_resurrection=852094
constant integer ORDER_returnresources=852020
constant integer ORDER_revenge=852241
constant integer ORDER_revive=852039
constant integer ORDER_roar=852164
constant integer ORDER_robogoblin=852656
constant integer ORDER_root=852165
constant integer ORDER_sacrifice=852205
constant integer ORDER_sanctuary=852569
constant integer ORDER_scout=852181
constant integer ORDER_selfdestruct=852040
constant integer ORDER_selfdestructoff=852042
constant integer ORDER_selfdestructon=852041
constant integer ORDER_sentinel=852182
constant integer ORDER_setrally=851980
constant integer ORDER_shadowsight=852570
constant integer ORDER_shadowstrike=852527
constant integer ORDER_shockwave=852125
constant integer ORDER_silence=852592
constant integer ORDER_sleep=852227
constant integer ORDER_slow=852075
constant integer ORDER_slowoff=852077
constant integer ORDER_slowon=852076
constant integer ORDER_smart=851971
constant integer ORDER_soulburn=852668
constant integer ORDER_soulpreservation=852242
constant integer ORDER_spellshield=852571
constant integer ORDER_spellshieldaoe=852572
constant integer ORDER_spellsteal=852483
constant integer ORDER_spellstealoff=852485
constant integer ORDER_spellstealon=852484
constant integer ORDER_spies=852235
constant integer ORDER_spiritlink=852499
constant integer ORDER_spiritofvengeance=852528
constant integer ORDER_spirittroll=852573
constant integer ORDER_spiritwolf=852126
constant integer ORDER_stampede=852593
constant integer ORDER_standdown=852113
constant integer ORDER_starfall=852183
constant integer ORDER_stasistrap=852114
constant integer ORDER_steal=852574
constant integer ORDER_stomp=852127
constant integer ORDER_stoneform=852206
constant integer ORDER_stop=851972
constant integer ORDER_submerge=852604
constant integer ORDER_summonfactory=852658
constant integer ORDER_summongrizzly=852594
constant integer ORDER_summonphoenix=852489
constant integer ORDER_summonquillbeast=852595
constant integer ORDER_summonwareagle=852596
constant integer ORDER_tankdroppilot=852079
constant integer ORDER_tankloadpilot=852080
constant integer ORDER_tankpilot=852081
constant integer ORDER_taunt=852520
constant integer ORDER_thunderbolt=852095
constant integer ORDER_thunderclap=852096
constant integer ORDER_tornado=852597
constant integer ORDER_townbelloff=852083
constant integer ORDER_townbellon=852082
constant integer ORDER_tranquility=852184
constant integer ORDER_transmute=852665
constant integer ORDER_unavatar=852087
constant integer ORDER_unavengerform=852532
constant integer ORDER_unbearform=852139
constant integer ORDER_unburrow=852534
constant integer ORDER_uncoldarrows=852245
constant integer ORDER_uncorporealform=852494
constant integer ORDER_undeadbuild=851998
constant integer ORDER_undefend=852056
constant integer ORDER_undivineshield=852091
constant integer ORDER_unetherealform=852497
constant integer ORDER_unflamingarrows=852175
constant integer ORDER_unflamingattack=852541
constant integer ORDER_unholyfrenzy=852209
constant integer ORDER_unimmolation=852178
constant integer ORDER_unload=852047
constant integer ORDER_unloadall=852048
constant integer ORDER_unloadallcorpses=852054
constant integer ORDER_unloadallinstant=852049
constant integer ORDER_unpoisonarrows=852256
constant integer ORDER_unravenform=852156
constant integer ORDER_unrobogoblin=852657
constant integer ORDER_unroot=852166
constant integer ORDER_unstableconcoction=852500
constant integer ORDER_unstoneform=852207
constant integer ORDER_unsubmerge=852605
constant integer ORDER_unsummon=852210
constant integer ORDER_unwindwalk=852130
constant integer ORDER_vengeance=852521
constant integer ORDER_vengeanceinstant=852524
constant integer ORDER_vengeanceoff=852523
constant integer ORDER_vengeanceon=852522
constant integer ORDER_volcano=852669
constant integer ORDER_voodoo=852503
constant integer ORDER_ward=852504
constant integer ORDER_waterelemental=852097
constant integer ORDER_wateryminion=852598
constant integer ORDER_web=852211
constant integer ORDER_weboff=852213
constant integer ORDER_webon=852212
constant integer ORDER_whirlwind=852128
constant integer ORDER_windwalk=852129
constant integer ORDER_wispharvest=852214
constant integer ORDER_buildtiny=852619
constant integer ORDER_scrollofspeed=852285
constant integer ORDER_scrollofregeneration=852609
constant integer ORDER_cancel=851976
constant integer ORDER_moveslot1=852002
constant integer ORDER_moveslot2=852003
constant integer ORDER_moveslot3=852004
constant integer ORDER_moveslot4=852005
constant integer ORDER_moveslot5=852006
constant integer ORDER_moveslot6=852007
constant integer ORDER_useslot1=852008
constant integer ORDER_useslot2=852009
constant integer ORDER_useslot3=852010
constant integer ORDER_useslot4=852011
constant integer ORDER_useslot5=852012
constant integer ORDER_useslot6=852013
constant integer ORDER_skillmenu=852000
constant integer ORDER_stunned=851973
constant integer ORDER_instant1=851991 //patrol sub-order
constant integer ORDER_instant2=851987 //?
constant integer ORDER_instant3=851975 //?
constant integer ORDER_instant4=852019 //?
endglobals
endlibrary
//===========================================================================
// Damage Engine 3A.0.0.0 - a new type of Damage Engine for users who don't
// have access to the latest version of WarCraft 3, which incorporates new
// features to inherited from Damage Engine 5.7 by hooking TriggerRegisterVariableEvent.
// However, it requires having JassHelper installed.
//
// Stuff that doesn't work:
// - Pre-armor modification
// - Damage/Attack/Weapotype detection/modification
// - Armor/defense detection/modification
// - Melee/ranged detection
// - Filters for u.
// - Spirit link won't interact with custom damage.
// - Still needs workarounds for Anti-Magic Shell/Mana Shield/Life Drain/etc.
//
// Stuff that is changed from how it worked with 3.8:
// - Recursive damage now uses the Damage Engine 5 anti-recursion method. So
// all recursive damage will be postponed until the sequence has completed.
// - No more need for using ClearDamageEvent
// - No more need to disable the DamageEventTrigger in order to avoid things
// going recursively.
//
library DamageEngine
globals
private timer alarm = CreateTimer()
private boolean alarmSet = false
//Values to track the original pre-spirit Link/defensive damage values
private Damage lastInstance = 0
private boolean canKick = false
//These variables coincide with Blizzard's "limitop" type definitions so as to enable users (GUI in particular) with some nice performance perks.
public constant integer FILTER_ATTACK = 0 //LESS_THAN
public constant integer FILTER_OTHER = 2 //EQUAL
public constant integer FILTER_SPELL = 4 //GREATER_THAN
public constant integer FILTER_CODE = 5 //NOT_EQUAL
public constant integer FILTER_MAX = 6
private integer eventFilter = FILTER_OTHER
private constant integer LIMBO = 16 //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
public boolean inception = false //When true, it allows your trigger to potentially go recursive up to LIMBO. However it must be set per-trigger throughout the game and not only once per trigger during map initialization.
private boolean dreaming = false
private integer sleepLevel = 0
private group proclusGlobal = CreateGroup() //track sources of recursion
private group fischerMorrow = CreateGroup() //track targets of recursion
private boolean kicking = false
private boolean eventsRun = false
private unit protectUnit = null
private real protectLife = 0.00
private boolean blocked = false
private keyword run
private keyword trigFrozen
private keyword levelsDeep
private keyword inceptionTrig
private keyword checkLife
private keyword lifeTrigger
endglobals
private function CheckAddUnitToEngine takes unit u returns boolean
if GetUnitAbilityLevel(u, 'Aloc') > 0 then
elseif not TriggerEvaluate(gg_trg_Damage_Engine_Config) then
//Add some more elseifs to rule out stuff you don't want to get registered, such as:
//elseif IsUnitType(u, UNIT_TYPE_STRUCTURE) then
else
return true
endif
return false
endfunction
struct DamageTrigger extends array
//The below variables are constant
readonly static thistype MOD = 1
readonly static thistype DAMAGE = 5
readonly static thistype ZERO = 6
readonly static thistype AFTER = 7
readonly static thistype AOE = 9
private static integer count = 9
static thistype lastRegistered = 0
private static thistype array trigIndexStack
static thistype eventIndex = 0
static boolean array filters
readonly string eventStr
readonly real weight
readonly boolean configured
boolean usingGUI
//The below variables are private
private thistype next
private trigger rootTrig
boolean trigFrozen //Whether the trigger is currently disabled due to recursion
integer levelsDeep //How deep the user recursion currently is.
boolean inceptionTrig //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
static method operator enabled= takes boolean b returns nothing
if b then
call EnableTrigger(udg_DamageEventTrigger)
else
call DisableTrigger(udg_DamageEventTrigger)
endif
endmethod
static method operator enabled takes nothing returns boolean
return IsTriggerEnabled(udg_DamageEventTrigger)
endmethod
static method setGUIFromStruct takes boolean full returns nothing
set udg_DamageEventAmount = Damage.index.damage
set udg_DamageEventType = Damage.index.userType
set udg_DamageEventOverride = Damage.index.override
if full then
set udg_DamageEventSource = Damage.index.sourceUnit
set udg_DamageEventTarget = Damage.index.targetUnit
set udg_DamageEventPrevAmt = Damage.index.prevAmt
set udg_IsDamageSpell = Damage.index.isSpell
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_05()
endif
endmethod
static method getVerboseStr takes string eventName returns string
if eventName == "Modifier" or eventName == "Mod" then
return "udg_DamageModifierEvent"
endif
return "udg_" + eventName + "DamageEvent"
endmethod
private static method getStrIndex takes string var, real lbs returns thistype
local integer root = R2I(lbs)
if var == "udg_DamageModifierEvent" then
set root= MOD
elseif var == "udg_DamageEvent" then
if root == 2 or root == 0 then
set root= ZERO
else
set root= DAMAGE //Above 0.00 but less than 2.00, generally would just be 1.00
endif
elseif var == "udg_AfterDamageEvent" then
set root = AFTER
elseif var == "udg_AOEDamageEvent" then
set root = AOE
else
set root = 0
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_05()
endif
return root
endmethod
private method toggleAllFilters takes boolean flag returns nothing
set filters[this + FILTER_ATTACK] = flag
set filters[this + FILTER_OTHER] = flag
set filters[this + FILTER_SPELL] = flag
set filters[this + FILTER_CODE] = flag
endmethod
method operator filter= takes integer f returns nothing
set this = this*FILTER_MAX
if f == FILTER_OTHER then
call this.toggleAllFilters(true)
else
if f == FILTER_ATTACK then
set filters[this + FILTER_ATTACK] = true
else
set filters[this + f] = true
endif
endif
endmethod
static method registerVerbose takes trigger whichTrig, string var, real lbs, boolean GUI, integer filt returns thistype
local thistype index= getStrIndex(var, lbs)
local thistype i = 0
local thistype id = 0
if index == 0 then
return 0
elseif lastRegistered.rootTrig == whichTrig and lastRegistered.usingGUI then
set filters[lastRegistered*FILTER_MAX + filt] = true //allows GUI to register multiple different types of Damage filters to the same trigger
return 0
endif
if trigIndexStack[0] == 0 then
set count = count + 1 //List runs from index 10 and up
set id = count
else
set id = trigIndexStack[0]
set trigIndexStack[0] = trigIndexStack[id]
endif
set lastRegistered = id
set id.filter = filt
set id.rootTrig = whichTrig
set id.usingGUI = GUI
set id.weight = lbs
set id.eventStr = var
loop
set i = index.next
exitwhen i == 0 or lbs < i.weight
set index = i
endloop
set index.next = id
set id.next = i
//call BJDebugMsg("Registered " + I2S(id) + " to " + I2S(index) + " and before " + I2S(i))
return lastRegistered
endmethod
static method registerTrigger takes trigger t, string var, real lbs returns thistype
return registerVerbose(t, DamageTrigger.getVerboseStr(var), lbs, false, FILTER_OTHER)
endmethod
private static thistype prev = 0
static method getIndex takes trigger t, string eventName, real lbs returns thistype
local thistype index = getStrIndex(getVerboseStr(eventName), lbs)
loop
set prev = index
set index = index.next
exitwhen index == 0 or index.rootTrig == t
endloop
return index
endmethod
static method unregister takes trigger t, string eventName, real lbs, boolean reset returns boolean
local thistype index = getIndex(t, eventName, lbs)
if index == 0 then
return false
endif
set prev.next = index.next
set trigIndexStack[index] = trigIndexStack[0]
set trigIndexStack[0] = index
if reset then
set index.configured = false
set index = index*FILTER_MAX
call index.toggleAllFilters(false)
endif
return true
endmethod
static method damageUnit takes unit u, real life returns nothing
call SetWidgetLife(u, RMaxBJ(life, 0.41))
if life <= 0.405 then
if udg_DamageEventType < 0 then
call SetUnitExploded(u, true)
endif
//Kill the unit
set DamageTrigger.enabled = false
call UnitDamageTarget(udg_DamageEventSource, u, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
set DamageTrigger.enabled = true
endif
endmethod
static method checkLife takes nothing returns boolean
if protectUnit != null then
if Damage.lifeTrigger != null then
call DestroyTrigger(Damage.lifeTrigger)
set Damage.lifeTrigger = null
endif
if GetUnitAbilityLevel(protectUnit, udg_DamageBlockingAbility) > 0 then
call UnitRemoveAbility(protectUnit, udg_DamageBlockingAbility)
call SetWidgetLife(protectUnit, protectLife)
elseif udg_IsDamageSpell or blocked then
call DamageTrigger.damageUnit(protectUnit, protectLife)
endif
if blocked then
set blocked = false
endif
set protectUnit = null
return true
endif
return false
endmethod
method run takes nothing returns nothing
local integer cat = this
local Damage d = Damage.index
if cat == MOD or not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set dreaming = true
//call BJDebugMsg("Start of event running")
loop
set this = this.next
exitwhen this == 0
if cat == MOD then
exitwhen d.override or udg_DamageEventOverride
exitwhen this.weight >= 4.00 and udg_DamageEventAmount <= 0.00
endif
set eventIndex = this
if not this.trigFrozen and filters[this*FILTER_MAX + eventFilter] and IsTriggerEnabled(this.rootTrig) then
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_05()
if TriggerEvaluate(this.rootTrig) then
call TriggerExecute(this.rootTrig)
endif
if cat == MOD then
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_05()
if this.usingGUI then
set d.damage = udg_DamageEventAmount
set d.userType = udg_DamageEventType
set d.override = udg_DamageEventOverride
elseif this.next == 0 or this.next.usingGUI then //Might offer a slight performance improvement
call setGUIFromStruct(false)
endif
endif
call checkLife()
endif
endloop
//call BJDebugMsg("End of event running")
set dreaming = false
endif
endmethod
static method finish takes nothing returns boolean
if checkLife() and not blocked and udg_DamageEventAmount != 0.00 then
call DamageTrigger.AFTER.run()
endif
return false
endmethod
static trigger array autoTriggers
static boolexpr array autoFuncs
static integer autoN = 0
static method operator [] takes code c returns trigger
local integer i = 0
local boolexpr b = Filter(c)
loop
if i == autoN then
set autoTriggers[i] = CreateTrigger()
set autoFuncs[i] = b
call TriggerAddCondition(autoTriggers[i], b)
exitwhen true
endif
set i = i + 1
exitwhen b == autoFuncs[i]
endloop
return autoTriggers[i]
endmethod
endstruct
struct Damage extends array
readonly unit sourceUnit //stores udg_DamageEventSource
readonly unit targetUnit //stores udg_DamageEventTarget
real damage //stores udg_DamageEventAmount
readonly real prevAmt //stores udg_DamageEventPrevAmt
integer userType //stores udg_DamageEventType
readonly boolean isCode
readonly boolean isSpell //stores udg_IsDamageSpell
boolean override
readonly static unit aoeSource = null
readonly static Damage index = 0
private static Damage damageStack = 0
private static integer count = 0 //The number of currently-running queued or sequential damage instances
private Damage stackRef
private DamageTrigger recursiveTrig
static trigger lifeTrigger = null //private
static method operator source takes nothing returns unit
return udg_DamageEventSource
endmethod
static method operator target takes nothing returns unit
return udg_DamageEventTarget
endmethod
static method operator amount takes nothing returns real
return Damage.index.damage
endmethod
static method operator amount= takes real r returns nothing
set Damage.index.damage = r
endmethod
private static method onAOEEnd takes nothing returns nothing
if udg_DamageEventAOE > 1 then
call DamageTrigger.AOE.run()
endif
set udg_DamageEventAOE = 0
set udg_DamageEventLevel = 0
set udg_EnhancedDamageTarget = null
set aoeSource = null
call GroupClear(udg_DamageEventAOEGroup)
endmethod
static method finish takes nothing returns nothing
local Damage i = 0
local integer exit
if canKick then
set canKick = false
set kicking = true
call DamageTrigger.finish()
if damageStack != 0 then
loop
set exit = damageStack
set sleepLevel = sleepLevel + 1
loop
set eventFilter = FILTER_CODE
set Damage.index = i.stackRef
call DamageTrigger.setGUIFromStruct(true)
call DamageTrigger.MOD.run()
call DamageTrigger.DAMAGE.run()
if udg_DamageEventAmount != 0.00 then
call DamageTrigger.damageUnit(udg_DamageEventTarget, GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount)
call DamageTrigger.AFTER.run()
endif
set i = i + 1
exitwhen i == exit
endloop
exitwhen i == damageStack
endloop
loop
set i = i - 1
set i.stackRef.recursiveTrig.trigFrozen = false
set i.stackRef.recursiveTrig.levelsDeep = 0
exitwhen i == 0
endloop
set damageStack = 0
endif
set dreaming = false
set sleepLevel = 0
call GroupClear(proclusGlobal)
call GroupClear(fischerMorrow)
set kicking = false
//call BJDebugMsg("Cleared up the groups")
endif
endmethod
private static method wakeUp takes nothing returns nothing
set alarmSet = false
set dreaming = false
set DamageTrigger.enabled = true
call finish()
call onAOEEnd()
set Damage.count = 0
set Damage.index = 0
set udg_DamageEventTarget = null
set udg_DamageEventSource = null
endmethod
private static method createLifeTrigger takes unit u, limitop op, real amount returns nothing
if not blocked then
set lifeTrigger = CreateTrigger()
call TriggerAddCondition(lifeTrigger, Filter(function DamageTrigger.finish))
call TriggerRegisterUnitStateEvent(lifeTrigger, u, UNIT_STATE_LIFE, op, amount)
endif
set protectUnit = u
endmethod
private method mitigate takes real newAmount, boolean recursive returns nothing
local real prevLife
local real life
local unit u = targetUnit
local real prevAmount = prevAmt
set life = GetWidgetLife(u)
if not isSpell then
if newAmount != prevAmount then
set life = life + prevAmount - newAmount
if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
set protectLife = life - prevAmount
call UnitAddAbility(u, udg_DamageBlockingAbility)
endif
call SetWidgetLife(u, RMaxBJ(life, 0.42))
endif
call createLifeTrigger(u, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
else
set protectLife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
set prevLife = life
if life + prevAmount*0.75 > protectLife then
set life = RMaxBJ(protectLife - prevAmount/2.00, 1.00)
call SetWidgetLife(u, life)
set life = (life + protectLife)/2.00
else
set life = life + prevAmount*0.50
endif
set protectLife = prevLife - (prevAmount - (prevAmount - newAmount))
call createLifeTrigger(u, GREATER_THAN, life)
endif
set u = null
endmethod
private method getSpellAmount takes real amt returns real
local integer i = 6
local real mult = 1.00
set isSpell = amt < 0.00
if isSpell then
set amt = -amt
if IsUnitType(target, UNIT_TYPE_ETHEREAL) and not IsUnitType(target, UNIT_TYPE_HERO) then
set mult = mult*udg_DAMAGE_FACTOR_ETHEREAL //1.67
endif
if GetUnitAbilityLevel(target, 'Aegr') > 0 then
set mult = mult*udg_DAMAGE_FACTOR_ELUNES //0.80
endif
if udg_DmgEvBracers != 0 and IsUnitType(target, UNIT_TYPE_HERO) then
//Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
loop
set i = i - 1
if GetItemTypeId(UnitItemInSlot(target, i)) == udg_DmgEvBracers then
set mult = mult*udg_DAMAGE_FACTOR_BRACERS //0.67
exitwhen true
endif
exitwhen i == 0
endloop
endif
return amt*mult
endif
return amt
endmethod
private method addRecursive takes nothing returns boolean
if this.damage != 0.00 then
set this.recursiveTrig = DamageTrigger.eventIndex
if not this.isCode then
set this.isCode = true
endif
set inception = inception or DamageTrigger.eventIndex.inceptionTrig
if kicking and IsUnitInGroup(this.sourceUnit, proclusGlobal) and IsUnitInGroup(this.targetUnit, fischerMorrow) then
if inception and not DamageTrigger.eventIndex.trigFrozen then
set DamageTrigger.eventIndex.inceptionTrig = true
if DamageTrigger.eventIndex.levelsDeep < sleepLevel then
set DamageTrigger.eventIndex.levelsDeep = DamageTrigger.eventIndex.levelsDeep + 1
if DamageTrigger.eventIndex.levelsDeep >= LIMBO then
set DamageTrigger.eventIndex.trigFrozen = true
endif
endif
else
set DamageTrigger.eventIndex.trigFrozen = true
endif
endif
set damageStack.stackRef = this
set damageStack = damageStack + 1
//call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(DamageTrigger.eventIndex.levelsDeep) + " sleepLevel: " + I2S(sleepLevel))
return true
endif
set inception = false
return false
endmethod
private static method onDamageResponse takes nothing returns boolean
local Damage d = Damage.count + 1
set Damage.count = d
set d.sourceUnit = GetEventDamageSource()
set d.targetUnit = GetTriggerUnit()
set d.damage = d.getSpellAmount(GetEventDamage())
set d.prevAmt = d.damage
set d.userType = udg_NextDamageType
set d.isCode = udg_NextDamageType != 0 or udg_NextDamageOverride or dreaming
set d.override = udg_NextDamageOverride
set udg_NextDamageOverride = false
set udg_NextDamageType = 0
call finish() //in case the unit state event failed and the 0.00 second timer hasn't yet expired
if dreaming then
if d.addRecursive() then
set blocked = true
call d.mitigate(0.00, true)
else
set Damage.count = d - 1
endif
return false
endif
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
if alarmSet then
if d.sourceUnit != aoeSource then
call onAOEEnd()
set aoeSource = d.sourceUnit
elseif d.targetUnit == udg_EnhancedDamageTarget then
set udg_DamageEventLevel= udg_DamageEventLevel + 1
elseif not IsUnitInGroup(d.targetUnit, udg_DamageEventAOEGroup) then
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
else
call TimerStart(alarm, 0.00, false, function Damage.wakeUp)
set alarmSet = true
set aoeSource = d.sourceUnit
set udg_EnhancedDamageTarget= d.targetUnit
endif
set Damage.index = d
call DamageTrigger.setGUIFromStruct(true)
call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
call GroupAddUnit(proclusGlobal, udg_DamageEventSource)
call GroupAddUnit(fischerMorrow, udg_DamageEventTarget)
if udg_DamageEventAmount == 0.00 then
call DamageTrigger.ZERO.run()
set canKick = true
call finish()
else
if d.isCode then
set eventFilter = FILTER_CODE
elseif udg_IsDamageSpell then
set eventFilter = FILTER_SPELL
else
set eventFilter = FILTER_ATTACK
endif
call DamageTrigger.MOD.run()
call DamageTrigger.DAMAGE.run()
//The damage amount is finalized.
call d.mitigate(udg_DamageEventAmount, false)
set canKick = true
endif
return false
endmethod
static method createDamageTrigger takes nothing returns nothing //private
set udg_DamageEventTrigger = CreateTrigger()
call TriggerAddCondition(udg_DamageEventTrigger, Filter(function thistype.onDamageResponse))
endmethod
static method setup takes nothing returns boolean //private
local integer i = udg_UDex
local unit u
if udg_UnitIndexEvent == 1.00 then
set u = udg_UDexUnits[i]
if CheckAddUnitToEngine(u) 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 createDamageTrigger()
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
endmethod
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_05()
endstruct
public function RegisterFromHook takes trigger whichTrig, string var, limitop op, real value returns nothing
call DamageTrigger.registerVerbose(whichTrig, var, value, true, GetHandleId(op))
endfunction
hook TriggerRegisterVariableEvent RegisterFromHook
function TriggerRegisterDamageEngineEx takes trigger whichTrig, string eventName, real value, integer f returns DamageTrigger
return DamageTrigger.registerVerbose(whichTrig, DamageTrigger.getVerboseStr(eventName), value, false, f)
endfunction
function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns DamageTrigger
return DamageTrigger.registerTrigger(whichTrig, eventName, value)
endfunction
function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns DamageTrigger
return TriggerRegisterDamageEngineEx(DamageTrigger[c], eventName, value, f)
endfunction
//Similar to TriggerRegisterDamageEvent, although takes code instead of trigger as the first argument.
function RegisterDamageEngine takes code c, string eventName, real value returns DamageTrigger
return RegisterDamageEngineEx(c, eventName, value, FILTER_OTHER)
endfunction
endlibrary
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 to 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 Damage.setup))
set t = null
//Run the configuration actions to set all configurables:
call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
//Create trigger for storing all EVENT_UNIT_DAMAGED events.
call Damage.createDamageTrigger()
//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 FacelessSetup initializer Init requires CustomRaceSystem
private function WorkerHideToggle takes nothing returns nothing
call ShowUnit(GetEnumUnit(),IsUnitHidden(GetEnumUnit()))
endfunction
private function TwistGoldMineForPlayer takes unit goldMine, player whichPlayer returns unit
local real mineX
local real mineY
local integer mineGold
local unit newMine
if GetUnitTypeId(goldMine) != 'ngol' then
return null
endif
set mineX = GetUnitX(goldMine)
set mineY = GetUnitY(goldMine)
set mineGold = GetResourceAmount(goldMine)
call RemoveUnit(goldMine)
set newMine = CreateUnit(whichPlayer, 'obb0', mineX, mineY, bj_UNIT_FACING)
call SetResourceAmount(newMine, mineGold)
return newMine
endfunction
private function FacelessConfig takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
call ForGroup(workers,function WorkerHideToggle)
call TwistGoldMineForPlayer(goldmine,play)
call ForGroup(workers,function WorkerHideToggle)
call DestroyGroup(workers)
// 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("Heralds of Oblivion",RACE_UNDEAD,0.9)
call c.setTownHall('ob00') // Mastermind
call c.addWorkerType('OBH0',c.NEAR_MINE,3) // Harbingers
// call c.addHeroType('OBH0') // Harbingers
// call c.addHeroType('OBH1') // C'Thrax
call c.setCallback(CustomRaceCall.FacelessConfig)
call c.setAIScript("faceless.ai")
endfunction
endlibrary
scope HarvestLumberSys
globals
private constant integer SPELL_ID = 'A003'
private constant string ON_TARGET_FX = "Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl"
private constant integer LUMBER_AMOUNT = 5
private constant real AOE = 275.
private constant real CHECK_AOE = 700.
private constant real AOE_NEW = 375.
private constant real INTERVAL = 2.
private constant real DURATION = 6.
private player HarvesterPlayer
private unit HarvesterUnit
private boolean TreesCount
private boolean TreePicked
endglobals
private function IsTreeFilter takes destructable tree returns boolean
return IsDestTree(tree) and IsAlive(tree)
endfunction
struct HarvestLumberSys
private unit harvesterU
private rect newRect
private real remainingDuration
private timer loopTimer
private static method CheckTrees takes nothing returns boolean
local destructable tree = GetFilterDestructable()
local real treeX = GetWidgetX(tree)
local real treeY = GetWidgetY(tree)
if IsTreeFilter(tree) and RectContainsCoords(bj_mapInitialPlayableArea, treeX, treeY) then
set TreesCount = true
endif
set tree = null
return false
endmethod
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
call RemoveRect(.newRect)
set .loopTimer = null
set .harvesterU = null
set .newRect = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real harvesterX = GetUnitX(.harvesterU)
local real harvesterY = GetUnitY(.harvesterU)
local rect tempRect = Rect(harvesterX - AOE+50, harvesterY - AOE+50, harvesterX + AOE+50, harvesterY + AOE+50)
if .remainingDuration > 0 and IsAlive(.harvesterU) then
set .remainingDuration = .remainingDuration - INTERVAL
if RectContainsCoords(.newRect, GetUnitX(.harvesterU), GetUnitY(.harvesterU)) and /*
*/ GetUnitCurrentOrder(.harvesterU) != ORDER_unstoneform then
set .remainingDuration = 0
call IssueImmediateOrderById(.harvesterU, ORDER_stoneform)
call .destroy()
endif
elseif .remainingDuration <= 0 then
set TreesCount = false
call EnumDestructablesInRect(tempRect, Filter(function thistype.CheckTrees), null)
if TreesCount then
call IssueImmediateOrderById(.harvesterU, ORDER_stoneform)
endif
call .destroy()
elseif not IsAlive(.harvesterU) then
call .destroy()
endif
call RemoveRect(tempRect)
set tempRect = null
endmethod
private static method PickTree takes nothing returns boolean
local thistype this
local destructable tree = GetFilterDestructable()
local real treeX = GetWidgetX(tree)
local real treeY = GetWidgetY(tree)
if not TreePicked and IsTreeFilter(tree) and RectContainsCoords(bj_mapInitialPlayableArea, treeX, treeY) then
set TreePicked = true
set this = allocate()
set .harvesterU = HarvesterUnit
set .newRect = Rect(treeX - AOE_NEW, treeY - AOE_NEW, treeX + AOE_NEW, treeY + AOE_NEW)
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call IssueImmediateOrderById(HarvesterUnit, ORDER_unstoneform)
call IssuePointOrderById(HarvesterUnit, ORDER_move, treeX, treeY)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endif
set tree = null
return false
endmethod
private static method HarvestFunction takes nothing returns boolean
local destructable tree = GetFilterDestructable()
local real treeLife = GetWidgetLife(tree)
local real treeX = GetWidgetX(tree)
local real treeY = GetWidgetY(tree)
local location treeLoc = Location(treeX, treeY)
local integer lumber = GetPlayerState(HarvesterPlayer, PLAYER_STATE_RESOURCE_LUMBER)
local FloatingText text
if IsTreeFilter(tree) then
set text = FloatingText.create()
if treeLife >= LUMBER_AMOUNT then
call SetPlayerState(HarvesterPlayer, PLAYER_STATE_RESOURCE_LUMBER, lumber + LUMBER_AMOUNT)
call text.msgSetup("+" + I2S(LUMBER_AMOUNT), 95, 240, 95, 0)
else
call SetPlayerState(HarvesterPlayer, PLAYER_STATE_RESOURCE_LUMBER, lumber + R2I(treeLife))
call text.msgSetup("+" + I2S(R2I(treeLife)), 95, 240, 95, 0)
endif
call text.overLoc(treeX, treeY)
call text.showOnlyToPlayer(HarvesterPlayer)
call text.runText()
call CreateEffectOnLoc(ON_TARGET_FX, treeLoc)
call SetDestructableLife(tree, treeLife - LUMBER_AMOUNT)
endif
call RemoveLocation(treeLoc)
set tree = null
set treeLoc = null
return false
endmethod
private static method onCast takes nothing returns nothing
local unit harvester = GetSpellAbilityUnit()
local real harvesterX = GetUnitX(harvester)
local real harvesterY = GetUnitY(harvester)
local rect tempRect = Rect(harvesterX - AOE, harvesterY - AOE, harvesterX + AOE, harvesterY + AOE)
local rect checkRect = Rect(harvesterX - CHECK_AOE, harvesterY - CHECK_AOE, harvesterX + CHECK_AOE, harvesterY + CHECK_AOE)
local real treeX
local real treeY
set HarvesterPlayer = GetOwningPlayer(harvester)
call EnumDestructablesInRect(tempRect, Filter(function thistype.HarvestFunction), null)
set HarvesterPlayer = null
set HarvesterUnit = harvester
set TreesCount = false
call EnumDestructablesInRect(tempRect, Filter(function thistype.CheckTrees), null)
if not TreesCount then
set TreePicked = false
call EnumDestructablesInRect(checkRect, Filter(function thistype.PickTree), null)
endif
set HarvesterUnit = null
call RemoveRect(tempRect)
call RemoveRect(checkRect)
set harvester = null
set tempRect = null
set checkRect = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope HarvestGoldSys
globals
private constant integer SPELL_ID = 'A004'
private constant integer GOLD_AMOUNT = 5
private constant integer GOLD_AMOUNT_UPG = 7
endglobals
struct HarvestGoldSys
private static method onCast takes nothing returns nothing
local unit harvester = GetSpellAbilityUnit()
local real harvesterX = GetUnitX(harvester)
local real harvesterY = GetUnitY(harvester)
local player harvesterOwner = GetOwningPlayer(harvester)
local integer gold = GetPlayerState(harvesterOwner, PLAYER_STATE_RESOURCE_GOLD)
local integer remainingGold = GetResourceAmount(harvester)
local integer harvestedGold
local FloatingText text
set text = FloatingText.create()
if IsResearched(harvesterOwner, UPG_YOGGS) and remainingGold > GOLD_AMOUNT_UPG then
set harvestedGold = GOLD_AMOUNT_UPG
elseif remainingGold > GOLD_AMOUNT then
set harvestedGold = GOLD_AMOUNT
else
set harvestedGold = remainingGold
endif
call SetResourceAmount(harvester, remainingGold - harvestedGold)
call SetPlayerState(harvesterOwner, PLAYER_STATE_RESOURCE_GOLD, gold + harvestedGold)
call text.msgSetup("+" + I2S(harvestedGold), 255, 220, 0, 0)
if remainingGold - harvestedGold <= 0 then
call KillUnit(harvester)
endif
call text.overUnit(harvester)
call text.showOnlyToPlayer(harvesterOwner)
call text.runText()
set harvester = null
set harvesterOwner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
library TrainingSystem requires SpellUtils, MapSetup
globals
private constant integer BAR_ID = 'bar0'
private constant real INTERVAL = .2
private player Play
endglobals
private function ForceFilter takes nothing returns boolean
return GetFilterPlayer() == Play
endfunction
struct CustomTraining
private string name
private player owner
private unit trainingBuilding
private location rallyPoint
private integer unitType
private integer unitQty
private integer goldCost
private integer lumberCost
private real trainingTime
private unit dummyUnit
private integer dummyType
private sound unitSound
private unit progressBar
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
call RemoveLocation(.rallyPoint)
set .loopTimer = null
set .owner = null
set .trainingBuilding = null
set .dummyUnit = null
set .unitSound = null
set .rallyPoint = null
set .progressBar = null
endmethod
static method create takes string name, player p returns CustomTraining
local CustomTraining c = CustomTraining.allocate()
set c.name = name
set c.owner = p
return c
endmethod
method setTrainingBuilding takes unit building returns nothing
local integer buildingId
set .trainingBuilding = building
set buildingId = GetUnitId(.trainingBuilding)
set IsTraining[buildingId] = false
endmethod
method setUnitType takes integer unitId, integer quantity returns nothing
set .unitType = unitId
set .unitQty = quantity
endmethod
method setTrainingStats takes integer gold, integer lumber, real time returns nothing
set .goldCost = gold
set .lumberCost = lumber
set .trainingTime = time
endmethod
method setEffects takes integer dummyId, sound s returns nothing
set .dummyType = dummyId
set .unitSound = s
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real buildingX = GetUnitX(.trainingBuilding)
local real buildingY = GetUnitY(.trainingBuilding)
local integer buildingId = GetUnitId(.trainingBuilding)
local integer gold = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER)
local force f = CreateForce()
local integer count = 0
local unit u
if .remainingDuration > 0 and IsAlive(.trainingBuilding) and IsTraining[buildingId] then
set .remainingDuration = .remainingDuration - INTERVAL
elseif .remainingDuration <= 0 then
set IsTraining[buildingId] = false
set .rallyPoint = GetUnitRallyPoint(.trainingBuilding)
if .rallyPoint == null then
set .rallyPoint = GetUnitLoc(.trainingBuilding)
endif
loop
set count = count + 1
exitwhen count > .unitQty
set u = CreateUnitAtLoc(.owner, .unitType, GetUnitLoc(.trainingBuilding), bj_UNIT_FACING)
call IssuePointOrderById(u, ORDER_move, GetLocationX(.rallyPoint), GetLocationY(.rallyPoint))
set u = null
endloop
set Play = .owner
call ForceEnumPlayers(f, Condition(function ForceFilter))
if IsPlayerInForce(GetLocalPlayer(), f) then
call PingMinimapEx(buildingX, buildingY, 2., 0, 255, 0, false)
endif
set Play = null
call StartSound(.unitSound)
call DestroyForce(f)
call RemoveUnit(.dummyUnit)
call RemoveUnit(.progressBar)
call .destroy()
elseif not IsAlive(.trainingBuilding) or not IsTraining[buildingId] then
set IsTraining[buildingId] = false
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD, gold + .goldCost)
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER, lumber + .lumberCost)
call DestroyForce(f)
call RemoveUnit(.dummyUnit)
call RemoveUnit(.progressBar)
call .destroy()
endif
set f = null
endmethod
method startTraining takes nothing returns nothing
local integer buildingId = GetUnitId(.trainingBuilding)
local integer gold = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER)
local real barSpeed
set .remainingDuration = .trainingTime
set .loopTimer = NewTimerEx(this)
if IsAlive(.trainingBuilding) then
set IsTraining[buildingId] = true
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD, gold - .goldCost)
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER, lumber - .lumberCost)
set .dummyUnit = CreateUnitAtLoc(.owner, .dummyType, GetUnitLoc(.trainingBuilding), bj_UNIT_FACING)
call SetUnitVertexColor(.dummyUnit, 255, 255, 255, 90)
set .progressBar = CreateUnitAtLoc(.owner, BAR_ID, GetUnitLoc(.trainingBuilding), bj_UNIT_FACING)
set barSpeed = 1 / .trainingTime
call SetUnitTimeScale(.progressBar, barSpeed)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
else
call .destroy()
endif
endmethod
endstruct
endlibrary
scope IsRiftCompleted
globals
private constant integer RIFT_ID = 'obb1'
endglobals
struct IsRiftCompleted
private static method onStart takes nothing returns nothing
local unit building = GetConstructedStructure()
local integer buildingId = GetUnitId(building)
if GetUnitTypeId(building) == RIFT_ID then
set IsBuilt[buildingId] = false
endif
set building = null
endmethod
private static method onFinish takes nothing returns nothing
local unit building = GetConstructedStructure()
local integer buildingId = GetUnitId(building)
if GetUnitTypeId(building) == RIFT_ID then
set IsBuilt[buildingId] = true
set IsTraining[buildingId] = false
endif
set building = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_START, function thistype.onStart)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function thistype.onFinish)
endmethod
endstruct
endscope
scope CancelOrder
globals
private constant integer SPELL_ID = 'A020'
private constant integer RIFT_ID = 'obb1'
endglobals
struct CancelOrder
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player owner = GetOwningPlayer(caster)
local integer buildingId = GetUnitId(caster)
if GetUnitTypeId(caster) == RIFT_ID and IsBuilt[buildingId] and IsTraining[buildingId] then
set IsTraining[buildingId] = false
elseif GetUnitTypeId(caster) == RIFT_ID and IsBuilt[buildingId] then
call BlizzardMessage("This Rift has no issued orders currently.", "|cffffcc00", 31, owner)
endif
set caster = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainVilespawn
globals
private constant integer SPELL_ID = 'A007'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob02'
private constant integer UNIT_QTY = 2
private constant integer GOLD_COST = 150
private constant integer LUMBER_COST = 0
private constant integer FOOD_COST = 2
private constant real TRAINING_TIME = 20.
private constant integer DUMMY_ID = 'obd0'
endglobals
struct TrainVilespawn
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Vilespawn", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_VilespawnReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainTainted
globals
private constant integer SPELL_ID = 'A008'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob03'
private constant integer UNIT_QTY = 2
private constant integer GOLD_COST = 200
private constant integer LUMBER_COST = 30
private constant integer FOOD_COST = 2
private constant real TRAINING_TIME = 24.
private constant integer DUMMY_ID = 'obd1'
endglobals
struct TrainTainted
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Tainted", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_TaintedReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainGloomfiend
globals
private constant integer SPELL_ID = 'A009'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob04'
private constant integer UNIT_QTY = 2
private constant integer GOLD_COST = 160
private constant integer LUMBER_COST = 40
private constant integer FOOD_COST = 2
private constant real TRAINING_TIME = 26.
private constant integer DUMMY_ID = 'obd2'
endglobals
struct TrainGloomfiend
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Gloomfiend", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_GloomfiendReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainWhisperer
globals
private constant integer SPELL_ID = 'A00A'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob05'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 135
private constant integer LUMBER_COST = 25
private constant integer FOOD_COST = 2
private constant real TRAINING_TIME = 26.
private constant integer DUMMY_ID = 'obd3'
endglobals
struct TrainWhisperer
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Whisperer", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_WhispererReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainManipulator
globals
private constant integer SPELL_ID = 'A00B'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob06'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 150
private constant integer LUMBER_COST = 30
private constant integer FOOD_COST = 2
private constant real TRAINING_TIME = 27.
private constant integer DUMMY_ID = 'obd4'
endglobals
struct TrainManipulator
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Manipulator", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_ManipulatorReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainPunisher
globals
private constant integer SPELL_ID = 'A00C'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob07'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 150
private constant integer LUMBER_COST = 50
private constant integer FOOD_COST = 3
private constant real TRAINING_TIME = 27.
private constant integer DUMMY_ID = 'obd5'
endglobals
struct TrainPunisher
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Punisher", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_PunisherReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainVoidbringer
globals
private constant integer SPELL_ID = 'A00D'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob08'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 195
private constant integer LUMBER_COST = 60
private constant integer FOOD_COST = 4
private constant real TRAINING_TIME = 41.
private constant integer DUMMY_ID = 'obd6'
endglobals
struct TrainVoidbringer
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Voidbringer", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_VoidbringerReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainMisshapen
globals
private constant integer SPELL_ID = 'A00E'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob09'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 250
private constant integer LUMBER_COST = 65
private constant integer FOOD_COST = 4
private constant real TRAINING_TIME = 44.
private constant integer DUMMY_ID = 'obd7'
endglobals
struct TrainMisshapen
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Misshapen", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_MisshapenReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainNightmare
globals
private constant integer SPELL_ID = 'A00F'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob10'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 310
private constant integer LUMBER_COST = 65
private constant integer FOOD_COST = 5
private constant real TRAINING_TIME = 56.
private constant integer DUMMY_ID = 'obd8'
endglobals
struct TrainNightmare
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Nightmare", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_NightmareReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainCorruptor
globals
private constant integer SPELL_ID = 'A02M'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob14'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 160
private constant integer LUMBER_COST = 40
private constant integer FOOD_COST = 2
private constant real TRAINING_TIME = 26.
private constant integer DUMMY_ID = 'obd9'
endglobals
struct TrainCorruptor
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Corruptor", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_GloomfiendReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainRavager
globals
private constant integer SPELL_ID = 'A02O'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob15'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 175
private constant integer LUMBER_COST = 40
private constant integer FOOD_COST = 3
private constant real TRAINING_TIME = 26.
private constant integer DUMMY_ID = 'obda'
endglobals
struct TrainRavager
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Ravager", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_VoidbringerReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainSpawnNzoth
globals
private constant integer SPELL_ID = 'A02Q'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob16'
private constant integer UNIT_QTY = 3
private constant integer GOLD_COST = 180
private constant integer LUMBER_COST = 0
private constant integer FOOD_COST = 3
private constant real TRAINING_TIME = 18.
private constant integer DUMMY_ID = 'obdb'
endglobals
struct TrainSpawnNzoth
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Spawn of N'Zoth", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_VilespawnReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainCultist
globals
private constant integer SPELL_ID = 'A02R'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob17'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 190
private constant integer LUMBER_COST = 35
private constant integer FOOD_COST = 2
private constant real TRAINING_TIME = 25.
private constant integer DUMMY_ID = 'obdc'
endglobals
struct TrainCultist
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Cultist", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_TaintedReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope TrainJuggernaut
globals
private constant integer SPELL_ID = 'A02S'
private constant integer RIFT_ID = 'obb1'
private constant integer UNIT_ID = 'ob18'
private constant integer UNIT_QTY = 1
private constant integer GOLD_COST = 290
private constant integer LUMBER_COST = 85
private constant integer FOOD_COST = 5
private constant real TRAINING_TIME = 44.
private constant integer DUMMY_ID = 'obdd'
endglobals
struct TrainJuggernaut
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer foodUsed = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_USED)
local integer foodCap = GetPlayerState(owner, PLAYER_STATE_RESOURCE_FOOD_CAP)
local integer gold = GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(owner, PLAYER_STATE_RESOURCE_LUMBER)
local CustomTraining c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if foodUsed + FOOD_COST > 100 then
call BlizzardMessage("Unable to create unit due to maximum food limit.", "|cffffcc00", 31, owner)
elseif gold < GOLD_COST then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, owner)
elseif lumber < LUMBER_COST then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, owner)
elseif foodCap < foodUsed + FOOD_COST then
call BlizzardMessage("Spawn more Maws or Creepers to continue the production of units.", "|cffffcc00", 31, owner)
else
set c = CustomTraining.create("Juggernaut", owner)
call c.setTrainingBuilding(targetB)
call c.setUnitType(UNIT_ID, UNIT_QTY)
call c.setTrainingStats(GOLD_COST, LUMBER_COST, TRAINING_TIME)
call c.setEffects(DUMMY_ID, gg_snd_MisshapenReady)
call c.startTraining()
endif
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
library UpgradeSystem requires SpellUtils, MapSetup
globals
private constant integer BAR_ID = 'bar0'
private constant real INTERVAL = .2
private constant integer MAX = 10
private constant integer BOOK1_ID = 'A01P'
private constant integer BOOK2_ID = 'A035'
private constant integer TIER_UPG = 'R002'
private constant integer DMG_UPG = 'R003'
private constant integer ARM_UPG = 'R006'
private constant integer CASTER_UPG = 'R00C'
private constant integer BACKP_UPG = 'R000'
private constant integer UNIT_UPG1 = 'R008'
private constant integer UNIT_UPG2 = 'R00B'
private constant integer UNIT_UPG3 = 'R00E'
private constant integer UNIT_UPG4 = 'R00F'
private constant integer UNIT_UPG5 = 'R00G'
private player Play
endglobals
private function ForceFilter takes nothing returns boolean
return GetFilterPlayer() == Play
endfunction
struct CustomUpgrades
private string name
private player owner
private integer spellId
private boolean enableSpell
private unit upgradeBuilding
private integer buildingId
private integer totalUpgrades = 0
private integer array upgradeId[MAX]
private integer array upgradeLvl[MAX]
private integer goldCost
private integer lumberCost
private real upgradeTime
private unit dummyUnit
private integer dummyType
private unit progressBar
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .owner = null
set .upgradeBuilding = null
set .dummyUnit = null
set .progressBar = null
endmethod
static method create takes string name, player p returns CustomUpgrades
local CustomUpgrades c = CustomUpgrades.allocate()
set c.name = name
set c.owner = p
return c
endmethod
method setSpellData takes integer id, boolean enable returns nothing
set .spellId = id
set .enableSpell = enable
endmethod
method setUpgradeBuilding takes unit building returns nothing
set .upgradeBuilding = building
set .buildingId = GetUnitId(.upgradeBuilding)
set IsTraining[.buildingId] = false
endmethod
method setUpgradeType takes integer upgradeId, integer level returns nothing
set .upgradeId[.totalUpgrades] = upgradeId
set .upgradeLvl[.totalUpgrades] = level
set .totalUpgrades = .totalUpgrades + 1
endmethod
method setUpgradeStats takes integer gold, integer lumber, real time returns nothing
set .goldCost = gold
set .lumberCost = lumber
set .upgradeTime = time
endmethod
method setEffects takes integer dummyId returns nothing
set .dummyType = dummyId
endmethod
private method disableBook takes nothing returns nothing
if GetPlayerTechCount(.owner, TIER_UPG, true) == 2 and GetPlayerTechCount(.owner, DMG_UPG, true) == 3 and /*
*/ GetPlayerTechCount(.owner, ARM_UPG, true) == 3 and GetPlayerTechCount(.owner, CASTER_UPG, true) == 2 and /*
*/ IsResearched(.owner, UNIT_UPG1) and IsResearched(.owner, UNIT_UPG2) and IsResearched(.owner, UNIT_UPG3) and /*
*/ IsResearched(.owner, UNIT_UPG4) and IsResearched(.owner, UNIT_UPG5) then
call SetPlayerAbilityAvailable(.owner, BOOK1_ID, false)
call SetPlayerAbilityAvailable(.owner, BOOK2_ID, false)
endif
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real buildingX = GetUnitX(.upgradeBuilding)
local real buildingY = GetUnitY(.upgradeBuilding)
local integer gold = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER)
local force f = CreateForce()
local integer count = 0
local real barSpeed
local unit u
if .remainingDuration > 0 and IsAlive(.upgradeBuilding) and IsTraining[.buildingId] then
set .remainingDuration = .remainingDuration - INTERVAL
set barSpeed = 1 / (.remainingDuration * 2.)
call SetUnitTimeScale(.progressBar, barSpeed)
elseif .remainingDuration <= 0 then
set IsTraining[.buildingId] = false
loop
exitwhen count > .totalUpgrades
call SetPlayerTechResearched(.owner, .upgradeId[count], .upgradeLvl[count])
if .upgradeId[count] == TIER_UPG then
call SetPlayerTechMaxAllowed(.owner, MAW_ID, 2 + .upgradeLvl[count])
endif
set count = count + 1
endloop
if .enableSpell then
call SetPlayerAbilityAvailable(.owner, .spellId, true)
endif
set Play = .owner
call ForceEnumPlayers(f, Condition(function ForceFilter))
if IsPlayerInForce(GetLocalPlayer(), f) then
call PingMinimapEx(buildingX, buildingY, 2., 0, 255, 0, false)
endif
call .disableBook()
call BlizzardMessage("Completed: "+name, "|cffffcc00", 45, .owner)
call DestroyForce(f)
call RemoveUnit(.dummyUnit)
call RemoveUnit(.progressBar)
call .destroy()
elseif not IsAlive(.upgradeBuilding) or not IsTraining[.buildingId] then
set IsTraining[.buildingId] = false
call SetPlayerAbilityAvailable(.owner, .spellId, true)
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD, gold + .goldCost)
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER, lumber + .lumberCost)
call DestroyForce(f)
call RemoveUnit(.dummyUnit)
call RemoveUnit(.progressBar)
call .destroy()
endif
set f = null
endmethod
method startUpgrade takes nothing returns nothing
local integer gold = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD)
local integer lumber = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER)
local real barSpeed
set .remainingDuration = .upgradeTime
set .loopTimer = NewTimerEx(this)
if gold < .goldCost then
call BlizzardMessage("Not enough gold.", "|cffffcc00", 31, .owner)
elseif lumber < .lumberCost then
call BlizzardMessage("Not enough lumber.", "|cffffcc00", 31, .owner)
elseif IsAlive(.upgradeBuilding) then
set IsTraining[.buildingId] = true
call SetPlayerAbilityAvailable(.owner, .spellId, false)
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD, gold - .goldCost)
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER, lumber - .lumberCost)
set .dummyUnit = CreateUnitAtLoc(.owner, .dummyType, GetUnitLoc(.upgradeBuilding), bj_UNIT_FACING)
set .progressBar = CreateUnitAtLoc(.owner, BAR_ID, GetUnitLoc(.upgradeBuilding), bj_UNIT_FACING)
set barSpeed = 1 / (.upgradeTime * 2.)
call SetUnitTimeScale(.progressBar, barSpeed)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
else
call .destroy()
endif
endmethod
endstruct
endlibrary
scope TierUpg
globals
private constant integer SPELL_ID = 'A01Q'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R001'
private constant integer UPG2_ID = 'R002'
private constant integer UPG3_ID = 'R004'
private constant integer GOLD_COST = 325
private constant integer LUMBER_COST = 200
private constant real UPGRADE_TIME = 140.
private constant integer DUMMY_ID = 'obu0'
endglobals
struct TierUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if lvl == 1 then
set c = CustomUpgrades.create("Twilight Ascent", owner)
call c.setSpellData(SPELL_ID, true)
elseif lvl == 2 then
set c = CustomUpgrades.create("Twilight Dominance", owner)
call c.setSpellData(SPELL_ID, false)
endif
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
call c.setUpgradeType(UPG2_ID, lvl)
call c.setUpgradeType(UPG3_ID, lvl)
call c.setUpgradeStats(GOLD_COST, LUMBER_COST, UPGRADE_TIME)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope DmgUpg
globals
private constant integer SPELL_ID = 'A01R'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R003'
private constant integer UPG2_ID = 'R005'
private constant integer GOLD_COST = 200
private constant integer GOLD_INC = 100
private constant integer LUMBER_COST = 100
private constant integer LUMBER_INC = 150
private constant real UPGRADE_TIME = 80.
private constant real TIME_INC = 30.
private constant integer DUMMY_ID = 'obu1'
endglobals
struct DmgUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if lvl == 1 then
set c = CustomUpgrades.create("Corrupted Strength", owner)
call c.setSpellData(SPELL_ID, true)
elseif lvl == 2 then
set c = CustomUpgrades.create("Improved Corrupted Strength", owner)
call c.setSpellData(SPELL_ID, true)
elseif lvl == 3 then
set c = CustomUpgrades.create("Advanced Corrupted Strength", owner)
call c.setSpellData(SPELL_ID, false)
endif
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
call c.setUpgradeType(UPG2_ID, lvl)
call c.setUpgradeStats(GOLD_COST + (lvl - 1) * GOLD_INC, LUMBER_COST + (lvl - 1) * LUMBER_INC, UPGRADE_TIME + (lvl - 1) * TIME_INC)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope ArmorUpg
globals
private constant integer SPELL_ID = 'A01S'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R006'
private constant integer UPG2_ID = 'R007'
private constant integer GOLD_COST = 175
private constant integer GOLD_INC = 75
private constant integer LUMBER_COST = 100
private constant integer LUMBER_INC = 175
private constant real UPGRADE_TIME = 80.
private constant real TIME_INC = 30.
private constant integer DUMMY_ID = 'obu2'
endglobals
struct ArmorUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if lvl == 1 then
set c = CustomUpgrades.create("Corrupted Resilience", owner)
call c.setSpellData(SPELL_ID, true)
elseif lvl == 2 then
set c = CustomUpgrades.create("Improved Corrupted Resilience", owner)
call c.setSpellData(SPELL_ID, true)
elseif lvl == 3 then
set c = CustomUpgrades.create("Advanced Corrupted Resilience", owner)
call c.setSpellData(SPELL_ID, false)
endif
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
call c.setUpgradeType(UPG2_ID, lvl)
call c.setUpgradeStats(GOLD_COST + (lvl - 1) * GOLD_INC, LUMBER_COST + (lvl - 1) * LUMBER_INC, UPGRADE_TIME + (lvl - 1) * TIME_INC)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope BackpackUpg
globals
private constant integer SPELL_ID = 'A01T'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R000'
private constant integer GOLD_COST = 50
private constant integer LUMBER_COST = 25
private constant real UPGRADE_TIME = 20.
private constant integer DUMMY_ID = 'obu3'
endglobals
struct BackpackUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
set c = CustomUpgrades.create("Backpack", owner)
call c.setSpellData(SPELL_ID, false)
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
call c.setUpgradeStats(GOLD_COST, LUMBER_COST, UPGRADE_TIME)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope BasicUnitsUpg
globals
private constant integer SPELL_ID = 'A01U'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R008'
private constant integer UPG2_ID = 'R009'
private constant integer UPG3_ID = 'R00A'
private constant integer UPG4_ID = 'R00O'
private constant integer UPG5_ID = 'R00P'
private constant integer GOLD_COST = 225
private constant integer LUMBER_COST = 275
private constant real UPGRADE_TIME = 41.
private constant integer DUMMY_ID = 'obu4'
endglobals
struct BasicUnitsUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
set c = CustomUpgrades.create("Rise of Darkness", owner)
call c.setSpellData(SPELL_ID, false)
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
if IsResearched(owner, UPG_NZOTH) then
call c.setUpgradeType(UPG4_ID, lvl)
call c.setUpgradeType(UPG5_ID, lvl)
else
call c.setUpgradeType(UPG2_ID, lvl)
call c.setUpgradeType(UPG3_ID, lvl)
endif
call c.setUpgradeStats(GOLD_COST, LUMBER_COST, UPGRADE_TIME)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope GloomfiendUpg
globals
private constant integer SPELL_ID = 'A01V'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R00B'
private constant integer UPG2_ID = 'R00L'
private constant integer GOLD_COST = 125
private constant integer LUMBER_COST = 150
private constant real UPGRADE_TIME = 41.
private constant integer DUMMY_ID = 'obu5'
endglobals
struct GloomfiendUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
set c = CustomUpgrades.create("Shadow Infusion", owner)
call c.setSpellData(SPELL_ID, false)
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
if IsResearched(owner, UPG_CTHUN) then
call c.setUpgradeType(UPG2_ID, lvl)
endif
call c.setUpgradeStats(GOLD_COST, LUMBER_COST, UPGRADE_TIME)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope CastersUpg
globals
private constant integer SPELL_ID = 'A01W'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R00C'
private constant integer UPG2_ID = 'R00D'
private constant integer GOLD_COST = 225
private constant integer GOLD_INC = 0
private constant integer LUMBER_COST = 100
private constant integer LUMBER_INC = 175
private constant real UPGRADE_TIME = 41.
private constant real TIME_INC = 15.
private constant integer DUMMY_ID = 'obu6'
endglobals
struct CastersUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
if lvl == 1 then
set c = CustomUpgrades.create("Voidcasters Adept Training", owner)
call c.setSpellData(SPELL_ID, true)
elseif lvl == 2 then
set c = CustomUpgrades.create("Voidcasters Master Training", owner)
call c.setSpellData(SPELL_ID, false)
endif
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
call c.setUpgradeType(UPG2_ID, lvl)
call c.setUpgradeStats(GOLD_COST + (lvl - 1) * GOLD_INC, LUMBER_COST + (lvl - 1) * LUMBER_INC, UPGRADE_TIME + (lvl - 1) * TIME_INC)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope VoidbringerUpg
globals
private constant integer SPELL_ID = 'A01X'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R00E'
private constant integer UPG2_ID = 'R00N'
private constant integer GOLD_COST = 150
private constant integer LUMBER_COST = 125
private constant real UPGRADE_TIME = 41.
private constant integer DUMMY_ID = 'obu7'
endglobals
struct VoidbringerUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
set c = CustomUpgrades.create("Desolation", owner)
call c.setSpellData(SPELL_ID, false)
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
if IsResearched(owner, UPG_CTHUN) then
call c.setUpgradeType(UPG2_ID, lvl)
endif
call c.setUpgradeStats(GOLD_COST, LUMBER_COST, UPGRADE_TIME)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope MisshapenUpg
globals
private constant integer SPELL_ID = 'A01Y'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R00F'
private constant integer UPG2_ID = 'R00S'
private constant integer GOLD_COST = 150
private constant integer LUMBER_COST = 225
private constant real UPGRADE_TIME = 45.
private constant integer DUMMY_ID = 'obu8'
endglobals
struct MisshapenUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
set c = CustomUpgrades.create("Maddening Wrath", owner)
call c.setSpellData(SPELL_ID, false)
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
if IsResearched(owner, UPG_YOGGS) then
call c.setUpgradeType(UPG2_ID, lvl)
endif
call c.setUpgradeStats(GOLD_COST, LUMBER_COST, UPGRADE_TIME)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope NightmareUpg
globals
private constant integer SPELL_ID = 'A01Z'
private constant integer RIFT_ID = 'obb1'
private constant integer UPG1_ID = 'R00G'
private constant integer GOLD_COST = 175
private constant integer LUMBER_COST = 250
private constant real UPGRADE_TIME = 56.
private constant integer DUMMY_ID = 'obu9'
endglobals
struct NightmareUpg
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local unit targetB = GetSpellTargetUnit()
local integer buildingId = GetUnitId(targetB)
local player owner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local CustomUpgrades c
if GetUnitTypeId(targetB) == RIFT_ID and IsBuilt[buildingId] and not IsTraining[buildingId] then
set c = CustomUpgrades.create("Haunting Presence", owner)
call c.setSpellData(SPELL_ID, false)
call c.setUpgradeBuilding(targetB)
call c.setUpgradeType(UPG1_ID, lvl)
call c.setUpgradeStats(GOLD_COST, LUMBER_COST, UPGRADE_TIME)
call c.setEffects(DUMMY_ID)
call c.startUpgrade()
else
call BlizzardMessage("Must target an available Rift.", "|cffffcc00", 31, owner)
endif
set caster = null
set targetB = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope OldGods
globals
private constant integer BOOK_ID = 'A02F'
private constant integer CTHUN = 'A02G'
private constant integer YOGGS = 'A02H'
private constant integer NZOTH = 'A02I'
private constant integer UPG_GOD = 'R00K'
constant integer UPG_CTHUN = 'R00H'
constant integer UPG_YOGGS = 'R00I'
constant integer UPG_NZOTH = 'R00J'
private constant integer UPG_MAW_RANGE = 'R00M'
private constant integer UPG_CREEPERS = 'R00Q'
private constant integer UPG_MAW_SPEED = 'R00R'
private constant integer UPG_BUILDINGS = 'R00T'
private constant integer UPG_GOLD_MINE = 'R00U'
private constant integer HARBINGER_ID = 'OBH0'
private constant integer CTHRAX_ID = 'OBH1'
private constant integer DIAMOND_ID = 'I004'
endglobals
private function HarbingerFilter takes unit hero, player owner returns boolean
return IsAlive(hero) and GetUnitTypeId(hero) == HARBINGER_ID and GetOwningPlayer(hero) == owner
endfunction
struct OldGods
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player owner = GetOwningPlayer(caster)
local integer godId = GetSpellAbilityId()
local group heroes = CreateGroup()
local boolean wasPicked = false
local unit picked
local real pickedX
local real pickedY
local integer pickedXp
local item diamond
local unit cthrax
if godId == CTHUN then
call SetPlayerAbilityAvailable(owner, YOGGS, false)
call SetPlayerAbilityAvailable(owner, NZOTH, false)
call SetPlayerAbilityAvailable(owner, BOOK_ID, false)
call SetPlayerTechResearched(owner, UPG_GOD, 1)
call SetPlayerTechResearched(owner, UPG_CTHUN, 1)
call SetPlayerTechResearched(owner, UPG_MAW_RANGE, 1)
elseif godId == YOGGS then
call SetPlayerAbilityAvailable(owner, CTHUN, false)
call SetPlayerAbilityAvailable(owner, NZOTH, false)
call SetPlayerAbilityAvailable(owner, BOOK_ID, false)
call SetPlayerTechResearched(owner, UPG_GOD, 1)
call SetPlayerTechResearched(owner, UPG_YOGGS, 1)
call SetPlayerTechResearched(owner, UPG_BUILDINGS, 1)
call SetPlayerTechResearched(owner, UPG_GOLD_MINE, 1)
elseif godId == NZOTH then
call SetPlayerAbilityAvailable(owner, CTHUN, false)
call SetPlayerAbilityAvailable(owner, YOGGS, false)
call SetPlayerAbilityAvailable(owner, BOOK_ID, false)
call SetPlayerTechResearched(owner, UPG_GOD, 1)
call SetPlayerTechResearched(owner, UPG_NZOTH, 1)
call SetPlayerTechResearched(owner, UPG_CREEPERS, 1)
call SetPlayerTechResearched(owner, UPG_MAW_SPEED, 1)
endif
call GroupEnumUnitsInRect(heroes, bj_mapInitialPlayableArea, null)
loop
set picked = FirstOfGroup(heroes)
exitwhen picked == null or wasPicked
if HarbingerFilter(picked, owner) then
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set pickedXp = GetHeroXP(picked)
if godId == CTHUN or godId == NZOTH then
set diamond = CreateItem(DIAMOND_ID, pickedX, pickedY)
call UnitAddItem(picked, diamond)
set wasPicked = true
else
call RemoveUnit(picked)
set diamond = CreateItem(DIAMOND_ID, pickedX, pickedY)
set cthrax = CreateUnitAtLoc(owner, CTHRAX_ID, Location(pickedX, pickedY), 270.)
call SetHeroXP(cthrax, pickedXp, false)
call UnitAddItem(cthrax, diamond)
set cthrax = null
set wasPicked = true
endif
endif
call GroupRemoveUnit(heroes, picked)
set picked = null
endloop
call DestroyGroup(heroes)
set caster = null
set owner = null
set heroes = null
set diamond = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(CTHUN, function thistype.onCast)
call RegisterSpellEffectEvent(YOGGS, function thistype.onCast)
call RegisterSpellEffectEvent(NZOTH, function thistype.onCast)
endmethod
endstruct
endscope
scope onDamage
globals
private constant integer SEETHING_EVIL_ABILITY = 'A00G'
private constant integer SEETHING_EVIL_BUFF = 'B000'
private constant integer SEETHING_EVIL_RESEARCH = 'R008'
private constant real SEETHING_EVIL_CHANCE = 50.
private constant real SEETHING_EVIL_DMG = 5.
private constant integer CAUSTIC_VENOM_ABILITY = 'A00H'
private constant real CAUSTIC_VENOM_DMG = .05
private constant integer VOID_BARRIER_BUFF = 'B001'
private constant real VOID_BARRIER_BLOCK = .7
private constant integer OLD_WHISPERS_BUFF = 'B003'
private constant real OLD_WHISPERS_DMG = 1.2
private constant integer WHISPERER_ID = 'ob05'
private constant integer MANIPULATOR_ID = 'ob06'
private constant integer PUNISHER_ID = 'ob07'
private constant integer ASSIMILATE_ABILITY = 'B001'
private constant integer ASSIMILATE_RESEARCH = 'R00S'
private constant real ASSIMILATE_BLOCK = .8
private constant real ASSIMILATE_BLOCK_UPG = .6
private constant integer MAW_ID = 'obb3'
private constant integer DARKSPEAKER_ABILITY = 'A02E'
private constant real DARKSPEAKER_DMG = 1.2
endglobals
private function SeethingEvilFilter takes unit attacker, unit target, player attackerOwner returns boolean
return HasAbility(attacker, SEETHING_EVIL_ABILITY) and HasBuff(target, SEETHING_EVIL_BUFF) and /*
*/ HasChance(SEETHING_EVIL_CHANCE) and IsResearched(attackerOwner, SEETHING_EVIL_RESEARCH) and /*
*/ IsUnitEnemy(target, attackerOwner)
endfunction
private function CausticVenomFilter takes unit attacker, unit target, player attackerOwner returns boolean
return HasAbility(attacker, CAUSTIC_VENOM_ABILITY) and IsUnitEnemy(target, attackerOwner) and /*
*/ IsFlying(target)
endfunction
private function VoidBarrierFilter takes unit target, boolean isSpell returns boolean
return HasBuff(target, VOID_BARRIER_BUFF) and not isSpell
endfunction
private function OldWhispersFilter takes unit attacker, unit target, player attackerOwner returns boolean
return HasBuff(target, OLD_WHISPERS_BUFF) and IsUnitEnemy(target, attackerOwner) and /*
*/ (GetUnitTypeId(attacker) == WHISPERER_ID or GetUnitTypeId(attacker) == MANIPULATOR_ID or /*
*/ GetUnitTypeId(attacker) == PUNISHER_ID)
endfunction
private function AssimilateFilter takes unit attacker, unit target returns boolean
return GetUnitStatePercent(target, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE) < 50 and /*
*/ HasAbility(target, ASSIMILATE_ABILITY) and not IsMelee(attacker)
endfunction
private function MawAttackFilter takes unit attacker returns boolean
return GetUnitTypeId(attacker) == MAW_ID and IsAlive(attacker)
endfunction
private function DarkspeakerFilter takes unit attacker returns boolean
return GetUnitStatePercent(attacker, UNIT_STATE_MANA, UNIT_STATE_MAX_MANA) >= 40 and /*
*/ HasAbility(attacker, DARKSPEAKER_ABILITY)
endfunction
struct onDamage
private static method Actions takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local player targetOwner = GetOwningPlayer(target)
local real targetCurrentHP = GetUnitState(target, UNIT_STATE_LIFE)
local real targetMissingHP = GetUnitMissingLife(target)
local integer targetId = GetUnitId(target)
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local unit dummy
if SeethingEvilFilter(attacker, target, attackerOwner) and dmgAmount >= 1 then
set dmgAmount = dmgAmount + SEETHING_EVIL_DMG
endif
if CausticVenomFilter(attacker, target, attackerOwner) and dmgAmount >= 1 then
set dmgAmount = dmgAmount + targetMissingHP * CAUSTIC_VENOM_DMG
endif
if VoidBarrierFilter(target, isSpell) and dmgAmount >= 1 then
set dmgAmount = dmgAmount * VOID_BARRIER_BLOCK
endif
if OldWhispersFilter(attacker, target, attackerOwner) and dmgAmount >= 1 then
set dmgAmount = dmgAmount * OLD_WHISPERS_DMG
endif
if AssimilateFilter(attacker, target) and dmgAmount >= 1 then
if IsResearched(targetOwner, ASSIMILATE_RESEARCH) then
set dmgAmount = dmgAmount * ASSIMILATE_BLOCK_UPG
else
set dmgAmount = dmgAmount * ASSIMILATE_BLOCK
endif
endif
if DarkspeakerFilter(attacker) and dmgAmount >= 1 then
set dmgAmount = dmgAmount * DARKSPEAKER_DMG
endif
if MawAttackFilter(attacker) and dmgAmount >= 1 then
set dmgAmount = 0
endif
if dmgAmount > targetCurrentHP then
if HasAbility(target, BOOK_TWILIGHT_EYE) then
call UnitRemoveAbility(target, BOOK_TWILIGHT_EYE)
endif
if IsCharmed[targetId] then
call SetUnitOwner(target, CharmedOldOwner[targetId], true)
endif
endif
if dmgAmount >= 0 then
set udg_DamageEventAmount = dmgAmount
else
set udg_DamageEventAmount = 0
endif
set attacker = null
set attackerOwner = null
set target = null
set targetOwner = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageModifierEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.Actions)
set t = null
endmethod
endstruct
endscope
scope VolatileBurst
globals
private constant integer ABILITY_ID = 'A00K'
private constant integer ABILITY_RESEARCH = 'R00B'
private constant string ON_UNIT_FX = "Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl"
private constant string ON_TARGET_FX = "Abilities\\Spells\\Other\\AcidBomb\\BottleMissile.mdl"
private constant real DMG = 60.
private constant real AOE = 250.
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_NORMAL
endglobals
private function VolatileBurstFilter takes unit dying, player dyingOwner returns boolean
return HasAbility(dying, ABILITY_ID) and IsResearched(dyingOwner, ABILITY_RESEARCH)
endfunction
private function UnitFilter takes unit target, player dyingOwner returns boolean
return IsAlive(target) and IsFlying(target) and IsUnitEnemy(target, dyingOwner)
endfunction
struct VolatileBurst
private static method onDeath takes nothing returns nothing
local unit dying = GetDyingUnit()
local player dyingOwner = GetOwningPlayer(dying)
local real dyingX = GetUnitX(dying)
local real dyingY = GetUnitY(dying)
local group enemies = CreateGroup()
local unit picked
if VolatileBurstFilter(dying, dyingOwner) then
call CreateEffectOnUnit(ON_UNIT_FX, dying, "origin")
call GroupEnumUnitsWithCollision(enemies, dyingX, dyingY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, dyingOwner) then
call CreateEffectOnUnit(ON_TARGET_FX, picked, "origin")
call UnitDamageTarget(dying, picked, DMG, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
endif
call DestroyGroup(enemies)
set dying = null
set dyingOwner = null
set enemies = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
endmethod
endstruct
endscope
scope UmbralMending
globals
private constant integer SPELL_ID = 'A00L'
private constant real HEAL = 10.
private constant real AOE = 220.
endglobals
private function UnitFilter takes unit target, player casterOwner returns boolean
return IsAlive(target) and not IsBuilding(target) and not IsMechanical(target) and /*
*/ IsUnitAlly(target, casterOwner)
endfunction
struct UmbralMending
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 real targetMaxHP = GetUnitState(target, UNIT_STATE_MAX_LIFE)
local real targetCurrentHP = GetUnitState(target, UNIT_STATE_LIFE)
local group allies = CreateGroup()
local unit picked
local real pickedCurrentHP
call GroupEnumUnitsWithCollision(allies, targetX, targetY, AOE)
loop
set picked = FirstOfGroup(allies)
exitwhen picked == null
if UnitFilter(picked, casterOwner) and picked != target then
set pickedCurrentHP = GetUnitState(picked, UNIT_STATE_LIFE)
call SetUnitState(picked, UNIT_STATE_LIFE, pickedCurrentHP + HEAL)
endif
call GroupRemoveUnit(allies, picked)
set picked = null
endloop
if targetCurrentHP < targetMaxHP / 2 then
call SetUnitState(target, UNIT_STATE_LIFE, targetCurrentHP + HEAL)
endif
call DestroyGroup(allies)
set caster = null
set casterOwner = null
set target = null
set allies = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope VoidBarrier
globals
private constant integer BUFF_ID = 'B001'
private constant real BLOCKED_DMG = .7
private constant real RETURNED_DMG = .15
private constant real CHECK = .02
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
endglobals
private function VoidBarrierFilter takes unit attacker, unit target, player targetOwner returns boolean
return HasBuff(target, BUFF_ID) and IsUnitEnemy(attacker, targetOwner)
endfunction
struct VoidBarrier
private unit attackerU
private unit targetU
private real dmg
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .attackerU = null
set .targetU = null
endmethod
private static method onCheck takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call UnitDamageTarget(.targetU, .attackerU, .dmg, true, false, ATT_TYPE, DMG_TYPE, null)
call .destroy()
endmethod
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local real attackerX = GetUnitX(attacker)
local real attackerY = GetUnitY(attacker)
local unit target = udg_DamageEventTarget
local player targetOwner = GetOwningPlayer(target)
local real dmgAmount = udg_DamageEventAmount / BLOCKED_DMG * RETURNED_DMG
local boolean isSpell = udg_IsDamageSpell
local thistype this
if VoidBarrierFilter(attacker, target, targetOwner) and not isSpell then
set this = allocate()
set .attackerU = attacker
set .targetU = target
set .dmg = dmgAmount
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, CHECK, false, function thistype.onCheck)
endif
set attacker = null
set target = null
set targetOwner = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope TwilightEye
globals
private constant integer SPELL_ID = 'A00O'
private constant integer BOOK_ID = BOOK_TWILIGHT_EYE
private constant string SPELL_ORDER = "sentinel"
private constant integer MANIPULATOR_ID = 'ob06'
private constant real DURATION = 120.
private constant real INTERVAL = .5
private constant real CHECK = .02
endglobals
private function OrderFilter takes unit caster, unit target, integer order returns boolean
return GetUnitTypeId(caster) == MANIPULATOR_ID and order == OrderId(SPELL_ORDER) and /*
*/ HasAbility(target, BOOK_ID)
endfunction
struct TwilightEye
private unit targetU
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .targetU = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if .remainingDuration > 0 and IsAlive(.targetU) then
set .remainingDuration = .remainingDuration - INTERVAL
elseif not IsAlive(.targetU) then
call .destroy()
elseif .remainingDuration <= 0 then
call UnitRemoveAbility(.targetU, BOOK_ID)
call .destroy()
endif
endmethod
private static method stopOrder takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call IssueImmediateOrderById(.targetU, ORDER_stop)
call .destroy()
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local real targetCurrentMana
set .targetU = GetSpellTargetUnit()
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
if UnitHasMana(.targetU) then
set targetCurrentMana = GetUnitState(.targetU, UNIT_STATE_MANA)
call UnitAddAbility(.targetU, BOOK_ID)
call SetUnitState(.targetU, UNIT_STATE_MANA, targetCurrentMana)
else
call UnitAddAbility(.targetU, BOOK_ID)
endif
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endmethod
private static method onOrder takes nothing returns nothing
local unit caster = GetOrderedUnit()
local unit target = GetOrderTargetUnit()
local player owner = GetOwningPlayer(caster)
local integer order = GetIssuedOrderId()
local thistype this
if OrderFilter(caster, target, order) then
set this = allocate()
set .targetU = caster
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call BlizzardMessage("This unit already has a Twilight Eye.", "|cffffcc00", 31, owner)
call IssueTargetOrderById(caster, ORDER_move, target)
call TimerStart(.loopTimer, CHECK, false, function thistype.stopOrder)
endif
set caster = null
set target = null
set owner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, function thistype.onOrder)
endmethod
endstruct
endscope
scope VeilOfShadows
globals
private constant integer BUFF_ID = 'B004'
private constant real HEAL_REDUCTION = .3
endglobals
struct VeilOfShadows
private static method onHeal takes nothing returns nothing
local unit healTarget = udg_heal_target
local real healTargetHP = GetUnitState(healTarget, UNIT_STATE_LIFE)
local real healAmount = udg_heal_amount * HEAL_REDUCTION
if HasBuff(healTarget, BUFF_ID) then
call SetUnitState(healTarget, UNIT_STATE_LIFE, healTargetHP - healAmount)
endif
set healTarget = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_HealEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onHeal)
set t = null
endmethod
endstruct
endscope
scope DarkPortrait
globals
private constant integer BUFF_ID = 'B005'
private constant integer ABILITY_ID = 'ACm2'
private constant integer MANIPULATOR_ID = 'ob06'
endglobals
private function DarkPortraitFilter takes unit summoner, unit summoned returns boolean
return GetUnitTypeId(summoner) == MANIPULATOR_ID and IsUnitIllusion(summoned) and /*
*/ HasBuff(summoned, BUFF_ID)
endfunction
struct DarkPortrait
private static method onSummon takes nothing returns nothing
local unit summoner = GetSummoningUnit()
local unit summoned = GetSummonedUnit()
if DarkPortraitFilter(summoner, summoned) then
call UnitAddAbility(summoned, ABILITY_ID)
endif
set summoner = null
set summoned = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function thistype.onSummon)
endmethod
endstruct
endscope
scope Disperse
globals
private constant integer SPELL_ID = 'A00T'
private constant integer DUMMY_SPELL_ID = 'A00W'
private constant integer DUMMY_SPELL_ORDER = ORDER_autodispel
private constant real AOE = 300.
endglobals
private function UnitFilter takes unit target returns boolean
return IsAlive(target) and not IsBuilding(target) and not IsMechanical(target)
endfunction
struct Disperse
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 target = GetSpellTargetUnit()
local group nearbyUnits = CreateGroup()
local boolean wasPicked = false
local unit picked
local real pickedX
local real pickedY
local unit dummy
call GroupEnumUnitsWithCollision(nearbyUnits, casterX, casterY, AOE)
loop
set picked = GroupRandomUnit(nearbyUnits)
exitwhen picked == null or wasPicked
if UnitFilter(picked) and picked != target then
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set dummy = DummyCaster(casterX, casterY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER, picked)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
set wasPicked = true
endif
call GroupRemoveUnit(nearbyUnits, picked)
set picked = null
endloop
call DestroyGroup(nearbyUnits)
set caster = null
set casterOwner = null
set target = null
set nearbyUnits = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope MindFlay
globals
private constant integer SPELL_ID = 'A00U'
private constant integer DUMMY_SPELL_ID = 'A00X'
private constant integer DUMMY_SPELL_ORDER = ORDER_howlofterror
endglobals
struct MindFlay
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 = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueImmediateOrderById(dummy, DUMMY_SPELL_ORDER)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set caster = null
set casterOwner = null
set target = null
set dummy = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope PsychicShock
globals
private constant integer SPELL_ID = 'A00V'
private constant string ON_CAST_FX = "war3mapImported\\ArcaneExplosion.mdx"
private constant string ON_TARGET_FX = "Abilities\\Spells\\NightElf\\SpiritOfVengeance\\SpiritOfVengeanceBirthMissile.mdl"
private constant real DMG = 35.
private constant real AOE = 400.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
private function UnitFilter takes unit target, player casterOwner returns boolean
return IsAlive(target) and not IsBuilding(target) and not IsMechanical(target) and /*
*/ not IsMagicImmune(target) and UnitHasMana(target) and IsUnitEnemy(target, casterOwner) and/*
*/ GetUnitState(target, UNIT_STATE_MANA) < GetUnitState(target, UNIT_STATE_MAX_MANA) / 2
endfunction
struct PsychicShock
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 unit picked
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
call CreateEffectOnUnit(ON_TARGET_FX, picked, "chest")
call UnitDamageTarget(caster, picked, DMG, 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 NetherVolley
globals
private constant integer ABILITY_ID = 'A00Z'
private constant string MISSILE_MODEL = "war3mapImported\\Voidbolt Minor.mdx"
private constant real MIN_DMG = 7.
private constant real MAX_DMG = 10.
private constant real SEARCH_AOE = 200.
private constant real IMPACT_AOE = 125.
private constant integer MISSILE_AMOUNT = 2
private constant attacktype ATT_TYPE = ATTACK_TYPE_SIEGE
private constant damagetype DMG_TYPE = DAMAGE_TYPE_NORMAL
endglobals
private function OnHitFilter takes unit target returns boolean
return IsAlive(target) and not IsFlying(target)
endfunction
private function NetherVolleyFilter takes unit attacker, unit target, player attackerOwner returns boolean
return HasAbility(attacker, ABILITY_ID) and IsUnitEnemy(target, attackerOwner)
endfunction
private function OnPickFilter takes unit target, player attackerOwner returns boolean
return IsAlive(target) and not IsFlying(target) and IsUnitEnemy(target, attackerOwner)
endfunction
private struct NetherVolley extends Missiles
method onFinish takes nothing returns boolean
local group enemies = CreateGroup()
local unit picked
call GroupEnumUnitsWithCollision(enemies, .x, .y, IMPACT_AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if OnHitFilter(picked) then
call UnitDamageTarget(source, picked, damage, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
call DestroyGroup(enemies)
set enemies = null
return false
endmethod
private static method onAttack takes nothing returns nothing
local unit attacker = GetAttacker()
local real attackerX = GetUnitX(attacker)
local real attackerY = GetUnitY(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local integer lvl = GetUnitAbilityLevel(attacker, ABILITY_ID)
local unit target = GetTriggerUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local group enemies = CreateGroup()
local integer i = 1
local unit picked
local real pickedX
local real pickedY
local thistype this
if NetherVolleyFilter(attacker, target, attackerOwner) then
call GroupEnumUnitsWithCollision(enemies, targetX, targetY, SEARCH_AOE)
loop
exitwhen i > MISSILE_AMOUNT * lvl
set picked = GroupRandomUnit(enemies)
if OnPickFilter(picked, attackerOwner) then
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set this = thistype.create(attackerX, attackerY, 100., pickedX, pickedY, 100.)
set damage = GetRandomReal(MIN_DMG, MAX_DMG)
set source = attacker
set model = MISSILE_MODEL
set speed = 900.
set collision = 20.
set arc = GetRandomReal(5, 15)
set curve = GetRandomReal(3, 10)
call launch()
set i = i + 1
else
call GroupRemoveUnit(enemies, picked)
endif
set picked = null
endloop
endif
call DestroyGroup(enemies)
set attacker = null
set attackerOwner = null
set target = null
set enemies = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.onAttack)
endmethod
endstruct
endscope
scope Overpower
globals
private constant integer ABILITY_ID = 'A011'
private constant integer ABILITY_RESEARCH = 'R00F'
endglobals
private function ChargeSwap takes unit u, integer n returns nothing
local integer i = 0
loop
exitwhen i > 9
call UnitRemoveAbility(u, OVERPOWER_LVL[i])
set i = i + 1
endloop
call SetUnitAbilityLevel(u, ABILITY_ID, n + 1)
call UnitAddAbility(u, OVERPOWER_LVL[n])
endfunction
private function OverpowerFilter takes unit attacker, player attackerOwner returns boolean
return IsAlive(attacker) and HasAbility(attacker, ABILITY_ID) and /*
*/ IsResearched(attackerOwner, ABILITY_RESEARCH)
endfunction
struct Overpower
private static method onAttack takes nothing returns nothing
local unit attacker = GetAttacker()
local player attackerOwner = GetOwningPlayer(attacker)
local real attackerHP = GetUnitStatePercent(attacker, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE)
local integer n
if attackerHP >= 90 and OverpowerFilter(attacker, attackerOwner) then
set n = 0
elseif attackerHP < 90 and attackerHP >= 80 and OverpowerFilter(attacker, attackerOwner) then
set n = 1
elseif attackerHP < 80 and attackerHP >= 70 and OverpowerFilter(attacker, attackerOwner) then
set n = 2
elseif attackerHP < 70 and attackerHP >= 60 and OverpowerFilter(attacker, attackerOwner) then
set n = 3
elseif attackerHP < 60 and attackerHP >= 50 and OverpowerFilter(attacker, attackerOwner) then
set n = 4
elseif attackerHP < 50 and attackerHP >= 40 and OverpowerFilter(attacker, attackerOwner) then
set n = 5
elseif attackerHP < 40 and attackerHP >= 30 and OverpowerFilter(attacker, attackerOwner) then
set n = 6
elseif attackerHP < 30 and attackerHP >= 20 and OverpowerFilter(attacker, attackerOwner) then
set n = 7
elseif attackerHP < 20 and attackerHP >= 10 and OverpowerFilter(attacker, attackerOwner) then
set n = 8
elseif attackerHP < 10 and OverpowerFilter(attacker, attackerOwner) then
set n = 9
endif
call ChargeSwap(attacker, n)
set attacker = null
set attackerOwner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.onAttack)
endmethod
endstruct
endscope
scope Atrophy
globals
private constant integer ABILITY_ID = 'A01D'
private constant real DURATION = 20.
private constant real INTERVAL = .5
private constant real AOE = 700.
private integer array Charges
endglobals
private function ChargeSwap takes unit u, integer n returns nothing
local integer i = 0
loop
exitwhen i > 10
call UnitRemoveAbility(u, ATROPHY_LVL[i])
set i = i + 1
endloop
call SetUnitAbilityLevel(u, ABILITY_ID, n + 1)
call UnitAddAbility(u, ATROPHY_LVL[n])
endfunction
private function AtrophyFilter takes unit target, player dyingOwner returns boolean
return IsAlive(target) and HasAbility(target, ABILITY_ID) and /*
*/ IsUnitEnemy(target, dyingOwner)
endfunction
struct Atrophy
private unit targetU
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .targetU = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer id = GetUnitId(.targetU)
if .remainingDuration > 0 then
if IsAlive(.targetU) and Charges[id] > 0 then
set .remainingDuration = .remainingDuration - INTERVAL
else
set .remainingDuration = 0
endif
else
if Charges[id] > 0 and IsAlive(.targetU) then
set Charges[id] = Charges[id] - 1
if Charges[id] < 11 then
call ChargeSwap(.targetU, Charges[id])
endif
elseif not IsAlive(.targetU) then
set Charges[id] = 0
endif
call .destroy()
endif
endmethod
private static method onDeath takes nothing returns nothing
local unit dying = GetDyingUnit()
local real dyingX = GetUnitX(dying)
local real dyingY = GetUnitY(dying)
local player dyingOwner = GetOwningPlayer(dying)
local group nightmares = CreateGroup()
local unit picked
local integer pickedId
local thistype this
call GroupEnumUnitsWithCollision(nightmares, dyingX, dyingY, AOE)
loop
set picked = FirstOfGroup(nightmares)
set pickedId = GetUnitId(picked)
exitwhen picked == null
if AtrophyFilter(picked, dyingOwner) then
set Charges[pickedId] = Charges[pickedId] + 1
if Charges[pickedId] < 11 then
call ChargeSwap(picked, Charges[pickedId])
endif
set this = allocate()
set .targetU = picked
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endif
call GroupRemoveUnit(nightmares, picked)
set picked = null
endloop
call DestroyGroup(nightmares)
set dying = null
set dyingOwner = null
set nightmares = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
endmethod
endstruct
endscope
scope MawSystems
globals
private constant integer MAW_ID = 'obb3'
private constant integer TENTACLE_ID = 'obx1'
private constant real AOE = 170.
private unit array FIRST_TENTACLE
private unit array SECOND_TENTACLE
private unit array THIRD_TENTACLE
private unit array FOURTH_TENTACLE
private constant integer DUMMY_SPELL_ID = 'A028'
private constant integer DUMMY_SPELL_ORDER = ORDER_ward
endglobals
struct MawSystems
private static method onFinish takes nothing returns nothing
local unit building = GetConstructedStructure()
local real buildingX = GetUnitX(building)
local real buildingY = GetUnitY(building)
local player buildingOwner = GetOwningPlayer(building)
local integer buildingId = GetUnitId(building)
local integer i = 1
local real x
local real y
local location loc
if GetUnitTypeId(building) == MAW_ID then
set x = buildingX + AOE * Cos((45.) * bj_DEGTORAD)
set y = buildingY + AOE * Sin((45.) * bj_DEGTORAD)
set loc = Location(x, y)
set FIRST_TENTACLE[buildingId] = CreateUnitAtLoc(buildingOwner, TENTACLE_ID, loc, 270.)
call RemoveLocation(loc)
set x = buildingX + AOE * Cos((135) * bj_DEGTORAD)
set y = buildingY + AOE * Sin((135) * bj_DEGTORAD)
set loc = Location(x, y)
set SECOND_TENTACLE[buildingId] = CreateUnitAtLoc(buildingOwner, TENTACLE_ID, loc, 270.)
call RemoveLocation(loc)
set x = buildingX + AOE * Cos((225) * bj_DEGTORAD)
set y = buildingY + AOE * Sin((225) * bj_DEGTORAD)
set loc = Location(x, y)
set THIRD_TENTACLE[buildingId] = CreateUnitAtLoc(buildingOwner, TENTACLE_ID, loc, 270.)
call RemoveLocation(loc)
set x = buildingX + AOE * Cos((315) * bj_DEGTORAD)
set y = buildingY + AOE * Sin((315) * bj_DEGTORAD)
set loc = Location(x, y)
set FOURTH_TENTACLE[buildingId] = CreateUnitAtLoc(buildingOwner, TENTACLE_ID, loc, 270.)
call RemoveLocation(loc)
set loc = null
endif
set building = null
set buildingOwner = null
endmethod
private static method onAttack takes nothing returns nothing
local unit attacker = GetAttacker()
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = GetTriggerUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local location targetLoc = GetUnitLoc(target)
local unit dummy
if GetUnitTypeId(attacker) == MAW_ID then
set dummy = DummyCaster(targetX, targetY, 0., attackerOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssuePointOrderByIdLoc(dummy, DUMMY_SPELL_ORDER, targetLoc)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
endif
call RemoveLocation(targetLoc)
set attacker = null
set attackerOwner = null
set target = null
set targetLoc = null
endmethod
private static method onDeath takes nothing returns nothing
local unit dying = GetDyingUnit()
local integer dyingId = GetUnitId(dying)
if GetUnitTypeId(dying) == MAW_ID then
call KillUnit(FIRST_TENTACLE[dyingId])
call KillUnit(SECOND_TENTACLE[dyingId])
call KillUnit(THIRD_TENTACLE[dyingId])
call KillUnit(FOURTH_TENTACLE[dyingId])
set FIRST_TENTACLE[dyingId] = null
set SECOND_TENTACLE[dyingId] = null
set THIRD_TENTACLE[dyingId] = null
set FOURTH_TENTACLE[dyingId] = null
endif
set dying = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function thistype.onFinish)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.onAttack)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
endmethod
endstruct
endscope
scope Tentacle
globals
private constant integer SPELL_ID = 'A02A'
private constant integer DUMMY_SPELL_ID = 'A029'
private constant integer DUMMY_SPELL_ORDER = ORDER_cyclone
endglobals
struct Tentacle
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 = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set caster = null
set casterOwner = null
set target = null
set dummy = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Manifest
globals
private constant integer ORDER_ID = ORDER_buildtiny
private constant integer HARBINGER_ID = 'OBH0'
private constant integer MASTERM_ID = 'ob00'
private constant integer MASTERM1_ID = 'ob11'
private constant integer MASTERM2_ID = 'ob12'
private constant integer MASTERM3_ID = 'ob13'
private constant real CHECK = .02
endglobals
private function OrderFilter takes unit caster, integer order returns boolean
return GetUnitTypeId(caster) == HARBINGER_ID and order == ORDER_ID
endfunction
private function MastermindFilter takes unit picked, player owner returns boolean
return IsAlive(picked) and GetOwningPlayer(picked) == owner and /*
*/ (GetUnitTypeId(picked) == MASTERM_ID or GetUnitTypeId(picked) == MASTERM1_ID or /*
*/ GetUnitTypeId(picked) == MASTERM2_ID or GetUnitTypeId(picked) == MASTERM3_ID)
endfunction
struct Manifest
private unit targetU
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .targetU = null
endmethod
private static method stopOrder takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call IssueImmediateOrderById(.targetU, ORDER_stop)
call .destroy()
endmethod
private static method onOrder takes nothing returns nothing
local unit caster = GetOrderedUnit()
local player owner = GetOwningPlayer(caster)
local integer order = GetIssuedOrderId()
local group tempGroup = CreateGroup()
local boolean mastermindExists = false
local unit picked
local thistype this
call GroupEnumUnitsInRect(tempGroup, bj_mapInitialPlayableArea, null)
loop
set picked = FirstOfGroup(tempGroup)
exitwhen picked == null or mastermindExists
if MastermindFilter(picked, owner) then
set mastermindExists = true
endif
call GroupRemoveUnit(tempGroup, picked)
set picked = null
endloop
if OrderFilter(caster, order) and mastermindExists then
set this = allocate()
set .targetU = caster
set .loopTimer = NewTimerEx(this)
call BlizzardMessage("You cannot have more than one Mastermind.", "|cffffcc00", 31, owner)
call IssueTargetOrderById(caster, ORDER_move, caster)
call TimerStart(.loopTimer, CHECK, false, function thistype.stopOrder)
endif
call DestroyGroup(tempGroup)
set caster = null
set owner = null
set tempGroup = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
endmethod
endstruct
endscope
scope BrainSpike
globals
private constant integer SPELL_ID = 'A02B'
private constant string MISSILE_MODEL = "war3mapImported\\Void Arrow.mdx"
private constant real DMG = 100.
private constant real AOE = 200.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
private function OnHitFilter takes unit target, player missileOwner returns boolean
return IsAlive(target) and not IsMagicImmune(target) and IsUnitEnemy(target, missileOwner)
endfunction
private struct BrainSpike extends Missiles
method onHit takes unit hit returns boolean
local player missileOwner = GetOwningPlayer(source)
local real hitX = GetUnitX(hit)
local real hitY = GetUnitY(hit)
local group enemies = CreateGroup()
local unit picked
if hit == target then
if IsHero(hit) and not IsResearched(missileOwner, UPG_CTHUN) then
call UnitDamageTarget(source, hit, damage / 2, true, false, ATT_TYPE, DMG_TYPE, null)
else
call UnitDamageTarget(source, hit, damage, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupEnumUnitsWithCollision(enemies, hitX, hitY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if OnHitFilter(picked, missileOwner) and picked != hit then
call UnitDamageTarget(source, picked, damage / 4, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
endif
call DestroyGroup(enemies)
set missileOwner = null
set enemies = null
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 integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local unit targetU = GetSpellTargetUnit()
local real targetX = GetUnitX(targetU)
local real targetY = GetUnitY(targetU)
local thistype this
set this = thistype.create(casterX, casterY, 100., targetX, targetY, 100.)
set damage = DMG * lvl
set source = caster
set target = targetU
set model = MISSILE_MODEL
set speed = 1000.
set collision = 20.
call launch()
set caster = null
set casterOwner = null
set targetU = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope SoulChain
globals
private constant integer SPELL_ID = 'A02C'
private constant integer HARBINGER_ID = 'OBH0'
private constant string CHAIN_MODEL = "SPLK"
private constant string ON_TARGET_FX = "war3mapImported\\Bondage Pink SD.mdx"
private constant real DURATION = 15.
private constant real INTERVAL = .2
private constant real CHECK = .02
private constant real HP_DRAINED_INC = 5.
private constant real SHARED_DMG_BASE = .15
private constant real SHARED_DMG_INC = .1
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
private unit array ChainCaster
private unit array ChainTarget
endglobals
private function OnCheckFilter takes unit caster, unit target returns boolean
return IsAlive(caster) and IsAlive(target) and /*
*/ Distance(GetUnitX(caster), GetUnitY(caster), GetUnitX(target), GetUnitY(target)) < 900.
endfunction
private function SoulChainFilter takes unit target returns boolean
local integer id = GetUnitId(target)
return GetUnitTypeId(target) == HARBINGER_ID and ChainTarget[id] != null
endfunction
struct SoulChain
private unit caster
private unit target
private lightning chain
private effect chainFx
private real dmg
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
call DestroyLightning(.chain)
call DestroyEffect(.chainFx)
set .loopTimer = null
set .caster = null
set .target = null
set .chain = null
set .chainFx = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer casterId = GetUnitId(.caster)
local location casterLoc = GetUnitLoc(.caster)
local player casterOwner = GetOwningPlayer(.caster)
local real casterX = GetUnitX(.caster)
local real casterY = GetUnitY(.caster)
local real casterZ = GetLocationZ(casterLoc)
local real casterHP = GetUnitState(.caster, UNIT_STATE_LIFE)
local real casterMana = GetUnitState(.caster, UNIT_STATE_MANA)
local real lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
local real targetX = GetUnitX(.target)
local real targetY = GetUnitY(.target)
local real targetZ = GetUnitFlyHeight(.target)
if .remainingDuration > 0 and OnCheckFilter(.caster, .target) then
set .remainingDuration = .remainingDuration - INTERVAL
call UnitDamageTarget(.caster, .target, (HP_DRAINED_INC * lvl) / 5, true, false, ATT_TYPE, DMG_TYPE, null)
call SetUnitState(.caster, UNIT_STATE_MANA, casterMana + (HP_DRAINED_INC * lvl) / 5)
if IsResearched(casterOwner, UPG_YOGGS) then
call SetUnitState(.caster, UNIT_STATE_LIFE, casterHP + (HP_DRAINED_INC * lvl) / 10)
endif
call MoveLightningEx(.chain, true, casterX, casterY, casterZ, targetX, targetY, targetZ)
else
set ChainCaster[casterId] = null
set ChainTarget[casterId] = null
call .destroy()
endif
call RemoveLocation(casterLoc)
set casterLoc = null
set casterOwner = null
endmethod
private static method onCast takes nothing returns nothing
local unit casterU = GetSpellAbilityUnit()
local location casterLoc = GetUnitLoc(casterU)
local real casterX = GetUnitX(casterU)
local real casterY = GetUnitY(casterU)
local real casterZ = GetLocationZ(casterLoc)
local player casterOwner = GetOwningPlayer(casterU)
local integer casterId = GetUnitId(casterU)
local unit targetU = GetSpellTargetUnit()
local location targetLoc = GetUnitLoc(targetU)
local real targetX = GetUnitX(targetU)
local real targetY = GetUnitY(targetU)
local real targetZ = GetUnitFlyHeight(targetU)
local thistype this
set ChainCaster[casterId] = casterU
set ChainTarget[casterId] = targetU
set this = allocate()
set .caster = casterU
set .target = targetU
set .chain = AddLightningEx(CHAIN_MODEL, true, casterX, casterY, casterZ, targetX, targetY, targetZ)
set .chainFx = AddSpecialEffectTarget(ON_TARGET_FX, .target, "chest")
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call SetLightningColor(.chain, .5, .0, 1., 1.)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
call RemoveLocation(casterLoc)
set casterU = null
set casterLoc = null
set casterOwner = null
set targetU = null
endmethod
private static method onCheck takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer id = GetUnitId(.target)
call UnitDamageTarget(ChainCaster[id], ChainTarget[id], .dmg, true, false, ATT_TYPE, DMG_TYPE, null)
call .destroy()
endmethod
private static method onDamage takes nothing returns nothing
local unit targetU = udg_DamageEventTarget
local real lvl = GetUnitAbilityLevel(targetU, SPELL_ID)
local real dmgAmount = udg_DamageEventAmount * (SHARED_DMG_BASE + SHARED_DMG_INC * (lvl - 1))
local thistype this
if SoulChainFilter(targetU) then
set this = allocate()
set .target = targetU
set .dmg = dmgAmount
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, CHECK, false, function thistype.onCheck)
endif
set targetU = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope TouchOfInsanity
globals
private constant integer ABILITY_ID = 'A02D'
private constant string ON_TARGET_FX = "war3mapImported\\Dazed_Effect.mdx"
private constant real COOLDOWN_BASE = 20.
private constant real COOLDOWN_DEC = 5.
private constant real DURATION_BASE = 6.
private constant real DURATION_INC = 1.
private constant real DURATION_NZOTH = 3.
private constant real INTERVAL = .5
private constant real CHANCE = 15.
private constant real CHANCE_NZOTH = 18.
boolean array IsCharmed
boolean array CharmedCooldown
player array CharmedOldOwner
player array CharmedNewOwner
endglobals
private function InsanityFilter takes unit attacker, unit target, player attackerOwner, real chance returns boolean
return HasAbility(attacker, ABILITY_ID) and IsAlive(target) and not IsBuilding(target) and /*
*/ not IsMechanical(target) and not IsMagicImmune(target) and not IsHero(target) and /*
*/ not IsWorker(target) and GetUnitLevel(target) <= 5 and IsUnitEnemy(target, attackerOwner) and /*
*/ HasChance(chance)
endfunction
struct TouchOfInsanity
private unit target
private player oldOwner
private player newOwner
private effect insanityFx
private real charmDuration
private real cooldownDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
call DestroyEffect(.insanityFx)
set .loopTimer = null
set .target = null
set .oldOwner = null
set .newOwner = null
set .insanityFx = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer targetId = GetUnitId(.target)
if .cooldownDuration > 0 and IsAlive(.target) and CharmedCooldown[targetId] then
set .cooldownDuration = .cooldownDuration - INTERVAL
if .charmDuration > 0 and IsCharmed[targetId] then
set .charmDuration = .charmDuration - INTERVAL
else
call SetUnitOwner(.target, .oldOwner, true)
set IsCharmed[targetId] = false
set CharmedOldOwner[targetId] = null
set CharmedNewOwner[targetId] = null
endif
elseif .cooldownDuration <= 0 then
set CharmedCooldown[targetId] = false
call .destroy()
else
set IsCharmed[targetId] = false
set CharmedCooldown[targetId] = false
set CharmedOldOwner[targetId] = null
set CharmedNewOwner[targetId] = null
call .destroy()
endif
endmethod
private static method onAttack takes nothing returns nothing
local unit attacker = GetAttacker()
local player attackerOwner = GetOwningPlayer(attacker)
local integer lvl = GetUnitAbilityLevel(attacker, ABILITY_ID)
local unit target = GetTriggerUnit()
local player targetOwner = GetOwningPlayer(target)
local integer targetId = GetUnitId(target)
local thistype this
local real chance
if IsResearched(attackerOwner, UPG_NZOTH) then
set chance = CHANCE_NZOTH
else
set chance = CHANCE
endif
if InsanityFilter(attacker, target, attackerOwner, chance) and not CharmedCooldown[targetId] then
set IsCharmed[targetId] = true
set CharmedCooldown[targetId] = true
set CharmedOldOwner[targetId] = targetOwner
set CharmedNewOwner[targetId] = attackerOwner
set this = allocate()
set .target = target
set .oldOwner = targetOwner
set .newOwner = attackerOwner
set .insanityFx = AddSpecialEffectTarget(ON_TARGET_FX, .target, "overhead")
set .charmDuration = DURATION_BASE + DURATION_INC * (lvl - 1)
set .cooldownDuration = COOLDOWN_BASE - COOLDOWN_DEC * (lvl - 1) + .charmDuration
set .loopTimer = NewTimerEx(this)
if IsResearched(attackerOwner, UPG_NZOTH) then
set .charmDuration = .charmDuration + DURATION_NZOTH
endif
call SetUnitOwner(target, attackerOwner, true)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endif
set attacker = null
set attackerOwner = null
set target = null
set targetOwner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.onAttack)
endmethod
endstruct
endscope
scope DireBlast
globals
private constant integer SPELL_ID = 'A031'
private constant integer FISSURE_BUFF_ID = 'B00A'
private constant string ON_LOC_FX = "war3mapImported\\Nether Blast IV.mdx"
private constant real DMG_BASE = 60.
private constant real DMG_INC = 40.
private constant real DMG_BONUS = 60.
private constant real AOE = 220.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
private function UnitFilter takes unit target, player casterOwner returns boolean
return IsAlive(target) and not IsBuilding(target) and not IsMechanical(target) and /*
*/ not IsMagicImmune(target) and IsUnitEnemy(target, casterOwner)
endfunction
struct DireBlast
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local location targetLoc = GetSpellTargetLoc()
local real locX = GetLocationX(targetLoc)
local real locY = GetLocationY(targetLoc)
local group enemies = CreateGroup()
local unit picked
local real dmgAmount
call CreateEffectOnLoc(ON_LOC_FX, targetLoc)
call GroupEnumUnitsWithCollision(enemies, locX, locY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, casterOwner) then
set dmgAmount = DMG_BASE + DMG_INC * (lvl - 1)
if HasBuff(picked, FISSURE_BUFF_ID) then
set dmgAmount = dmgAmount + DMG_BONUS
endif
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)
set caster = null
set casterOwner = 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 TearReality
globals
private constant integer SPELL_ID = 'A02X'
private constant real DURATION = 12.
endglobals
struct TearReality
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local location targetLoc = GetSpellTargetLoc()
local unit fissure = CreateUnitAtLoc(casterOwner, FISSURE_ID[lvl], targetLoc, 270.)
call UnitApplyTimedLife(fissure, 'BTLF', DURATION)
call RemoveLocation(targetLoc)
set caster = null
set casterOwner = null
set targetLoc = null
set fissure = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope DespairAura
globals
private constant integer FISSURE_BUFF_ID = 'B00A'
private constant integer AURA1_BUFF_ID = 'B00C'
private constant integer AURA2_BUFF_ID = 'B00D'
private constant integer AURA3_BUFF_ID = 'B00E'
private constant real CHANCE = 15.
endglobals
private function DespairFilter takes unit attacker, player targetOwner returns boolean
return HasBuff(attacker, FISSURE_BUFF_ID) and IsUnitEnemy(attacker, targetOwner) and /*
*/ (HasBuff(attacker, AURA1_BUFF_ID) or HasBuff(attacker, AURA2_BUFF_ID) or /*
*/ HasBuff(attacker, AURA3_BUFF_ID)) and HasChance(CHANCE)
endfunction
struct DespairAura
private static method onAttack takes nothing returns nothing
local unit attacker = GetAttacker()
local unit target = GetTriggerUnit()
local player targetOwner = GetOwningPlayer(target)
local real targetHP = GetUnitState(target, UNIT_STATE_LIFE)
local real healAmount
if DespairFilter(attacker, targetOwner) then
if HasBuff(attacker, AURA1_BUFF_ID) then
set healAmount = 10
elseif HasBuff(attacker, AURA2_BUFF_ID) then
set healAmount = 20
elseif HasBuff(attacker, AURA3_BUFF_ID) then
set healAmount = 30
endif
call SetUnitState(target, UNIT_STATE_LIFE, targetHP + healAmount)
endif
set attacker = null
set target = null
set targetOwner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.onAttack)
endmethod
endstruct
endscope
scope RealmOfMadness
globals
private constant integer SPELL_ID = 'A033'
private constant integer BUFF_ID = 'B00F'
private constant integer FISSURE_SPELL_ID = 'A02X'
private constant integer FISSURE_BUFFP_ID = 'B00B'
private constant integer FISSURE_BUFF_ID = 'B00A'
private constant string ON_HEAL_FX = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
private constant real DURATION = 26.
private constant real FISSURE_DURATION = 5.
private constant real INTERVAL = 2.
private constant real HEAL = .06
private constant real AOE = 700.
endglobals
private function OnLoopFilter takes unit caster returns boolean
return IsAlive(caster) and HasBuff(caster, BUFF_ID)
endfunction
private function RealmFilter takes unit dying, unit hero returns boolean
return (HasBuff(dying, FISSURE_BUFFP_ID) or HasBuff(dying, FISSURE_BUFF_ID)) and /*
*/ HasBuff(hero, BUFF_ID)
endfunction
struct RealmOfMadness
private unit caster
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local player casterOwner = GetOwningPlayer(.caster)
local real casterX = GetUnitX(.caster)
local real casterY = GetUnitY(.caster)
local location targetLoc = Location(GetRandomReal(casterX - AOE*0.5, casterX + AOE*0.5), GetRandomReal(casterY - AOE*0.5, casterY + AOE*0.5))
local integer lvl
local unit fissure
if .remainingDuration > 0 and OnLoopFilter(.caster) then
set .remainingDuration = .remainingDuration - INTERVAL
if HasAbility(.caster, FISSURE_SPELL_ID) then
set lvl = GetUnitAbilityLevel(.caster, FISSURE_SPELL_ID)
else
set lvl = 1
endif
set fissure = CreateUnitAtLoc(casterOwner, FISSURE_ID[lvl], targetLoc, 270.)
call UnitApplyTimedLife(fissure, 'BTLF', FISSURE_DURATION)
set fissure = null
else
call .destroy()
endif
call RemoveLocation(targetLoc)
set casterOwner = null
set targetLoc = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
set .caster = GetSpellAbilityUnit()
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endmethod
private static method onDeath takes nothing returns nothing
local unit dying = udg_DamageEventTarget
local real dyingX = GetUnitX(dying)
local real dyingY = GetUnitY(dying)
local real dyingHP = GetUnitState(dying, UNIT_STATE_LIFE)
local group heroes = CreateGroup()
local real dmgAmount = udg_DamageEventAmount
local unit picked
local real pickedHP
local real pickedMaxHP
call GroupEnumUnitsWithCollision(heroes, dyingX, dyingY, AOE)
loop
set picked = FirstOfGroup(heroes)
exitwhen picked == null
if RealmFilter(dying, picked) and dmgAmount > dyingHP then
set pickedHP = GetUnitState(picked, UNIT_STATE_LIFE)
set pickedMaxHP = GetUnitState(picked, UNIT_STATE_MAX_LIFE)
call CreateEffectOnUnit(ON_HEAL_FX, picked, "origin")
call SetUnitState(picked, UNIT_STATE_LIFE, pickedHP + pickedMaxHP * HEAL)
endif
call GroupRemoveUnit(heroes, picked)
set picked = null
endloop
call DestroyGroup(heroes)
set dying = null
set heroes = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDeath)
set t = null
endmethod
endstruct
endscope
library MapSetup initializer Init
globals
constant integer BOOK_TWILIGHT_EYE = 'A00Q'
constant integer BOOK_OVERPOWER = 'A010'
constant integer BOOK_ATROPHY = 'A00I'
constant integer BOOK_ASSIMILATE = 'A02V'
constant integer MAW_ID = 'obb3'
integer array OVERPOWER_LVL
integer array ATROPHY_LVL
integer array FISSURE_ID
boolean array IsBuilt
boolean array IsTraining
endglobals
private function Initialization takes nothing returns nothing
local integer i = 0
loop
exitwhen i == bj_MAX_PLAYERS
//Books
call SetPlayerAbilityAvailable(Player(i), BOOK_TWILIGHT_EYE, false)
call SetPlayerAbilityAvailable(Player(i), BOOK_OVERPOWER, false)
call SetPlayerAbilityAvailable(Player(i), BOOK_ATROPHY, false)
call SetPlayerAbilityAvailable(Player(i), BOOK_ASSIMILATE, false)
call SetPlayerTechMaxAllowed(Player(i), MAW_ID, 2)
set i = i + 1
endloop
set OVERPOWER_LVL[0] = 'A012'
set OVERPOWER_LVL[1] = 'A013'
set OVERPOWER_LVL[2] = 'A014'
set OVERPOWER_LVL[3] = 'A015'
set OVERPOWER_LVL[4] = 'A016'
set OVERPOWER_LVL[5] = 'A017'
set OVERPOWER_LVL[6] = 'A018'
set OVERPOWER_LVL[7] = 'A019'
set OVERPOWER_LVL[8] = 'A01A'
set OVERPOWER_LVL[9] = 'A01B'
set ATROPHY_LVL[0] = 'A01E'
set ATROPHY_LVL[1] = 'A01F'
set ATROPHY_LVL[2] = 'A01G'
set ATROPHY_LVL[3] = 'A01H'
set ATROPHY_LVL[4] = 'A01I'
set ATROPHY_LVL[5] = 'A01J'
set ATROPHY_LVL[6] = 'A01K'
set ATROPHY_LVL[7] = 'A01L'
set ATROPHY_LVL[8] = 'A01M'
set ATROPHY_LVL[9] = 'A01N'
set ATROPHY_LVL[10] = 'A01O'
set FISSURE_ID[1] = 'obx3'
set FISSURE_ID[2] = 'obx4'
set FISSURE_ID[3] = 'obx5'
endfunction
private function Init takes nothing returns nothing
call Initialization()
endfunction
endlibrary