Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
AOEDamageEvent | real | No | |
Behavior_IsPlayerBehavior | boolean | No | |
Behavior_OnAllyAttacked | trigger | No | |
Behavior_OnAllyAttacks | trigger | No | |
Behavior_OnAllyCasts | trigger | No | |
Behavior_OnAllyDeath | trigger | No | |
Behavior_OnAllyDmgDealt | trigger | No | |
Behavior_OnAllyDmgTaken | trigger | No | |
Behavior_OnAllyKills | trigger | No | |
Behavior_OnAllyTargeted | trigger | No | |
Behavior_OnAttack | trigger | No | |
Behavior_OnAttacked | trigger | No | |
Behavior_OnCast | trigger | No | |
Behavior_OnDamageDealt | trigger | No | |
Behavior_OnDamageTaken | trigger | No | |
Behavior_OnEnemyAttacked | trigger | No | |
Behavior_OnEnemyAttacks | trigger | No | |
Behavior_OnEnemyCasts | trigger | No | |
Behavior_OnEnemyDeath | trigger | No | |
Behavior_OnEnemyDmgDealt | trigger | No | |
Behavior_OnEnemyDmgTaken | trigger | No | |
Behavior_OnEnemyKills | trigger | No | |
Behavior_OnEnemyTargeted | trigger | No | |
Behavior_OnKill | trigger | No | |
Behavior_OnPeriod | trigger | No | |
Behavior_OnTargeted | trigger | No | |
Behavior_Unit | unit | No | |
Behavior_UnitType | unitcode | No | |
ClearDamageEvent | trigger | No | |
CustomO_Cooldown | real | No | |
CustomO_IsInCooldown | boolean | No | |
CustomO_Location | location | No | |
CustomO_ManaCost | integer | No | |
CustomO_Order | string | No | |
CustomO_OrderId | integer | No | |
CustomO_Priority | integer | No | |
CustomO_Target | unit | No | |
CustomO_Unit | unit | No | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DamageBlockingAbility | abilcode | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageModifierEvent | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypeReduced | integer | No | |
DmgEvBracers | itemcode | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
EnhancedDamageTarget | unit | No | |
Event_Ally | unit | No | |
Event_Attacker | unit | No | |
Event_Caster | unit | No | |
Event_Enemy | unit | No | |
Event_Killer | unit | No | |
Event_Source | unit | No | |
Event_Target | unit | No | |
Event_Unit | unit | No | |
Event_Victim | unit | No | |
HideDamageFrom | boolean | Yes | |
IsDamageSpell | boolean | No | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
SpellDamageAbility | abilcode | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitDamageRegistered | boolean | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes |
library MultidimensionalArray /* v1.2b
*/uses /*
*/Table /* http://www.hiveworkshop.com/threads/snippet-new-table.188084/
[Resource Link] - http://www.hiveworkshop.com/threads/snippet-multidimensional-array.289785/
*///! novjass
/* This snippet allows you to have array storage. Unlike default arrays, you can use an index beyond 8190
since this snippet uses Tables which uses hashtable. Therefore, saving data with a really large index
such as using GetHandleId(handle) as the index would not be a problem.
But you may ask, why would we want to use this when there is already the Table library which can support
any type that we want to store? First, this has a feature which supports multidimensions that allows you
to have up to 5-dimensional array. Table already allows you to create multidimensional storage if you do
proper nesting but this library removes the need for users to do it on their own and this also helps set
a standard instead of having redundant scripts that work for the same puspose. Secondly, unlike Table,
this implements a type specific storage i.e., you can create an array storage that is only exclusive for
a specific type but of course, this also provides a generic storage like Table but with a nicer API.
Furthermore, this includes some safety precautions such as compile time safety which throws an error if
you're using an incorrect number of dimensions, as well as preventing the use of an Array instance which
isn't allocated in DEBUG_MODE. lastly, this gives users a nice and intuitive syntax which resembles that
of the original vanilla Jass arrays (call KillUnit(u[1][3])) without having a need for an ugly keyword
at the end (ex: call KillUnit(u[1].unit[3])). */
|=========|
| Credits |
|=========|
/* AGD : Author
Bribe : For the Table library, and for the algorithm of making n-dimensional storage by nesting Tables */
|-----|
| API |
|-----|
Creating an Array:
/* Creates a new array for a specific type */
local Unit1D u = Array.create()
local Unit3D u3 = Unit3D.create()
local Unit5D u5 = Timer4D.create() //You could actually use any of the dimensional array creator
local Array4D a4 = Array.create()
Storing inside an Array:
/* Stores data inside an array */
set u[GetHandleId(timer)] = GetTriggerUnit()
set u3[0x2000]['AAAA'] = GetTriggerUnit() //Syntax error: number of indexes does not match with the number of dimensions
set u5[1][2][3][4][5] = GetTriggerUnit()
set a4[1][2][3][4].unit = GetTriggerUnit()
Retrieving from an Array:
/* Retrieves data from an array */
call KillUnit(u[1234567])
call KillUnit(u3['A']['B']['C'])
call KillUnit(u5[1][2][3][4]) //Syntax error: number of indexes does not match with the number of dimensions
call KillUnit(a4[1][2][3][4].unit)
Checking storage vacancy:
/* Checks if there is data stored inside an array index */
return u.has(index) //Similar to Table(u).unit.has(index)
return u3[1][2].has(3)
return u5[1].has(2) //Checks if the fourth dimension has index 2
return a4[1][2][3].hasHandle(4)
Removing an Array index:
/* Destroys the table instance of an index and clears all its child nodes if there are any */
call u.remove(1)
call u3[1].remove(2)
call u5[1][2][3][4][5].remove(6) //Syntax error: cannot use remove() on a node which has no children
call a4[1][2][3].removeHandle(4)
Flushing an Array Index:
/* Flushes all child nodes attached to the specific index */
call u.flush() //Flushes all data inside the array, analogous to flushing a parent hashtable
call u3[1][2][3].flush() //Syntax error: cannot clear a node which has no children, use u3[1][2].remove(3) instead
call u5[1][2].flush() //Flushes all child nodes attached to the index "2" of the second dimension
call a4[1][2].flush()
Destroying an Array:
/* Destroys an array instance, flushing all data inside it */
call u.destroy()
call u3.destroy()
call u5[1].destroy() //If destroy() is called upon a node which is not a root node, it will work like clear() instead
call a4.destroy()
//! endnovjass
static if DEBUG_MODE then
private struct S extends array
static key allocated
endstruct
private function Debug takes string msg returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "|CFFFFCC00[MultidimensionalArray] |R" + msg)
endfunction
endif
private function AllocateIndex takes integer this, integer index returns integer
debug if not Table(S.allocated).boolean[this] then
debug return 0
debug endif
debug set Table(S.allocated).boolean[HashTable(this)[index]] = true
return HashTable(this)[index]
endfunction
/*============= For a uniform allocator syntax =) ==============*/
struct Array extends array
static method create takes nothing returns thistype
static if DEBUG_MODE then
local Table t = Table.create()
set Table(S.allocated).boolean[t] = true
return t
else
return Table.create()
endif
endmethod
endstruct
/*==============================================================*/
/*====================== Struct methods ========================*/
private module Methods
static method create takes nothing returns thistype
return Array.create()
endmethod
static if not thistype.remove.exists then
method remove takes integer index returns nothing
call HashTable(this).remove(index)
endmethod
endif
static if not thistype.has.exists then
method has takes integer index returns boolean
return HashTable(this).has(index)
endmethod
endif
method flush takes nothing returns nothing
call Table(this).flush()
endmethod
method destroy takes nothing returns nothing
call Table(this).destroy()
debug set Table(S.allocated).boolean[this] = false
endmethod
endmodule
/*==============================================================*/
/*================= Generic Type Array Storage =================*/
private struct Type extends array
static key index
method operator agent= takes agent value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFF0000[Operator agent= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).agent[Table(this)[index]] = value
endmethod
//! textmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS takes TYPE
method operator $TYPE$ takes nothing returns $TYPE$
return Table(this).$TYPE$[Table(this)[index]]
endmethod
method operator $TYPE$= takes $TYPE$ value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFF0000[Operator $TYPE$= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).$TYPE$[Table(this)[index]] = value
endmethod
//! endtextmacro
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("integer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("real")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("string")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("boolean")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("player")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("widget")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("destructable")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("item")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("unit")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("ability")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("timer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("trigger")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("triggercondition")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("triggeraction")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("event")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("force")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("group")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("location")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("rect")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("boolexpr")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("sound")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("effect")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("unitpool")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("itempool")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("quest")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("questitem")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("defeatcondition")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("timerdialog")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("leaderboard")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("multiboard")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("multiboarditem")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("trackable")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("dialog")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("button")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("texttag")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("lightning")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("image")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("ubersplat")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("region")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("fogstate")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("fogmodifier")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("hashtable")
endstruct
struct Array1D extends array
//! textmacro GENERIC_DIMENSIONAL_ARRAY_METHODS takes NAME, TYPE
method has$NAME$ takes integer index returns boolean
return Table(this).$TYPE$.has(index)
endmethod
method remove$NAME$ takes integer index returns nothing
call Table(this).$TYPE$.remove(index)
endmethod
//! endtextmacro
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Integer", "integer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Real", "real")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("String", "string")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Boolean", "boolean")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Handle", "handle")
method has takes integer index returns boolean
return .hasInteger(index) /*
*/or .hasReal(index) /*
*/or .hasString(index) /*
*/or .hasBoolean(index) /*
*/or .hasHandle(index)
endmethod
method remove takes integer index returns nothing
call .removeInteger(index)
call .removeReal(index)
call .removeString(index)
call .removeBoolean(index)
call .removeHandle(index)
endmethod
implement Methods
method operator [] takes integer index returns Type
debug if not Table(S.allocated).boolean[this] then
debug return 0
debug endif
set Table(this)[Type.index] = index
return this
endmethod
endstruct
//! textmacro NEW_DIMENSIONAL_ARRAY_STRUCT takes DIM, RETURNED
struct Array$DIM$D extends array
implement Methods
method operator [] takes integer index returns Array$RETURNED$D
return AllocateIndex(this, index)
endmethod
endstruct
//! endtextmacro
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("2", "1")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("3", "2")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("4", "3")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("5", "4")
// If you want to increase the maximum number of available
// dimensions, just run the textmacros above once again like:
// runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("LAST_MAX_DIM + 1", "LAST_MAX_DIM")
/*==============================================================*/
/*================ Type Specific Array Storage =================*/
//! textmacro NEW_DIMENSIONAL_ARRAY takes NAME, TYPE
struct $NAME$1D extends array
method remove takes integer index returns nothing
call Table(this).$TYPE$.remove(index)
endmethod
method has takes integer index returns boolean
return Table(this).$TYPE$.has(index)
endmethod
implement Methods
method operator [] takes integer index returns $TYPE$
return Table(this).$TYPE$[index]
endmethod
method operator []= takes integer index, $TYPE$ value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFFCC00[ArrayType: $NAME$]|R |CFFFF0000[Operator []= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).$TYPE$[index] = value
endmethod
endstruct
struct $NAME$2D extends array
implement Methods
method operator [] takes integer index returns $NAME$1D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$3D extends array
implement Methods
method operator [] takes integer index returns $NAME$2D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$4D extends array
implement Methods
method operator [] takes integer index returns $NAME$3D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$5D extends array
implement Methods
method operator [] takes integer index returns $NAME$4D
return AllocateIndex(this, index)
endmethod
endstruct
//! endtextmacro
// If you want to increase the maximum number of available
// dimensions, just copy the last struct above and increase the
// number of dimension in the struct name and the returned struct
// of the operator [] by 1.
/*==============================================================*/
/*======== Implement textmacros for every storage type =========*/
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Integer", "integer")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Real", "real")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Str", "string")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Boolean", "boolean")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Player", "player")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Widget", "widget")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Item", "item")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Unit", "unit")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Ability", "ability")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Timer", "timer")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Force", "force")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Group", "group")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Location", "location")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Rect", "rect")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Sound", "sound")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Effect", "effect")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Quest", "quest")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Button", "button")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Image", "image")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Region", "region")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Hashtable", "hashtable")
/*==============================================================*/
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 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
//===========================================================================
// 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
//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
/*****************************************************************************
*
* 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
library AIBehaviorController /* v1.51 by Riki
Special credits to Kingz for his Behaviour AI System, which inspired me and from which I
based to create this system.
Find it here: https://www.hiveworkshop.com/threads/behaviour-ai-system-v1-5.248639/
----------------------------
---NECESSARY REQUIREMENTS---
----------------------------
*/ requires /*
*/ MultidimensionalArray, /* by AGD: http://www.hiveworkshop.com/threads/snippet-multidimensional-array.289785/
*/ TimerUtils, /*
Others:
-Unit Indexer by Bribe: https://www.hiveworkshop.com/threads/gui-unit-indexer-1-4-0-0.197329/
-Damage Engine by Bribe: https://www.hiveworkshop.com/threads/damage-engine-5-7-1-2.201016/
-Table by Bribe: http://www.hiveworkshop.com/threads/snippet-new-table.188084/
---------------------------
---OPTIONAL REQUIREMENTS---
---------------------------
*/ optional /*
*/ UnitIndexerGUI, /* by Bribe: https://www.hiveworkshop.com/threads/snippet-gui-unit-indexer-vjass-plugin.268592/
*/ RegisterPlayerUnitEvent /* by Bannar: https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
Others:
-RegisterNativeEvent by Bannar: https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*/
//! novjass
================================== AI Behavior Controller ==================================
/* A system designed to make it easy for users to create AI by giving them the option to */
/* define custom behaviors for specific units or unit types as required. These behaviors */
/* include following a specified order under certain circumstances, using custom spells */
/* and abilities (including those based on Channel), etc. */
/* */
/* Sometimes it is frustrating to have to deal with an AI that does not cast certain */
/* spells because they are based on Channel or some other ability that the AI would not */
/* use under normal circunstances. */
/* */
/* The goal of this system in principle is to provide a tool that allows AI units to use */
/* any spell that the user wishes to create, and it has been extended to include even */
/* more options. */
/* */
/* Please note that this system is not designed to directly create custom AI, but to */
/* support AI creation. */
/*----------------------------------------------------------------------------------------*/
=================
- HOW TO IMPORT -
=================
/*----------------------------------------------------------------------------------------*/
/* For users who want to use the system in vJass, follow steps from 1 to 3. */
/* Those users who need the GUI version must follow all the steps listed. */
/* */
/* 1. Copy all the Necessary Requirements into your map. If you have newer versions of */
/* one of the listed requirements, you can skip it, but remember that you will need all */
/* of them. */
/* */
/* 2. Copy this library into your map. After this you will be ready to use this system. */
/* */
/* 3. I recommend copying the Optional Requirements as well, but you can skip this step */
/* if you want. */
/* */
/* 4. To use the GUI version, you must first follow all the steps above. After that, you */
/* need to copy all the content within the AIBC GUI category to your map. */
/*----------------------------------------------------------------------------------------*/
=======
- API -
=======
struct Controller
|- static method registerBehavior takes unit u, Behavior b, boolean isUserBehavior returns nothing
| // Adds a custom Behavior to a single unit.
| // If isUserBehavior is false, it will fire only for Computer owned units.
| // If isUserBehavior is true, it will fire only for Player owned units.
|
|- static method registerBehaviorType takes integer uTypeId, Behavior b, boolean isUserBehavior returns nothing
| // Adds a custom Behavior to an unit type.
| // If isUserBehavior is false, it will fire only for Computer owned units.
| // If isUserBehavior is true, it will fire only for Player owned units.
|
|- static method removeBehavior takes unit u, Behavior b returns nothing
| // Removes a custom Behavior from a single unit.
|
|- static method removeBehaviorType takes integer uTypeId, Behavior b returns nothing
| // Removes a custom Behavior from an unit type.
struct Behavior
|- method onPeriod takes unit u returns nothing
| // Fires on every interval. Use u to access the unit.
|
|- method onCast takes unit caster, unit target returns nothing
| // Fires when the unit casts an ability. Use caster to access the unit.
|
|- method onTargeted takes unit target, unit caster returns nothing
| // Fires when the unit is targeted with an ability. Use target to access the unit.
|
|- method onAttack takes unit attacker, unit target returns nothing
| // Fires when the unit attacks. Use attacker to access the unit.
|
|- method onAttacked takes unit target, unit attacker returns nothing
| // Fires when the unit is attacked. Use target to access the unit.
|
|- method onDamageDealt takes unit source, unit target returns nothing
| // Fires when the unit deals damage. Use source to access the unit.
|
|- method onDamageTaken takes unit target, unit source returns nothing
| // Fires when the unit receives damage. Use target to access the unit.
|
|- method onKill takes unit killer, unit victim returns nothing
| // Fires when the unit kills another. Use killer to access the unit.
|
|- method onAllyCasts takes unit u, unit ally, unit target returns nothing
|- method onAllyTargeted takes unit u, unit ally, unit caster returns nothing
|- method onAllyAttacks takes unit u, unit ally, unit target returns nothing
|- method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
|- method onAllyDmgDealt takes unit u, unit ally, unit target returns nothing
|- method onAllyDmgTaken takes unit u, unit ally, unit source returns nothing
|- method onAllyKills takes unit u, unit ally, unit victim returns nothing
|- method onAllyDeath takes unit u, unit ally, unit killer returns nothing
| // Events that will fire when a nearby friendly unit performs a certain action.
| // These events will only activate if OTHER_UNITS_EVENTS is set to true.
| // Use u to access the unit with custom behavior.
| // Use ally to access the allied unit that fires the event.
|
|- method onEnemyCasts takes unit u, unit enemy, unit target returns nothing
|- method onEnemyTargeted takes unit u, unit enemy, unit caster returns nothing
|- method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
|- method onEnemyAttacked takes unit u, unit enemy, unit attacker returns nothing
|- method onEnemyDmgDealt takes unit u, unit enemy, unit target returns nothing
|- method onEnemyDmgTaken takes unit u, unit enemy, unit source returns nothing
|- method onEnemyKills takes unit u, unit enemy, unit victim returns nothing
|- method onEnemyDeath takes unit u, unit enemy, unit killer returns nothing
| // Events that will fire when a nearby enemy unit performs a certain action.
| // These events will only activate if OTHER_UNITS_EVENTS is set to true.
| // Use u to access the unit with custom behavior.
| // Use enemy to access the enemy unit that fires the event.
struct CustomOrder
|- method create takes unit u returns thistype
| // Registers the unit so it can use custom orders given by its Behavior.
|
|- method checkOrder takes string orderStr returns boolean
| // Checks if an orders is already registered.
|
|- method checkOrderById takes integer orderId returns boolean
| // Checks if an orders is already registered using integers for order id.
|
|- method isOrderInCooldown takes string orderStr returns boolean
| // Checks if an orders is in cooldown.
|
|- method isOrderInCooldownById takes integer orderId returns boolean
| // Checks if an orders is in cooldown using integers for order id.
|
|- method registerTargetOrder takes string orderStr, widget target, integer priority, real cooldown, integer manaCost returns nothing
|- method registerPointOrder takes string orderStr, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
|- method registerInstantOrder takes string orderStr, integer priority, real cooldown, integer manaCost returns nothing
| // Registers an order using strings.
|
|- method registerTargetOrderById takes integer orderId, widget target, integer priority, real cooldown, integer manaCost returns nothing
|- method registerPointOrderById takes integer orderId, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
|- method registerInstantOrderById takes integer orderId, integer priority, real cooldown, integer manaCost returns nothing
| // Registers an order using integers for order id.
//! endnovjass
native UnitAlive takes unit u returns boolean
/*------CONFIGURATION------*/
globals
private constant real PERIODIC_INTERVAL = .5 // Interval for Periodic Events
private constant real COOLDOWN_INTERVAL = .5 // Interval for Cooldown check
private constant integer MAX_ORDERS = 15 // Max number of orders for a unit per event
private constant integer LOWEST_PRIO = 1 // Minimum priority value for orders
private constant integer HIGHEST_PRIO = 100 // Maximum priority value for orders
private constant boolean OTHER_UNITS_EVENTS = true // Set to false if you don't want to fire onAlly / onEnemy events
private constant real UNIT_SEARCH_RANGE = 700. // Max range used to detect onAlly / onEnemy events
private constant real MIN_DMG = 1. // Minimum value required to fire a Damage event. Recommended value: 1.0
// Used for GroupEnumUnitsWithCollision function
private constant real MAX_COLLISION = 64. // Max collision size for units
private real tempX = 0. // Don't touch
private real tempY = 0. // Don't touch
private real tempRange = 0. // Don't touch
// Arrays, don't touch
Boolean2D OrderInCooldown
Boolean1D OrderExecuted
Integer1D RegOrders
Integer2D UnitBehavior
Integer2D UnitTypeBehavior
Boolean2D RequiredController
Boolean2D RequiredControllerType
Boolean1D HasCustomOrders
Integer1D TotalBehaviors
Integer1D TotalBehaviorsType
// Global behavior group for periodic events
group BehaviorGlobalGroup
endglobals
/*------FUNCTIONS------*/
private function FilterInRange takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), tempX, tempY, tempRange)
endfunction
private function GroupEnumUnitsWithCollision takes group g, real x, real y, real radius returns group
set tempX = x
set tempY = y
set tempRange = radius
call GroupEnumUnitsInRange(g, x, y, radius+MAX_COLLISION, Filter(function FilterInRange))
return g
endfunction
private function BehaviorMapController takes mapcontrol mc returns boolean
if mc == MAP_CONTROL_USER then
return true
else
return false
endif
endfunction
private function CopyNewGroup takes group g returns group
set bj_groupAddGroupDest = CreateGroup()
call ForGroup(g, function GroupAddGroupEnum)
return bj_groupAddGroupDest
endfunction
/*------EVENTS------*/
interface Behavior
// Spell Cast Events
method onCast takes unit caster, unit target returns nothing defaults nothing
method onTargeted takes unit target, unit caster returns nothing defaults nothing
// Attack Events
method onAttack takes unit attacker, unit target returns nothing defaults nothing
method onAttacked takes unit target, unit attacker returns nothing defaults nothing
// Damage Events
method onDamageDealt takes unit source, unit target returns nothing defaults nothing
method onDamageTaken takes unit target, unit source returns nothing defaults nothing
// Kill Event
method onKill takes unit killer, unit victim returns nothing defaults nothing
// Periodic Event
method onPeriod takes unit u returns nothing defaults nothing
// OnAlly Events
method onAllyCasts takes unit u, unit ally, unit target returns nothing defaults nothing
method onAllyTargeted takes unit u, unit ally, unit caster returns nothing defaults nothing
method onAllyAttacks takes unit u, unit ally, unit target returns nothing defaults nothing
method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing defaults nothing
method onAllyDmgDealt takes unit u, unit ally, unit target returns nothing defaults nothing
method onAllyDmgTaken takes unit u, unit ally, unit source returns nothing defaults nothing
method onAllyKills takes unit u, unit ally, unit victim returns nothing defaults nothing
method onAllyDeath takes unit u, unit ally, unit killer returns nothing defaults nothing
// OnEnemy Events
method onEnemyCasts takes unit u, unit enemy, unit target returns nothing defaults nothing
method onEnemyTargeted takes unit u, unit enemy, unit caster returns nothing defaults nothing
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing defaults nothing
method onEnemyAttacked takes unit u, unit enemy, unit attacker returns nothing defaults nothing
method onEnemyDmgDealt takes unit u, unit enemy, unit target returns nothing defaults nothing
method onEnemyDmgTaken takes unit u, unit enemy, unit source returns nothing defaults nothing
method onEnemyKills takes unit u, unit enemy, unit victim returns nothing defaults nothing
method onEnemyDeath takes unit u, unit enemy, unit killer returns nothing defaults nothing
endinterface
/*------MAIN SYSTEM------*/
struct Cooldown
private integer casterId
private integer order
private real duration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
endmethod
private static method check takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit caster
static if LIBRARY_UnitIndexerGUI then
set caster = GetUnitById(.casterId)
else
set caster = udg_UDexUnits[.casterId]
endif
if .duration > 0 and UnitAlive(caster) and OrderInCooldown[.casterId][.order] then
set .duration = .duration - COOLDOWN_INTERVAL
else
set OrderInCooldown[.casterId][.order] = false
call .destroy()
endif
set caster = null
endmethod
static method start takes integer unitId, integer orderId, real cooldown returns nothing
local thistype this = thistype.allocate()
set .casterId = unitId
set .order = orderId
set .duration = cooldown
set .loopTimer = NewTimerEx(this)
if not OrderInCooldown[.casterId][.order] then
set OrderInCooldown[.casterId][.order] = true
endif
call TimerStart(.loopTimer, COOLDOWN_INTERVAL, true, function thistype.check)
endmethod
endstruct
struct PreventNewOrders
private integer casterId
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
endmethod
private static method check takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set OrderExecuted[.casterId] = false
call .destroy()
endmethod
static method start takes integer unitId returns nothing
local thistype this = thistype.allocate()
set .casterId = unitId
set .loopTimer = NewTimerEx(this)
if not OrderExecuted[.casterId] then
set OrderExecuted[.casterId] = true
endif
call TimerStart(.loopTimer, 1., false, function thistype.check)
endmethod
endstruct
struct CustomOrder
private integer unitId
private integer totalOrders = 0
private integer array order[MAX_ORDERS]
private integer array priority[MAX_ORDERS]
private real array cooldown[MAX_ORDERS]
private integer array manaCost[MAX_ORDERS]
private widget array target[MAX_ORDERS]
private real array locX[MAX_ORDERS]
private real array locY[MAX_ORDERS]
private boolean array locBased[MAX_ORDERS]
private method onDestroy takes nothing returns nothing
local integer i = 0
set HasCustomOrders[.unitId] = false
loop
exitwhen i >= .totalOrders
set target[i] = null
set i = i + 1
endloop
endmethod
static method create takes unit u returns thistype
local thistype this
local integer id
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
if not HasCustomOrders[id] then
set HasCustomOrders[id] = true
set this = thistype.allocate()
set .unitId = id
set RegOrders[.unitId] = this
return RegOrders[.unitId]
else
return RegOrders[id]
endif
endmethod
method checkOrder takes string orderStr returns boolean
local integer i = 0
local boolean isRegistered = false
local integer orderId = OrderId(orderStr)
loop
exitwhen i >= .totalOrders or isRegistered
if orderId == .order[i] then
set isRegistered = true
endif
set i = i + 1
endloop
return isRegistered
endmethod
method checkOrderById takes integer orderId returns boolean
local integer i = 0
local boolean isRegistered = false
loop
exitwhen i >= .totalOrders or isRegistered
if orderId == .order[i] then
set isRegistered = true
endif
set i = i + 1
endloop
return isRegistered
endmethod
method isOrderInCooldown takes string orderStr returns boolean
local integer orderId = OrderId(orderStr)
return OrderInCooldown[.unitId][orderId]
endmethod
method isOrderInCooldownById takes integer orderId returns boolean
return OrderInCooldown[.unitId][orderId]
endmethod
method registerOrder takes integer orderId, widget target, real locX, real locY, boolean locBased, integer priority, real cooldown, integer manaCost returns nothing
set .order[.totalOrders] = orderId
if priority < LOWEST_PRIO then
set .priority[.totalOrders] = LOWEST_PRIO
elseif priority > HIGHEST_PRIO then
set .priority[.totalOrders] = HIGHEST_PRIO
else
set .priority[.totalOrders] = priority
endif
set .cooldown[.totalOrders] = cooldown
set .manaCost[.totalOrders] = manaCost
set .target[.totalOrders] = target
set .locX[.totalOrders] = locX
set .locY[.totalOrders] = locY
set .locBased[.totalOrders] = locBased
set .totalOrders = .totalOrders + 1
endmethod
// String Orders
method registerTargetOrder takes string orderStr, widget target, integer priority, real cooldown, integer manaCost returns nothing
local integer orderId = OrderId(orderStr)
call .registerOrder(orderId, target, 0, 0, false, priority, cooldown, manaCost)
endmethod
method registerPointOrder takes string orderStr, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
local integer orderId = OrderId(orderStr)
call .registerOrder(orderId, null, locX, locY, true, priority, cooldown, manaCost)
endmethod
method registerInstantOrder takes string orderStr, integer priority, real cooldown, integer manaCost returns nothing
local integer orderId = OrderId(orderStr)
call .registerOrder(orderId, null, 0, 0, false, priority, cooldown, manaCost)
endmethod
// Integer Orders
method registerTargetOrderById takes integer orderId, widget target, integer priority, real cooldown, integer manaCost returns nothing
call .registerOrder(orderId, target, 0, 0, false, priority, cooldown, manaCost)
endmethod
method registerPointOrderById takes integer orderId, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
call .registerOrder(orderId, null, locX, locY, true, priority, cooldown, manaCost)
endmethod
method registerInstantOrderById takes integer orderId, integer priority, real cooldown, integer manaCost returns nothing
call .registerOrder(orderId, null, 0, 0, false, priority, cooldown, manaCost)
endmethod
private method orderByPriority takes nothing returns nothing
local integer i = 0
local integer j
local integer tempOrder = 0
local integer tempPriority = 0
local real tempCooldown = 0
local integer tempMana = 0
local widget tempTarget
local real tempX = 0
local real tempY = 0
local boolean tempLocBased = false
local integer tempIndex
loop
exitwhen i > .totalOrders - 1
set j = i
set tempIndex = i
loop
exitwhen j > .totalOrders - 1
if .priority[tempIndex] < .priority[j + 1] then
set tempIndex = j + 1
endif
set j = j + 1
endloop
set tempOrder = .order[i]
set tempPriority = .priority[i]
set tempCooldown = .cooldown[i]
set tempMana = .manaCost[i]
set tempTarget = .target[i]
set tempX = .locX[i]
set tempY = .locY[i]
set tempLocBased = .locBased[i]
set .order[i] = .order[tempIndex]
set .priority[i] = .priority[tempIndex]
set .cooldown[i] = .cooldown[tempIndex]
set .manaCost[i] = .manaCost[tempIndex]
set .target[i] = .target[tempIndex]
set .locX[i] = .locX[tempIndex]
set .locY[i] = .locY[tempIndex]
set .locBased[i] = .locBased[tempIndex]
set .order[tempIndex] = tempOrder
set .priority[tempIndex] = tempPriority
set .cooldown[tempIndex] = tempCooldown
set .manaCost[tempIndex] = tempMana
set .target[tempIndex] = tempTarget
set .locX[tempIndex] = tempX
set .locY[tempIndex] = tempY
set .locBased[tempIndex] = tempLocBased
set i = i + 1
endloop
set tempTarget = null
endmethod
static method runOrders takes CustomOrder o returns nothing
local thistype this = o
local integer i = 0
local boolean hasUsedOrder = false
local unit caster
local real casterMana
local integer tempOrder
static if LIBRARY_UnitIndexerGUI then
set caster = GetUnitById(.unitId)
else
set caster = udg_UDexUnits[.unitId]
endif
set casterMana = GetUnitState(caster, UNIT_STATE_MANA)
if HasCustomOrders[.unitId] then
call .orderByPriority()
loop
exitwhen i >= .totalOrders or OrderExecuted[.unitId]
if not .isOrderInCooldownById(.order[i]) and casterMana >= .manaCost[i] then
if .target[i] != null then
if IssueTargetOrderById(caster, .order[i], .target[i]) then
set hasUsedOrder = true
endif
elseif .locBased[i] then
if IssuePointOrderById(caster, .order[i], .locX[i], .locY[i]) then
set hasUsedOrder = true
endif
elseif IssueImmediateOrderById(caster, .order[i]) then
set hasUsedOrder = true
endif
if hasUsedOrder then
call Cooldown.start(.unitId, .order[i], .cooldown[i])
call PreventNewOrders.start(.unitId)
endif
endif
set i = i + 1
endloop
endif
set caster = null
call .destroy()
endmethod
endstruct
private module Init
private static method onInit takes nothing returns nothing
call thistype.initArrays()
endmethod
endmodule
struct Controller extends array
private static timer loopTimer = CreateTimer()
//! textmacro LOAD_BEHAVIOR takes opName
set id = GetUnitTypeId(u)
set i = 0
loop
exitwhen i == TotalBehaviorsType[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredControllerType[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredControllerType[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitTypeBehavior[id][i]
call b.$opName$(u, secU)
endif
set i = i + 1
endloop
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
set i = 0
loop
exitwhen i == TotalBehaviors[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredController[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredController[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitBehavior[id][i]
call b.$opName$(u, secU)
endif
set i = i + 1
endloop
if HasCustomOrders[id] then
call CustomOrder.runOrders(RegOrders[id])
endif
//! endtextmacro
//! textmacro LOAD_BEHAVIOR_OTHERS takes opName
set id = GetUnitTypeId(u)
set i = 0
loop
exitwhen i == TotalBehaviorsType[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredControllerType[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredControllerType[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitTypeBehavior[id][i]
call b.$opName$(u, eventU, secU)
endif
set i = i + 1
endloop
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
set i = 0
loop
exitwhen i == TotalBehaviors[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredController[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredController[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitBehavior[id][i]
call b.$opName$(u, eventU, secU)
endif
set i = i + 1
endloop
if HasCustomOrders[id] then
call CustomOrder.runOrders(RegOrders[id])
endif
//! endtextmacro
public static method registerBehavior takes unit u, Behavior b, boolean isUserBehavior returns nothing
local integer i
local integer unitId
local mapcontrol mc
if isUserBehavior then
set mc = MAP_CONTROL_USER
else
set mc = MAP_CONTROL_COMPUTER
endif
static if LIBRARY_UnitIndexerGUI then
set unitId = GetUnitId(u)
else
set unitId = GetUnitUserData(u)
endif
set i = TotalBehaviors[unitId]
set UnitBehavior[unitId][i] = b
set RequiredController[unitId][i] = BehaviorMapController(mc)
set TotalBehaviors[unitId] = TotalBehaviors[unitId] + 1
call GroupAddUnit(BehaviorGlobalGroup, u)
set mc = null
endmethod
public static method registerBehaviorType takes integer uTypeId, Behavior b, boolean isUserBehavior returns nothing
local integer i = TotalBehaviorsType[uTypeId]
local mapcontrol mc
if isUserBehavior then
set mc = MAP_CONTROL_USER
else
set mc = MAP_CONTROL_COMPUTER
endif
set UnitTypeBehavior[uTypeId][i] = b
set RequiredControllerType[uTypeId][i] = BehaviorMapController(mc)
set TotalBehaviorsType[uTypeId] = TotalBehaviorsType[uTypeId] + 1
set mc = null
endmethod
public static method removeBehavior takes unit u, Behavior b returns nothing
local integer i = 0
local integer unitId
static if LIBRARY_UnitIndexerGUI then
set unitId = GetUnitId(u)
else
set unitId = GetUnitUserData(u)
endif
loop
exitwhen i == TotalBehaviors[unitId]
if UnitBehavior[unitId][i] == b then
set UnitBehavior[unitId][i] = 0
endif
set i = i + 1
endloop
endmethod
public static method removeBehaviorType takes integer uTypeId, Behavior b returns nothing
local integer i = 0
loop
exitwhen i == TotalBehaviorsType[uTypeId]
if UnitTypeBehavior[uTypeId][i] == b then
set UnitTypeBehavior[uTypeId][i] = 0
endif
set i = i + 1
endloop
endmethod
private static method registerCast takes nothing returns boolean
local unit u
local player p
local unit secU
local integer id
local Behavior b
local group g1 = CreateGroup()
local group g2 = CreateGroup()
local unit eventU
local integer i
set u = GetSpellAbilityUnit()
set p = GetOwningPlayer(u)
set secU = GetSpellTargetUnit()
//! runtextmacro LOAD_BEHAVIOR("onCast")
set u = GetSpellTargetUnit()
set p = GetOwningPlayer(u)
set secU = GetSpellAbilityUnit()
//! runtextmacro LOAD_BEHAVIOR("onTargeted")
if OTHER_UNITS_EVENTS then
set eventU = GetSpellAbilityUnit()
set secU = GetSpellTargetUnit()
call GroupEnumUnitsWithCollision(g1, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g1)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyCasts")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyCasts")
endif
call GroupRemoveUnit(g1, u)
set u = null
set p = null
endloop
set eventU = GetSpellTargetUnit()
set secU = GetSpellAbilityUnit()
call GroupEnumUnitsWithCollision(g2, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g2)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyTargeted")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyTargeted")
endif
call GroupRemoveUnit(g2, u)
set u = null
set p = null
endloop
set eventU = null
endif
call DestroyGroup(g1)
call DestroyGroup(g2)
set g1 = null
set g2 = null
set u = null
set p = null
set secU = null
return false
endmethod
private static method registerAttack takes nothing returns boolean
local unit u
local player p
local unit secU
local integer id
local Behavior b
local group g1 = CreateGroup()
local group g2 = CreateGroup()
local unit eventU
local integer i
set u = GetAttacker()
set p = GetOwningPlayer(u)
set secU = GetTriggerUnit()
//! runtextmacro LOAD_BEHAVIOR("onAttack")
set u = GetTriggerUnit()
set p = GetOwningPlayer(u)
set secU = GetAttacker()
//! runtextmacro LOAD_BEHAVIOR("onAttacked")
if OTHER_UNITS_EVENTS then
set eventU = GetAttacker()
set secU = GetTriggerUnit()
call GroupEnumUnitsWithCollision(g1, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g1)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyAttacks")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyAttacks")
endif
call GroupRemoveUnit(g1, u)
set u = null
set p = null
endloop
set eventU = GetTriggerUnit()
set secU = GetAttacker()
call GroupEnumUnitsWithCollision(g2, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g2)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyAttacked")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyAttacked")
endif
call GroupRemoveUnit(g2, u)
set u = null
set p = null
endloop
set eventU = null
endif
call DestroyGroup(g1)
call DestroyGroup(g2)
set g1 = null
set g2 = null
set u = null
set p = null
set secU = null
return false
endmethod
private static method registerDamage takes nothing returns boolean
local unit u
local player p
local unit secU
local integer id
local Behavior b
local group g1 = CreateGroup()
local group g2 = CreateGroup()
local unit eventU
local integer i
if udg_DamageEventAmount >= MIN_DMG then
set u = udg_DamageEventSource
set p = GetOwningPlayer(u)
set secU = udg_DamageEventTarget
//! runtextmacro LOAD_BEHAVIOR("onDamageDealt")
set u = udg_DamageEventTarget
set p = GetOwningPlayer(u)
set secU = udg_DamageEventSource
//! runtextmacro LOAD_BEHAVIOR("onDamageTaken")
if OTHER_UNITS_EVENTS then
set eventU = udg_DamageEventSource
set secU = udg_DamageEventTarget
call GroupEnumUnitsWithCollision(g1, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g1)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyDmgDealt")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyDmgDealt")
endif
call GroupRemoveUnit(g1, u)
set u = null
set p = null
endloop
set eventU = udg_DamageEventTarget
set secU = udg_DamageEventSource
call GroupEnumUnitsWithCollision(g2, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g2)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyDmgTaken")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyDmgTaken")
endif
call GroupRemoveUnit(g2, u)
set u = null
set p = null
endloop
set eventU = null
endif
endif
call DestroyGroup(g1)
call DestroyGroup(g2)
set g1 = null
set g2 = null
set u = null
set p = null
set secU = null
return false
endmethod
private static method registerKill takes nothing returns boolean
local unit u
local player p
local unit secU
local integer id
local Behavior b
local group g1 = CreateGroup()
local group g2 = CreateGroup()
local unit eventU
local integer i
set u = GetKillingUnit()
set p = GetOwningPlayer(u)
set secU = GetDyingUnit()
//! runtextmacro LOAD_BEHAVIOR("onKill")
if OTHER_UNITS_EVENTS then
set eventU = GetKillingUnit()
set secU = GetDyingUnit()
call GroupEnumUnitsWithCollision(g1, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g1)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyKills")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyKills")
endif
call GroupRemoveUnit(g1, u)
set u = null
set p = null
endloop
set eventU = GetDyingUnit()
set secU = GetKillingUnit()
call GroupEnumUnitsWithCollision(g2, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g2)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyDeath")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyDeath")
endif
call GroupRemoveUnit(g2, u)
set u = null
set p = null
endloop
set eventU = null
endif
call DestroyGroup(g1)
call DestroyGroup(g2)
set g1 = null
set g2 = null
set u = null
set p = null
set secU = null
return false
endmethod
private static method registerPeriod takes nothing returns nothing
local group g = CopyNewGroup(BehaviorGlobalGroup)
local unit u
local player p
local integer id
local Behavior b
local integer i
loop
set u = FirstOfGroup(g)
exitwhen u == null
set p = GetOwningPlayer(u)
set id = GetUnitTypeId(u)
set i = 0
loop
exitwhen i == TotalBehaviorsType[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredControllerType[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredControllerType[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitTypeBehavior[id][i]
call b.onPeriod(u)
endif
set i = i + 1
endloop
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
set i = 0
loop
exitwhen i == TotalBehaviors[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredController[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredController[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitBehavior[id][i]
call b.onPeriod(u)
endif
set i = i + 1
endloop
if HasCustomOrders[id] then
call CustomOrder.runOrders(RegOrders[id])
endif
call GroupRemoveUnit(g, u)
set u = null
set p = null
endloop
call DestroyGroup(g)
set g = null
endmethod
private static method initArrays takes nothing returns nothing
set OrderInCooldown = Boolean2D.create()
set OrderExecuted = Boolean1D.create()
set RegOrders = Integer1D.create()
set UnitBehavior = Integer2D.create()
set UnitTypeBehavior = Integer2D.create()
set RequiredController = Boolean2D.create()
set RequiredControllerType = Boolean2D.create()
set HasCustomOrders = Boolean1D.create()
set TotalBehaviors = Integer1D.create()
set TotalBehaviorsType = Integer1D.create()
endmethod
implement Init
private static method onInit takes nothing returns nothing
local trigger castEvent = CreateTrigger()
local trigger attackEvent = CreateTrigger()
local trigger damageEvent = CreateTrigger()
local trigger killEvent = CreateTrigger()
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.registerCast)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.registerAttack)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.registerKill)
call DestroyTrigger(castEvent)
call DestroyTrigger(attackEvent)
call DestroyTrigger(killEvent)
else
call TriggerRegisterAnyUnitEventBJ(castEvent, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(castEvent, function thistype.registerCast)
call TriggerRegisterAnyUnitEventBJ(attackEvent, EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(attackEvent, function thistype.registerAttack)
call TriggerRegisterAnyUnitEventBJ(killEvent, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(killEvent, function thistype.registerKill)
endif
call TriggerRegisterVariableEvent(damageEvent, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddCondition(damageEvent, function thistype.registerDamage)
call TimerStart(thistype.loopTimer, PERIODIC_INTERVAL, true, function thistype.registerPeriod)
set castEvent = null
set attackEvent = null
set damageEvent = null
set killEvent = null
endmethod
endstruct
struct BehaviorIndexingEvents
private static method onIndex takes nothing returns nothing
local unit u = GetIndexedUnit()
local boolean hasBehaviors = false
local integer id
local Behavior b
local integer i
set id = GetUnitTypeId(u)
set i = 0
loop
exitwhen i == TotalBehaviorsType[id] or hasBehaviors
set b = UnitTypeBehavior[id][i]
if b != 0 then
set hasBehaviors = true
endif
set i = i + 1
endloop
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
set i = 0
loop
exitwhen i == TotalBehaviors[id] or hasBehaviors
set b = UnitBehavior[id][i]
if b != 0 then
set hasBehaviors = true
endif
set i = i + 1
endloop
if hasBehaviors then
call GroupAddUnit(BehaviorGlobalGroup, u)
endif
set u = null
endmethod
private static method onDeindex takes nothing returns nothing
local unit u = GetIndexedUnit()
if IsUnitInGroup(u, BehaviorGlobalGroup) then
call GroupRemoveUnit(BehaviorGlobalGroup, u)
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
set BehaviorGlobalGroup = CreateGroup()
call OnUnitIndex(function thistype.onIndex)
call OnUnitDeindex(function thistype.onDeindex)
endmethod
endstruct
endlibrary
library AIBCAdapterGUI requires AIBehaviorController
// Clear variables to avoid accidentally inserting old values into new Behaviors
private function ClearVariables takes nothing returns nothing
set udg_Event_Caster = null
set udg_Event_Attacker = null
set udg_Event_Source = null
set udg_Event_Target = null
set udg_Event_Killer = null
set udg_Event_Victim = null
set udg_Event_Unit = null
set udg_Event_Ally = null
set udg_Event_Enemy = null
set udg_Behavior_Unit = null
set udg_Behavior_UnitType = 0
set udg_Behavior_IsPlayerBehavior = false
set udg_Behavior_OnPeriod = null
set udg_Behavior_OnCast = null
set udg_Behavior_OnTargeted = null
set udg_Behavior_OnAttack = null
set udg_Behavior_OnAttacked = null
set udg_Behavior_OnDamageDealt = null
set udg_Behavior_OnDamageTaken = null
set udg_Behavior_OnKill = null
set udg_Behavior_OnAllyCasts = null
set udg_Behavior_OnAllyTargeted = null
set udg_Behavior_OnAllyAttacks = null
set udg_Behavior_OnAllyAttacked = null
set udg_Behavior_OnAllyDmgDealt = null
set udg_Behavior_OnAllyDmgTaken = null
set udg_Behavior_OnAllyKills = null
set udg_Behavior_OnAllyDeath = null
set udg_Behavior_OnEnemyCasts = null
set udg_Behavior_OnEnemyTargeted = null
set udg_Behavior_OnEnemyAttacks = null
set udg_Behavior_OnEnemyAttacked = null
set udg_Behavior_OnEnemyDmgDealt = null
set udg_Behavior_OnEnemyDmgTaken = null
set udg_Behavior_OnEnemyKills = null
set udg_Behavior_OnEnemyDeath = null
call RemoveLocation(udg_CustomO_Location)
set udg_CustomO_Unit = null
set udg_CustomO_Target = null
set udg_CustomO_Location = null
set udg_CustomO_Priority = 0
set udg_CustomO_Cooldown = 0
set udg_CustomO_ManaCost = 0
set udg_CustomO_IsInCooldown = false
endfunction
// Behavior Template
private struct NewB extends Behavior
private trigger OnPeriod
private trigger OnCast
private trigger OnTargeted
private trigger OnAttack
private trigger OnAttacked
private trigger OnDamageDealt
private trigger OnDamageTaken
private trigger OnKill
private trigger OnAllyCasts
private trigger OnAllyTargeted
private trigger OnAllyAttacks
private trigger OnAllyAttacked
private trigger OnAllyDmgDealt
private trigger OnAllyDmgTaken
private trigger OnAllyKills
private trigger OnAllyDeath
private trigger OnEnemyCasts
private trigger OnEnemyTargeted
private trigger OnEnemyAttacks
private trigger OnEnemyAttacked
private trigger OnEnemyDmgDealt
private trigger OnEnemyDmgTaken
private trigger OnEnemyKills
private trigger OnEnemyDeath
private method assignVariables takes nothing returns nothing
set .OnPeriod = udg_Behavior_OnPeriod
set .OnCast = udg_Behavior_OnCast
set .OnTargeted = udg_Behavior_OnTargeted
set .OnAttack = udg_Behavior_OnAttack
set .OnAttacked = udg_Behavior_OnAttacked
set .OnDamageDealt = udg_Behavior_OnDamageDealt
set .OnDamageTaken = udg_Behavior_OnDamageTaken
set .OnKill = udg_Behavior_OnKill
set .OnAllyCasts = udg_Behavior_OnAllyCasts
set .OnAllyTargeted = udg_Behavior_OnAllyTargeted
set .OnAllyAttacks = udg_Behavior_OnAllyAttacks
set .OnAllyAttacked = udg_Behavior_OnAllyAttacked
set .OnAllyDmgDealt = udg_Behavior_OnAllyDmgDealt
set .OnAllyDmgTaken = udg_Behavior_OnAllyDmgTaken
set .OnAllyKills = udg_Behavior_OnAllyKills
set .OnAllyDeath = udg_Behavior_OnAllyDeath
set .OnEnemyCasts = udg_Behavior_OnEnemyCasts
set .OnEnemyTargeted = udg_Behavior_OnEnemyTargeted
set .OnEnemyAttacks = udg_Behavior_OnEnemyAttacks
set .OnEnemyAttacked = udg_Behavior_OnEnemyAttacked
set .OnEnemyDmgDealt = udg_Behavior_OnEnemyDmgDealt
set .OnEnemyDmgTaken = udg_Behavior_OnEnemyDmgTaken
set .OnEnemyKills = udg_Behavior_OnEnemyKills
set .OnEnemyDeath = udg_Behavior_OnEnemyDeath
endmethod
method onPeriod takes unit u returns nothing
if .OnPeriod != null then
set udg_Event_Unit = u
if TriggerEvaluate(.OnPeriod) then
call TriggerExecute(.OnPeriod)
endif
endif
endmethod
method onCast takes unit caster, unit target returns nothing
if .OnCast != null then
set udg_Event_Caster = caster
set udg_Event_Target = target
if TriggerEvaluate(.OnCast) then
call TriggerExecute(.OnCast)
endif
endif
endmethod
method onTargeted takes unit target, unit caster returns nothing
if .OnTargeted != null then
set udg_Event_Target = target
set udg_Event_Caster = caster
if TriggerEvaluate(.OnTargeted) then
call TriggerExecute(.OnTargeted)
endif
endif
endmethod
method onAttack takes unit attacker, unit target returns nothing
if .OnAttack != null then
set udg_Event_Attacker = attacker
set udg_Event_Target = target
if TriggerEvaluate(.OnAttack) then
call TriggerExecute(.OnAttack)
endif
endif
endmethod
method onAttacked takes unit target, unit attacker returns nothing
if .OnAttacked != null then
set udg_Event_Target = target
set udg_Event_Attacker = attacker
if TriggerEvaluate(.OnAttacked) then
call TriggerExecute(.OnAttacked)
endif
endif
endmethod
method onDamageDealt takes unit source, unit target returns nothing
if .OnDamageDealt != null then
set udg_Event_Source = source
set udg_Event_Target = target
if TriggerEvaluate(.OnDamageDealt) then
call TriggerExecute(.OnDamageDealt)
endif
endif
endmethod
method onDamageTaken takes unit target, unit source returns nothing
if .OnDamageTaken != null then
set udg_Event_Target = target
set udg_Event_Source = source
if TriggerEvaluate(.OnDamageTaken) then
call TriggerExecute(.OnDamageTaken)
endif
endif
endmethod
method onKill takes unit killer, unit victim returns nothing
if .OnKill != null then
set udg_Event_Killer = killer
set udg_Event_Victim = victim
if TriggerEvaluate(.OnKill) then
call TriggerExecute(.OnKill)
endif
endif
endmethod
method onAllyCasts takes unit u, unit ally, unit target returns nothing
if .OnAllyCasts != null then
set udg_Event_Unit = u
set udg_Event_Ally = ally
set udg_Event_Target = target
if TriggerEvaluate(.OnAllyCasts) then
call TriggerExecute(.OnAllyCasts)
endif
endif
endmethod
method onAllyTargeted takes unit u, unit ally, unit caster returns nothing
if .OnAllyTargeted != null then
set udg_Event_Unit = u
set udg_Event_Ally = ally
set udg_Event_Caster = caster
if TriggerEvaluate(.OnAllyTargeted) then
call TriggerExecute(.OnAllyTargeted)
endif
endif
endmethod
method onAllyAttacks takes unit u, unit ally, unit target returns nothing
if .OnAllyAttacks != null then
set udg_Event_Unit = u
set udg_Event_Ally = ally
set udg_Event_Target = target
if TriggerEvaluate(.OnAllyAttacks) then
call TriggerExecute(.OnAllyAttacks)
endif
endif
endmethod
method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
if .OnAllyAttacked != null then
set udg_Event_Unit = u
set udg_Event_Ally = ally
set udg_Event_Attacker = attacker
if TriggerEvaluate(.OnAllyAttacked) then
call TriggerExecute(.OnAllyAttacked)
endif
endif
endmethod
method onAllyDmgDealt takes unit u, unit ally, unit target returns nothing
if .OnAllyDmgDealt != null then
set udg_Event_Unit = u
set udg_Event_Ally = ally
set udg_Event_Target = target
if TriggerEvaluate(.OnAllyDmgDealt) then
call TriggerExecute(.OnAllyDmgDealt)
endif
endif
endmethod
method onAllyDmgTaken takes unit u, unit ally, unit source returns nothing
if .OnAllyDmgTaken != null then
set udg_Event_Unit = u
set udg_Event_Ally = ally
set udg_Event_Source = source
if TriggerEvaluate(.OnAllyDmgTaken) then
call TriggerExecute(.OnAllyDmgTaken)
endif
endif
endmethod
method onAllyKills takes unit u, unit ally, unit victim returns nothing
if .OnAllyKills != null then
set udg_Event_Unit = u
set udg_Event_Ally = ally
set udg_Event_Victim = victim
if TriggerEvaluate(.OnAllyKills) then
call TriggerExecute(.OnAllyKills)
endif
endif
endmethod
method onAllyDeath takes unit u, unit ally, unit killer returns nothing
if .OnAllyDeath != null then
set udg_Event_Unit = u
set udg_Event_Ally = ally
set udg_Event_Killer = killer
if TriggerEvaluate(.OnAllyDeath) then
call TriggerExecute(.OnAllyDeath)
endif
endif
endmethod
method onEnemyCasts takes unit u, unit enemy, unit target returns nothing
if .OnEnemyCasts != null then
set udg_Event_Unit = u
set udg_Event_Enemy = enemy
set udg_Event_Target = target
if TriggerEvaluate(.OnEnemyCasts) then
call TriggerExecute(.OnEnemyCasts)
endif
endif
endmethod
method onEnemyTargeted takes unit u, unit enemy, unit caster returns nothing
if .OnEnemyTargeted != null then
set udg_Event_Unit = u
set udg_Event_Enemy = enemy
set udg_Event_Caster = caster
if TriggerEvaluate(.OnEnemyTargeted) then
call TriggerExecute(.OnEnemyTargeted)
endif
endif
endmethod
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
if .OnEnemyAttacks != null then
set udg_Event_Unit = u
set udg_Event_Enemy = enemy
set udg_Event_Target = target
if TriggerEvaluate(.OnEnemyAttacks) then
call TriggerExecute(.OnEnemyAttacks)
endif
endif
endmethod
method onEnemyAttacked takes unit u, unit enemy, unit attacker returns nothing
if .OnEnemyAttacked != null then
set udg_Event_Unit = u
set udg_Event_Enemy = enemy
set udg_Event_Attacker = attacker
if TriggerEvaluate(.OnEnemyAttacked) then
call TriggerExecute(.OnEnemyAttacked)
endif
endif
endmethod
method onEnemyDmgDealt takes unit u, unit enemy, unit target returns nothing
if .OnEnemyDmgDealt != null then
set udg_Event_Unit = u
set udg_Event_Enemy = enemy
set udg_Event_Target = target
if TriggerEvaluate(.OnEnemyDmgDealt) then
call TriggerExecute(.OnEnemyDmgDealt)
endif
endif
endmethod
method onEnemyDmgTaken takes unit u, unit enemy, unit source returns nothing
if .OnEnemyDmgTaken != null then
set udg_Event_Unit = u
set udg_Event_Enemy = enemy
set udg_Event_Source = source
if TriggerEvaluate(.OnEnemyDmgTaken) then
call TriggerExecute(.OnEnemyDmgTaken)
endif
endif
endmethod
method onEnemyKills takes unit u, unit enemy, unit victim returns nothing
if .OnEnemyKills != null then
set udg_Event_Unit = u
set udg_Event_Enemy = enemy
set udg_Event_Victim = victim
if TriggerEvaluate(.OnEnemyKills) then
call TriggerExecute(.OnEnemyKills)
endif
endif
endmethod
method onEnemyDeath takes unit u, unit enemy, unit killer returns nothing
if .OnEnemyDeath != null then
set udg_Event_Unit = u
set udg_Event_Enemy = enemy
set udg_Event_Killer = killer
if TriggerEvaluate(.OnEnemyDeath) then
call TriggerExecute(.OnEnemyDeath)
endif
endif
endmethod
static method attach takes unit u, boolean isUserBehavior returns nothing
local NewB b = NewB.create()
call b.assignVariables()
call Controller.registerBehavior(u, b, isUserBehavior)
endmethod
static method attachType takes integer uTypeId, boolean isUserBehavior returns nothing
local NewB b = NewB.create()
call b.assignVariables()
call Controller.registerBehaviorType(uTypeId, b, isUserBehavior)
endmethod
endstruct
function AIBC_GUI_RegisterBehavior takes boolean isUnitType returns nothing
if isUnitType then
call NewB.attachType(udg_Behavior_UnitType, udg_Behavior_IsPlayerBehavior)
else
call NewB.attach(udg_Behavior_Unit, udg_Behavior_IsPlayerBehavior)
endif
call ClearVariables()
endfunction
endlibrary
library FootmanExample requires AIBehaviorController
// Defining a new Behavior
private struct Footman extends Behavior
// This event will have a 30% chance to fire when the Footman deals damage
method onDamageDealt takes unit source, unit target returns nothing
local unit u = source
local CustomOrder o = CustomOrder.create(u)
if GetRandomInt(1, 100) <= 30 then
// Defining the order, target, priority, cooldown and mana cost
call o.registerTargetOrder("thunderbolt", target, 50, 7, 0)
endif
set u = null
endmethod
// This event will fire when a nearby enemy attacks
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
local CustomOrder o = CustomOrder.create(u)
// If the enemy unit is a Necromancer, the order will be registered
if GetUnitTypeId(enemy) == 'unec' then
// Defining the order, target, priority, cooldown and mana cost
call o.registerTargetOrder("thunderbolt", enemy, 100, 7, 0)
endif
endmethod
// Creating the behavior and attaching it to all units of this type
private static method onInit takes nothing returns nothing
local Behavior b = Footman.create()
call Controller.registerBehaviorType('hfoo', b, false)
endmethod
endstruct
endlibrary
library KnightExample requires AIBehaviorController
// Defining a new Behavior
private struct Knight extends Behavior
// This one will fire when the Knight attacks another unit
method onAttack takes unit attacker, unit target returns nothing
local unit u = attacker
local CustomOrder o = CustomOrder.create(u)
// No target has been defined since the order to be registered will be an Instant Order
// Defining the order, priority, cooldown and mana cost
call o.registerInstantOrder("berserk", 100, 12, 0)
set u = null
endmethod
// Creating the behavior and attaching it to all units of this type
private static method onInit takes nothing returns nothing
local Behavior b = Knight.create()
call Controller.registerBehaviorType('hkni', b, false)
endmethod
endstruct
endlibrary
library RiflemanExample requires AIBehaviorController
// Defining a new Behavior
private struct Rifleman extends Behavior
// This event will fire when a Rifleman is attacked
method onAttacked takes unit target, unit attacker returns nothing
local unit u = target
local CustomOrder o = CustomOrder.create(u)
// If the attacker unit is an Abomination or Death Knight, the order will be registered
if GetUnitTypeId(attacker) == 'uabo' or GetUnitTypeId(attacker) == 'Udea' then
// No target has been defined since the order to be registered will be an Instant Order
// Defining the order, priority, cooldown and mana cost
call o.registerInstantOrder("bearform", 100, 0, 0)
endif
set u = null
endmethod
// Creating the behavior and attaching it to all units of this type
private static method onInit takes nothing returns nothing
local Behavior b = Rifleman.create()
call Controller.registerBehaviorType('hrif', b, false)
endmethod
endstruct
endlibrary
library SorceressExample requires AIBehaviorController
// Defining the first Behavior
private struct Sorceress1 extends Behavior
// This one will fire when the Sorceress attacks another unit
method onAttack takes unit attacker, unit target returns nothing
local unit u = attacker
local CustomOrder o = CustomOrder.create(u)
// Registering 2 orders inside the same event.
// In this case, the order that will be used first will be the one with higher priority if not in cooldown
// For the first order, we will use its Id because the order is based on Wand of Illusion
// The unit will cast this ability on itself
call o.registerTargetOrderById(852274, u, 100, 15, 50)
// Registering the second order
call o.registerTargetOrder("shockwave", target, 50, 10, 100)
set u = null
endmethod
// Creating the behavior and attaching it to all units of this type
private static method onInit takes nothing returns nothing
local Behavior b = Sorceress1.create()
call Controller.registerBehaviorType('hsor', b, false)
endmethod
endstruct
// Defining a second Behavior
private struct Sorceress2 extends Behavior
// Using an event fired by a nearby enemy unit. In this case, it will run when an enemy unit uses an ability
method onEnemyCasts takes unit u, unit enemy, unit target returns nothing
local CustomOrder o = CustomOrder.create(u)
// Checking if the enemy unit is a Necromancer
if GetUnitTypeId(enemy) == 'unec' then
call o.registerTargetOrder("frostnova", enemy, 100, 8, 75)
endif
endmethod
// Adding a second event to the Behavior. This one will run when a nearby enemy attacks another unit
method onEnemyAttacks takes unit u, unit enemy, unit attacker returns nothing
local CustomOrder o = CustomOrder.create(u)
// Checking if the enemy unit is a Necromancer
if GetUnitTypeId(enemy) == 'unec' then
call o.registerTargetOrder("frostnova", enemy, 100, 8, 75)
endif
endmethod
// Creating the behavior and attaching it to all units of this type
private static method onInit takes nothing returns nothing
local Behavior b = Sorceress2.create()
call Controller.registerBehaviorType('hsor', b, false)
endmethod
endstruct
endlibrary
library PaladinExample requires AIBehaviorController
// Defining a new Behavior
private struct Paladin extends Behavior
// Using 2 events fired by a nearby friendly unit and another event fired when a Paladin attacks
method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
local CustomOrder o = CustomOrder.create(u)
// Check if ally is below 50% hit points
if GetUnitStatePercent(ally, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE) <= 50. then
call o.registerTargetOrder("holybolt", ally, 30, 5, 60)
endif
endmethod
method onAllyAttacks takes unit u, unit ally, unit target returns nothing
local CustomOrder o = CustomOrder.create(u)
// If ally does not have the buff, then cast the ability
if not UnitHasBuffBJ(ally, 'Binf') then
call o.registerTargetOrder("innerfire", ally, 20, 3, 35)
endif
endmethod
method onAttack takes unit attacker, unit target returns nothing
local unit u = attacker
local CustomOrder o = CustomOrder.create(u)
// If the Paladin does not have the buff, then cast the ability on himself
if not UnitHasBuffBJ(u, 'Binf') then
call o.registerTargetOrder("innerfire", u, 15, 3, 35)
endif
set u = null
endmethod
// Creating the behavior and attaching it to all units of this type
private static method onInit takes nothing returns nothing
local Behavior b = Paladin.create()
call Controller.registerBehaviorType('Hpal', b, false)
endmethod
endstruct
endlibrary
library PaladinSpc requires AIBehaviorController
// This will be a more complex example. Here I will define 4 custom behaviors for 4 different Paladins
// Each Paladin will only use the Behavior that will be asigned to him
// If combined with the example above, all Paladins will have 2 custom Behaviors (Individual and Unit Type)
// Northwest Paladin
private struct PaladinNW extends Behavior
method onAttack takes unit attacker, unit target returns nothing
local unit u = attacker
local CustomOrder o = CustomOrder.create(u)
call o.registerTargetOrder("acidbomb", target, 25, 12, 75)
set u = null
endmethod
// Creating the behavior and attaching it to an specific Paladin
// Unlike the behaviors assigned to an Unit Type, the behaviors attached to an individual unit
// must be registered after all units have been indexed, so that custom values are available
private static method register takes nothing returns nothing
local Behavior b = PaladinNW.create()
call Controller.registerBehavior(gg_unit_Hpal_0038, b, false)
endmethod
// Once all the units are indexed, the behavior will be registered
private static method onInit takes nothing returns nothing
call OnUnitIndexerInitialized(function thistype.register)
endmethod
endstruct
// Northeast Paladin
private struct PaladinNE extends Behavior
method onAttacked takes unit target, unit attacker returns nothing
local unit u = target
local CustomOrder o = CustomOrder.create(u)
call o.registerInstantOrder("divineshield", 30, 25, 50)
set u = null
endmethod
private static method register takes nothing returns nothing
local Behavior b = PaladinNE.create()
call Controller.registerBehavior(gg_unit_Hpal_0037, b, false)
endmethod
private static method onInit takes nothing returns nothing
call OnUnitIndexerInitialized(function thistype.register)
endmethod
endstruct
// Southwest Paladin
private struct PaladinSW extends Behavior
method onEnemyCasts takes unit u, unit enemy, unit target returns nothing
local CustomOrder o = CustomOrder.create(u)
if GetUnitTypeId(enemy) == 'Udea' then
call o.registerTargetOrder("soulburn", enemy, 20, 12, 85)
endif
endmethod
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
local CustomOrder o = CustomOrder.create(u)
if GetUnitTypeId(enemy) == 'Udea' then
call o.registerTargetOrder("soulburn", enemy, 20, 12, 85)
endif
endmethod
private static method register takes nothing returns nothing
local Behavior b = PaladinSW.create()
call Controller.registerBehavior(gg_unit_Hpal_0041, b, false)
endmethod
private static method onInit takes nothing returns nothing
call OnUnitIndexerInitialized(function thistype.register)
endmethod
endstruct
// Southeast Paladin
private struct PaladinSE extends Behavior
method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
local CustomOrder o = CustomOrder.create(u)
if GetUnitStatePercent(ally, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE) <= 70. then
call o.registerTargetOrder("healingwave", ally, 25, 9, 90)
endif
endmethod
private static method register takes nothing returns nothing
local Behavior b = PaladinSE.create()
call Controller.registerBehavior(gg_unit_Hpal_0046, b, false)
endmethod
private static method onInit takes nothing returns nothing
call OnUnitIndexerInitialized(function thistype.register)
endmethod
endstruct
endlibrary
library DKExample requires AIBehaviorController
// Defining a new Behavior for a Player-owned unit, in this case for our Death Knight
private struct DK extends Behavior
// This event will fire when a nearby enemy attacks
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
local CustomOrder o = CustomOrder.create(u)
// If our Death Knight is low on mana, he will automatically use one of the available mana potions
if UnitHasItemOfTypeBJ(u, 'pman') and GetUnitState(u, UNIT_STATE_MANA) <= 80. then
call UnitUseItem(u, GetItemOfTypeFromUnitBJ(u, 'pman'))
endif
// Ordering our Death Knight to automatically use Death Coil against the attacking enemy
call o.registerTargetOrder("deathcoil", enemy, 100, 6, 75)
endmethod
// Creating the behavior and attaching it to all units of this type
private static method onInit takes nothing returns nothing
local Behavior b = DK.create()
call Controller.registerBehaviorType('Udea', b, true) // If true, this Behavior will trigger only for Player units
endmethod
endstruct
endlibrary