Name | Type | is_array | initial_value |
Anim_Index_Var | integer | No | |
Base_Incr | integer | No | |
CustomRace_AISetupTrig | trigger | No | |
CustomRace_BuildingID | unitcode | Yes | |
CustomRace_DefName | string | No | |
CustomRace_DefRace | race | No | |
CustomRace_Description | string | No | |
CustomRace_Display | string | No | |
CustomRace_FactionHall | unitcode | No | |
CustomRace_FactionWorker | unitcode | No | |
CustomRace_HallID | unitcode | Yes | |
CustomRace_HeroID | unitcode | Yes | |
CustomRace_Player | player | No | |
CustomRace_PlayerMine | unit | No | |
CustomRace_Playlist | string | No | |
CustomRace_PreloadPLDTrig | trigger | No | |
CustomRace_SetupTrig | trigger | No | |
CustomRace_StartLocation | location | No | |
CustomRace_UnitID | unitcode | Yes | |
Display_Abil_Active | boolean | No | |
Incr | integer | No | |
Max_Incr | integer | No | 50 |
Temp_Group | group | No |
library Init
module Init
private static method onTimerInit takes nothing returns nothing
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
static if thistype.timerInit.exists then
call thistype.timerInit()
endif
endmethod
private static method onInit takes nothing returns nothing
static if thistype.timerInit.exists then
local timer t = CreateTimer()
call TimerStart(t, 0.00, false, function thistype.onTimerInit)
set t = null
endif
static if thistype.init.exists then
call thistype.init()
endif
endmethod
endmodule
endlibrary
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Alloc ~~ By Sevion ~~ Version 1.09 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Alloc?
// - Alloc implements an intuitive allocation method for array structs
//
// =Pros=
// - Efficient.
// - Simple.
// - Less overhead than regular structs.
//
// =Cons=
// - Must use array structs (hardly a con).
// - Must manually call OnDestroy.
// - Must use Delegates for inheritance.
// - No default values for variables (use onInit instead).
// - No array members (use another Alloc struct as a linked list or type declaration).
//
// Methods:
// - struct.allocate()
// - struct.deallocate()
//
// These methods are used just as they should be used in regular structs.
//
// Modules:
// - Alloc
// Implements the most basic form of Alloc. Includes only create and destroy
// methods.
//
// Details:
// - Less overhead than regular structs
//
// - Use array structs when using Alloc. Put the implement at the top of the struct.
//
// - Alloc operates almost exactly the same as default structs in debug mode with the exception of onDestroy.
//
// How to import:
// - Create a trigger named Alloc.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Nestharus for the method of allocation and suggestions on further merging.
// - Bribe for suggestions like the static if and method names.
// - PurgeandFire111 for some suggestions like the merging of Alloc and AllocX as well as OnDestroy stuff.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Alloc requires /*
----------------------
*/ optional Table, /*
----------------------
*/
module Alloc
private thistype recycle
static method allocate takes nothing returns thistype
local thistype this = thistype(0).recycle
if (this.recycle == 0) then
if (integer(this) >= JASS_MAX_ARRAY_SIZE - 2) then
static if thistype.DEBUG_ALLOC then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to allocate too many instances (thistype)!")
endif
return 0
endif
set this = thistype(integer(this) + 1)
set thistype(0).recycle = this
else
set thistype(0).recycle = this.recycle
set this.recycle = 0
endif
return this
endmethod
method deallocate takes nothing returns nothing
if (this.recycle != 0) then
static if thistype.DEBUG_ALLOC then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to deallocate an invalid (thistype) instance at [" + I2S(this) + "]!")
endif
return
endif
set this.recycle = thistype(0).recycle
set thistype(0).recycle = this
endmethod
endmodule
static if LIBRARY_Table then
module AllocT
private static Table allocMap = 0
static method allocate takes nothing returns thistype
local integer this = thistype.allocMap[0]
if 0 == thistype.allocMap[this] then
set this = this + 1
set thistype.allocMap[0] = this
else
set thistype.allocMap[0] = thistype.allocMap[this]
call thistype.allocMap.remove(this)
endif
return thistype(this)
endmethod
method deallocate takes nothing returns nothing
if 0 != thistype.allocMap[this] then
static if thistype.ALLOC_DEBUG_MODE then
call BJDebugMsg("thistype.deallocate >> Double-free detected on instance (" + I2S(this) + ")")
endif
return
endif
set thistype.allocMap[this] = thistype.allocMap[0]
set thistype.allocMap[0] = this
endmethod
private static method onInit takes nothing returns nothing
set thistype.allocMap = Table.create()
endmethod
endmodule
endif
endlibrary
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
/*****************************************************************************
*
* List<T> v2.1.2.4
* by Bannar
*
* Doubly-linked list.
*
******************************************************************************
*
* Requirements:
*
* Table by Bribe
* hiveworkshop.com/threads/snippet-new-table.188084/
*
* Alloc - choose whatever you like
* e.g.: by Sevion hiveworkshop.com/threads/snippet-alloc.192348/
*
******************************************************************************
*
* Implementation:
*
* macro DEFINE_LIST takes ACCESS, NAME, TYPE
*
* macro DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
*
* ACCESS - encapsulation, choose restriction access
* NAME - name of list type
* TYPE - type of values stored
*
* Implementation notes:
*
* - DEFINE_STRUCT_LIST macro purpose is to provide natural typecasting syntax for struct types.
* - <NAME>Item structs inline directly into hashtable operations thus generate basically no code.
* - Lists defined with DEFINE_STRUCT_LIST are inlined nicely into single create method and single integer array.
*
******************************************************************************
*
* struct API:
*
* struct <NAME>Item:
*
* | <TYPE> data
* | <NAME>Item next
* | <NAME>Item prev
*
*
* General:
*
* | static method create takes nothing returns thistype
* | Default ctor.
* |
* | static method operator [] takes thistype other returns thistype
* | Copy ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
* |
* | method empty takes nothing returns boolean
* | Checks whether the list is empty.
* |
* | method size takes nothing returns integer
* | Returns size of a list.
* |
* | method terminated takes $TYPE$Name node returns boolean
* | Checks whether one has reached the end of the list.
*
*
* Access:
*
* | readonly <NAME>Item first
* | readonly <NAME>Item last
* | readonly integer count
* |
* | method front takes nothing returns $TYPE$
* | Retrieves first element.
* |
* | method back takes nothing returns $TYPE$
* | Retrieves last element.
*
*
* Modifiers:
*
* | method clear takes nothing returns nothing
* | Flushes list and recycles its nodes.
* |
* | method push takes $TYPE$ value returns thistype
* | Adds elements to the end.
* |
* | method unshift takes $TYPE$ value returns thistype
* | Adds elements to the front.
* |
* | method pop takes nothing returns thistype
* | Removes the last element.
* |
* | method shift takes nothing returns thistype
* | Removes the first element.
* |
* | method find takes $TYPE$ value returns $NAME$Item
* | Returns the first node which data equals value.
* |
* | method erase takes $NAME$Item node returns boolean
* | Removes node from the list, returns true on success.
* |
* | method removeElem takes $TYPE$ value returns thistype
* | Removes first element that equals value from the list.
*
******************************************************************************
*
* Interfaces: (new addition)
*
* - In the modified version, List structs now can optionally
* implement additional functions aside from the base version
* from the library. Some of these functions are called
* when calling certain methods. These are documented below:
*
* | method onInsert takes $NAME$Item node, $TYPE$ value, integer pos returns nothing
* | Runs upon calling push, or unshift.
* |
* | method onRemove takes $NAME$Item node, $TYPE$ value, boolean clearing returns nothing
* | Runs for each node when the list is being cleared or destroyed,
* | or when a node is removed via erase, removeElem, shift, pop,
* | destroy.
* |
* | method destructor takes nothing returns nothing
* | Runs when the list is destroyed.
*
*****************************************************************************/
library DLList requires /*
===============
*/ Table, /*
===============
- Bribe
- link: https://www.hiveworkshop.com/threads/snippet-new-table.188084/
===============
*/ Alloc, /*
===============
- Sevion
- link: https://www.hiveworkshop.com/threads/snippet-alloc.192348/
======================
*/ optional Init, /*
======================
---------------------------
|
| DLList
|
|---------------------------
|
| - A doubly linked list library written by Bannar.
| Updated to include external functionality to
| the base struct via modules.
|
---------------------------
*/
//! textmacro_once DEFINE_LIST takes PRIVACY, NAME, TYPE
static if not LIBRARY_Init then
private module $NAME$Init
private static method onInit takes nothing returns nothing
call thistype.init()
endmethod
endmodule
endif
$PRIVACY$ struct $NAME$Item extends array
implement AllocT
private static Table dataMap = 0
private static Table nextMap = 0
private static Table prevMap = 0
method operator data takes nothing returns $TYPE$
return thistype.dataMap.$TYPE$[integer(this)]
endmethod
method operator next takes nothing returns thistype
return thistype.nextMap[integer(this)]
endmethod
method operator prev takes nothing returns thistype
return thistype.prevMap[integer(this)]
endmethod
method operator data= takes $TYPE$ value returns nothing
set thistype.dataMap.$TYPE$[integer(this)] = value
endmethod
method operator next= takes thistype value returns nothing
set thistype.nextMap[integer(this)] = value
endmethod
method operator prev= takes thistype value returns nothing
set thistype.prevMap[integer(this)] = value
endmethod
// System only method. Do not touch!
method destroy takes nothing returns nothing
call thistype.dataMap.$TYPE$.remove(integer(this))
call thistype.nextMap.remove(integer(this))
call thistype.prevMap.remove(integer(this))
call this.deallocate()
endmethod
private static method init takes nothing returns nothing
set dataMap = Table.create()
set nextMap = Table.create()
set prevMap = Table.create()
endmethod
static if not LIBRARY_Init then
implement $NAME$Init
else
implement Init
endif
endstruct
$PRIVACY$ struct $NAME$ extends array
implement Alloc
private static Table ownerMap = 0
private boolean activated
private integer clearCount
readonly $NAME$Item first
readonly $NAME$Item last
readonly integer count
method inList takes $NAME$Item node returns boolean
return integer(thistype.ownerMap[node]) == integer(this)
endmethod
implement optional $NAME$Extras
private static method createNode takes thistype owner returns $NAME$Item
local $NAME$Item obj = $NAME$Item.allocate()
set thistype.ownerMap[obj] = owner
return obj
endmethod
private static method deleteNode takes $NAME$Item node returns nothing
local thistype owner = thistype.ownerMap[node]
local boolean pointZero = false
if node == 0 then
return
endif
set owner.count = owner.count - 1
if node == owner.first then
set pointZero = true
set owner.first = node.next
set owner.first.prev = node.prev
endif
if node == owner.last then
set pointZero = true
set owner.last = node.prev
set owner.last.next = node.next
endif
if (not pointZero) then
set node.next.prev = node.prev
set node.prev.next = node.next
endif
call thistype.ownerMap.remove(node)
static if thistype.onRemove.exists then
call owner.onRemove(node, node.data, owner.clearCount > 0)
endif
call node.destroy()
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.activated = true
set this.count = 0
set this.clearCount = 0
return this
endmethod
method push takes $TYPE$ value returns thistype
local $NAME$Item obj = thistype.createNode(this)
set this.count = this.count + 1
if this.count == 1 then
set this.first = obj
set obj.next = 0
set obj.prev = 0
else
set obj.prev = this.last
set obj.next = this.last.next
set this.last.next = obj
endif
set this.last = obj
set obj.data = value
static if thistype.onInsert.exists then
call this.onInsert(obj, value, this.count)
endif
return this
endmethod
method unshift takes $TYPE$ value returns thistype
local $NAME$Item obj = thistype.createNode(this)
set this.count = this.count + 1
if this.count == 1 then
set this.last = obj
set obj.next = 0
set obj.prev = 0
else
set obj.next = this.first
set obj.prev = this.first.prev
set this.first.prev = obj
endif
set this.first = obj
set obj.data = value
static if thistype.onInsert.exists then
call this.onInsert(obj, value, 1)
endif
return this
endmethod
method pop takes nothing returns thistype
local $NAME$Item node = this.last
if (this.count < 1) then
return this
endif
call thistype.deleteNode(node)
return this
endmethod
method shift takes nothing returns thistype
local $NAME$Item node = this.first
if (this.count < 1) then
return this
endif
call thistype.deleteNode(node)
return this
endmethod
method erase takes $NAME$Item obj returns boolean
if (integer(thistype.ownerMap[obj]) != integer(this)) or /*
*/ (this.count < 1) then
return false
endif
call thistype.deleteNode(obj)
return true
endmethod
method remove takes $NAME$Item obj returns boolean
return this.erase(obj)
endmethod
method find takes $TYPE$ value returns $NAME$Item
local $NAME$Item iter = this.first
loop
exitwhen this.terminated(iter) or iter.data == value
set iter = iter.next
endloop
return iter
endmethod
method removeElem takes $TYPE$ value returns thistype
local $NAME$Item iter = this.find(value)
if iter == 0 then
return this
endif
call thistype.deleteNode(iter)
return this
endmethod
method terminated takes $NAME$Item node returns boolean
return (node == $NAME$Item(0))
endmethod
method empty takes nothing returns boolean
return this.count == 0
endmethod
method size takes nothing returns integer
return this.count
endmethod
method front takes nothing returns $TYPE$
return this.first.data
endmethod
method back takes nothing returns $TYPE$
return this.last.data
endmethod
method clear takes nothing returns integer
local $NAME$Item iter = this.first
local $NAME$Item cur = iter
local integer count = IMaxBJ(this.count, 0)
if this.clearCount > 0 then
return 0
endif
set this.clearCount = this.clearCount + 1
loop
exitwhen (this.count < 1) or (this.terminated(iter))
set iter = iter.next
call thistype.deleteNode(cur)
set cur = iter
endloop
set this.clearCount = this.clearCount - 1
return count
endmethod
method destroy takes nothing returns nothing
if not this.activated then
return
endif
set this.activated = false
call this.clear()
static if thistype.destructor.exists then
call this.destructor()
endif
set this.count = 0
set this.clearCount = 0
call this.deallocate()
endmethod
method get takes integer index returns $NAME$Item
local $NAME$Item iter = this.first
if (index < 1) or (index > this.count) then
return $NAME$Item(0)
endif
loop
exitwhen (index < 2) or this.terminated(iter)
set iter = iter.next
set index = index - 1
endloop
return index
endmethod
static method operator [] takes thistype other returns thistype
local thistype this = thistype.create()
local $NAME$Item iter = other.first
loop
exitwhen other.terminated(iter)
call this.push(iter.data)
set iter = iter.next
endloop
return this
endmethod
private static method init takes nothing returns nothing
set thistype.ownerMap = Table.create()
endmethod
static if not LIBRARY_Init then
implement $NAME$Init
else
implement Init
endif
endstruct
//! endtextmacro
//! runtextmacro DEFINE_LIST("", "IntegerList", "integer")
//! textmacro_once DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// Cannot inherit methods via delegate due to limited array size
method operator data takes nothing returns $TYPE$
return IntegerListItem(this).data
endmethod
method operator data= takes $TYPE$ value returns nothing
set IntegerListItem(this).data = value
endmethod
method operator next takes nothing returns thistype
return IntegerListItem(this).next
endmethod
method operator next= takes thistype value returns nothing
set IntegerListItem(this).next = value
endmethod
method operator prev takes nothing returns thistype
return IntegerListItem(this).prev
endmethod
method operator prev= takes thistype value returns nothing
set IntegerListItem(this).prev = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
private delegate IntegerList parent
static method create takes nothing returns thistype
local thistype this = IntegerList.create()
set parent = this
return this
endmethod
method front takes nothing returns $TYPE$
return parent.front()
endmethod
method back takes nothing returns $TYPE$
return parent.back()
endmethod
endstruct
//! endtextmacro
endlibrary
library ListT requires DLList
endlibrary
/*****************************************************************************
*
* List<T> v2.1.2.3
* by Bannar
*
* Doubly-linked list.
*
******************************************************************************
*
* Requirements:
*
* Table by Bribe
* hiveworkshop.com/threads/snippet-new-table.188084/
*
* Alloc - choose whatever you like
* e.g.: by Sevion hiveworkshop.com/threads/snippet-alloc.192348/
*
******************************************************************************
*
* Implementation:
*
* macro DEFINE_LIST takes ACCESS, NAME, TYPE
*
* macro DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
*
* ACCESS - encapsulation, choose restriction access
* NAME - name of list type
* TYPE - type of values stored
*
* Implementation notes:
*
* - DEFINE_STRUCT_LIST macro purpose is to provide natural typecasting syntax for struct types.
* - <NAME>Item structs inline directly into hashtable operations thus generate basically no code.
* - Lists defined with DEFINE_STRUCT_LIST are inlined nicely into single create method and single integer array.
*
******************************************************************************
*
* struct API:
*
* struct <NAME>Item:
*
* | <TYPE> data
* | <NAME>Item next
* | <NAME>Item prev
*
*
* General:
*
* | static method create takes nothing returns thistype
* | Default ctor.
* |
* | static method operator [] takes thistype other returns thistype
* | Copy ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
* |
* | method empty takes nothing returns boolean
* | Checks whether the list is empty.
* |
* | method size takes nothing returns integer
* | Returns size of a list.
*
*
* Access:
*
* | readonly <NAME>Item first
* | readonly <NAME>Item last
* |
* | method front takes nothing returns $TYPE$
* | Retrieves first element.
* |
* | method back takes nothing returns $TYPE$
* | Retrieves last element.
*
*
* Modifiers:
*
* | method clear takes nothing returns nothing
* | Flushes list and recycles its nodes.
* |
* | method push takes $TYPE$ value returns thistype
* | Adds elements to the end.
* |
* | method unshift takes $TYPE$ value returns thistype
* | Adds elements to the front.
* |
* | method pop takes nothing returns thistype
* | Removes the last element.
* |
* | method shift takes nothing returns thistype
* | Removes the first element.
* |
* | method find takes $TYPE$ value returns $NAME$Item
* | Returns the first node which data equals value.
* |
* | method erase takes $NAME$Item node returns boolean
* | Removes node from the list, returns true on success.
* |
* | method removeElem takes $TYPE$ value returns thistype
* | Removes first element that equals value from the list.
*
*
*****************************************************************************/
library ListT requires Table, Alloc
//! runtextmacro DEFINE_LIST("", "IntegerList", "integer")
// Run here any global list types you want to be defined.
//! textmacro_once DEFINE_LIST takes ACCESS, NAME, TYPE
private module $NAME$ItemM
private static method onInit takes nothing returns nothing
call thistype.init()
endmethod
endmodule
$ACCESS$ struct $NAME$Item extends array
readonly static TableArray map = 0
method destroy takes nothing returns nothing
if thistype.map[1].integer[this] != 0 then
return
endif
call thistype.map[5].remove(this)
call thistype.map[4].integer.remove(this)
call thistype.map[3].integer.remove(this)
set thistype.map[1].integer[this] = thistype.map[1].integer[0]
set thistype.map[1].integer[0] = this
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.map[1].integer[0]
if thistype.map[1].integer[this] == 0 then
set this = this + 1
set thistype.map[1].integer[0] = this
else
set thistype.map[1].integer[0] = thistype.map[1].integer[this]
set thistype.map[1].integer[this] = 0
endif
return this
endmethod
method operator data takes nothing returns $TYPE$
return thistype.map[2].$TYPE$[this] // hashtable[ node, -1 ] = data
endmethod
method operator data= takes $TYPE$ value returns nothing
set thistype.map[2].$TYPE$[this] = value
endmethod
method operator next takes nothing returns thistype
return thistype.map[3][this] // hashtable[ node, -2 ] = next
endmethod
method operator next= takes thistype value returns nothing
set thistype.map[3][this] = value
endmethod
method operator prev takes nothing returns thistype
return thistype.map[4][this] // hashtable[ node, -3 ] = prev
endmethod
method operator prev= takes thistype value returns nothing
set thistype.map[4][this] = value
endmethod
private static method init takes nothing returns nothing
set thistype.map = TableArray[6]
endmethod
implement $NAME$ItemM
endstruct
$ACCESS$ struct $NAME$ extends array
readonly $NAME$Item first
readonly $NAME$Item last
private integer count
implement Alloc
private static method setNodeOwner takes $NAME$Item node, $NAME$ owner returns nothing
set $NAME$Item.map[5][node] = owner
endmethod
private static method getNodeOwner takes $NAME$Item node returns thistype
return $NAME$Item.map[5][node]
endmethod
private method createNode takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = $NAME$Item.create()
set node.data = value
call setNodeOwner(node, this) // ownership
return node
endmethod
private method deleteNode takes $NAME$Item node returns nothing
call node.destroy()
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set count = 0
return this
endmethod
method clear takes nothing returns nothing
local $NAME$Item node = first
local $NAME$Item temp
loop // recycle all Table indexes
exitwhen 0 == node
set temp = node.next
call deleteNode(node)
set node = temp
endloop
set first = 0
set last = 0
set count = 0
endmethod
method destroy takes nothing returns nothing
call clear()
call deallocate()
endmethod
method front takes nothing returns $TYPE$
return first.data
endmethod
method back takes nothing returns $TYPE$
return last.data
endmethod
method empty takes nothing returns boolean
return count == 0
endmethod
method size takes nothing returns integer
return count
endmethod
method push takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set last.next = node
set node.prev = last
else
set first = node
set node.prev = 0
endif
set last = node
set node.next = 0
set count = count + 1
return this
endmethod
method unshift takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set first.prev = node
set node.next = first
else
set last = node
set node.next = 0
endif
set first = node
set node.prev = 0
set count = count + 1
return this
endmethod
method pop takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = last
set last = last.prev
if last == 0 then
set first = 0
else
set last.next = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$NAME$::pop failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
method shift takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = first
set first = first.next
if first == 0 then
set last = 0
else
set first.prev = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$NAME$::shift failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
static method operator [] takes thistype other returns thistype
local thistype instance = create()
local $NAME$Item node = other.first
loop
exitwhen node == 0
call instance.push(node.data)
set node = node.next
endloop
return instance
endmethod
method find takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = first
loop
exitwhen node == 0 or node.data == value
set node = node.next
endloop
return node
endmethod
method erase takes $NAME$Item node returns boolean
if getNodeOwner(node) == this then // match ownership
if node == first then
call shift()
elseif node == last then
call pop()
else
set node.prev.next = node.next
set node.next.prev = node.prev
call deleteNode(node)
set count = count - 1
endif
return true
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$NAME$::erase failed for instance "+I2S(this)+". Attempted to remove invalid node "+I2S(node)+".")
endif
return false
endmethod
method remove takes $NAME$Item node returns boolean
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Method $NAME$::remove is obsolete, use $NAME$::erase instead.")
return erase(node)
endmethod
method removeElem takes $TYPE$ value returns thistype
local $NAME$Item node = find(value)
if node != 0 then
call erase(node)
endif
return this
endmethod
endstruct
//! endtextmacro
//! textmacro_once DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// Cannot inherit methods via delegate due to limited array size
method operator data takes nothing returns $TYPE$
return IntegerListItem(this).data
endmethod
method operator data= takes $TYPE$ value returns nothing
set IntegerListItem(this).data = value
endmethod
method operator next takes nothing returns thistype
return IntegerListItem(this).next
endmethod
method operator next= takes thistype value returns nothing
set IntegerListItem(this).next = value
endmethod
method operator prev takes nothing returns thistype
return IntegerListItem(this).prev
endmethod
method operator prev= takes thistype value returns nothing
set IntegerListItem(this).prev = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
private delegate IntegerList parent
static method create takes nothing returns thistype
local thistype this = IntegerList.create()
set parent = this
return this
endmethod
method front takes nothing returns $TYPE$
return parent.front()
endmethod
method back takes nothing returns $TYPE$
return parent.back()
endmethod
endstruct
//! endtextmacro
endlibrary
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
*
* Fields
* -------------------------
*
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
*
* readonly static integer centerX
* readonly static integer centerY
*
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX = R2I(GetRectMaxX(world))
set maxY = R2I(GetRectMaxY(world))
set minX = R2I(GetRectMinX(world))
set minY = R2I(GetRectMinY(world))
set centerX = R2I((maxX + minX)/2)
set centerY = R2I((minY + maxY)/2)
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
library BezierEasing /* 1.0.0
*************************************************************************************
*
* Build Cubic Bezier-based Easing functions
*
* Instead of solving for the point on the cubic bezier curve, BezierEasing
* solves for output Y where X is the input.
*
* Useful for adjusting animation rate smoothness
*
*************************************************************************************
*
* struct BezierEasing extends array
*
* static method create takes real ax, real ay, real bx, real by returns thistype
* - points (ax, ay) and (bx, by) are cubic bezier control points on 2D plane.
* - cx = cubic(0, ax, bx, 1)
* - cy = cubic(0, ay, by, 1)
* method operator [] takes real t returns real
* - real "t" is the given time progression whose value in [0..1] range
*
*************************************************************************************/
globals
/*
* Adjust precision of epsilon's value
* higher precision = lower performance
*
* May cause infinite loop if the
* precision is too high.
*/
private constant real EPSILON = 0.00001
endglobals
private function Abs takes real a returns real
if(a < 0) then
return -a
endif
return a
endfunction
private function Max takes real a, real b returns real
if(a < b) then
return b
endif
return a
endfunction
/*
* Float Equality Approximation
* Accuracy is influenced by EPSILON's value
*/
private function Equals takes real a, real b returns boolean
return Abs(a - b) <= EPSILON*Max(1., Max(Abs(a), Abs(b)))
endfunction
private function Bezier3 takes real a, real b, real c, real d, real t returns real
local real x = 1. - t
return x*x*x*a + 3*x*x*t*b + 3*x*t*t*c + t*t*t*d
endfunction
private module Init
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
struct BezierEasing extends array
private static thistype array r
private real x1
private real y1
private real x2
private real y2
static method create takes real ax, real ay, real bx, real by returns thistype
local thistype this = r[0]
if(r[this] == 0) then
set r[0] = this + 1
else
set r[0] = r[this]
endif
set r[this] = -1
set x1 = ax
set y1 = ay
set x2 = bx
set y2 = by
return this
endmethod
method operator [] takes real t returns real
/*
* Perform binary search for the equivalent points on curve
* by using the t factor of cubic beziers, where the input
* is equal to the bezier point's x, and the output is the
* point's y, respectively.
*/
local real lo = 0.
local real hi = 1.
local real mid
local real tx
local real ty
local real ax = x1
local real ay = y1
local real bx = x2
local real by = y2
/*
* Since bezier points lies within
* the [0, 1] bracket, just return
* the bound values.
*/
if(Equals(t, 0.)) or (t < 0.) then
return 0.
elseif(Equals(t, 1.)) or (t > 1.) then
return 1.
endif
/*
* Binary Search
*/
loop
set mid = (lo + hi)*0.5
set tx = Bezier3(0, ax, bx, 1, mid)
set ty = Bezier3(0, ay, by, 1, mid)
if(Equals(t, tx))then
return ty
elseif(t < tx) then
set hi = mid
else
set lo = mid
endif
endloop
return 0.
endmethod
method destroy takes nothing returns nothing
if(r[this] == -1) then
set r[this] = r[0]
set r[0] = this
set x1 = 0.
set y1 = 0.
set x2 = 0.
set y2 = 0.
endif
endmethod
private static method init takes nothing returns nothing
set r[0] = 1
endmethod
implement Init
endstruct
struct BezierEase extends array
readonly static BezierEasing inQuad
readonly static BezierEasing outQuad
readonly static BezierEasing inOutQuad
readonly static BezierEasing inCubic
readonly static BezierEasing outCubic
readonly static BezierEasing inOutCubic
readonly static BezierEasing inQuart
readonly static BezierEasing outQuart
readonly static BezierEasing inOutQuart
readonly static BezierEasing inQuint
readonly static BezierEasing outQuint
readonly static BezierEasing inOutQuint
readonly static BezierEasing inSine
readonly static BezierEasing outSine
readonly static BezierEasing inOutSine
readonly static BezierEasing inBack
readonly static BezierEasing outBack
readonly static BezierEasing inOutBack
readonly static BezierEasing inCirc
readonly static BezierEasing outCirc
readonly static BezierEasing inOutCirc
readonly static BezierEasing inExpo
readonly static BezierEasing outExpo
readonly static BezierEasing inOutExpo
readonly static BezierEasing linear
private static method init takes nothing returns nothing
set inSine = BezierEasing.create(0.47, 0, 0.745, 0.715)
set outSine = BezierEasing.create(0.39, 0.575, 0.565, 1)
set inOutSine = BezierEasing.create(0.445, 0.05, 0.55, 0.95)
set inQuad = BezierEasing.create(0.55, 0.085, 0.68, 0.53)
set outQuad = BezierEasing.create(0.25, 0.46, 0.45, 0.94)
set inOutQuad = BezierEasing.create(0.455, 0.03, 0.515, 0.955)
set inCubic = BezierEasing.create(0.55, 0.055, 0.675, 0.19)
set outCubic = BezierEasing.create(0.215, 0.61, 0.355, 1)
set inOutCubic = BezierEasing.create(0.645, 0.045, 0.355, 1)
set inQuart = BezierEasing.create(0.895, 0.03, 0.685, 0.22)
set outQuart = BezierEasing.create(0.165, 0.84, 0.44, 1)
set inOutQuart = BezierEasing.create(0.77, 0, 0.175, 1)
set inQuint = BezierEasing.create(0.755, 0.05, 0.855, 0.06)
set outQuint = BezierEasing.create(0.23, 1, 0.32, 1)
set inOutQuint = BezierEasing.create(0.86, 0, 0.07, 1)
set inExpo = BezierEasing.create(0.95, 0.05, 0.795, 0.035)
set outExpo = BezierEasing.create(0.19, 1, 0.22, 1)
set inOutExpo = BezierEasing.create(1, 0, 0, 1)
set inCirc = BezierEasing.create(0.6, 0.04, 0.98, 0.335)
set outCirc = BezierEasing.create(0.075, 0.82, 0.165, 1)
set inOutCirc = BezierEasing.create(0.785, 0.135, 0.15, 0.86)
set inBack = BezierEasing.create(0.6, -0.28, 0.735, 0.045)
set outBack = BezierEasing.create(0.175, 0.885, 0.32, 1.275)
set inOutBack = BezierEasing.create(0.68, -0.55, 0.265, 1.55)
set linear = BezierEasing.create(0.0, 0.0, 1.0, 1.0)
endmethod
implement Init
endstruct
endlibrary
library Flagbits
function IsFlagSet takes integer value, integer flag returns boolean
return BlzBitAnd(value, flag) == flag
endfunction
function SetFlag takes integer value, integer flag returns integer
return BlzBitOr(value, flag)
endfunction
function UnsetFlag takes integer value, integer flag returns integer
return BlzBitAnd(value, -flag - 1)
endfunction
endlibrary
library EventListener requires /*
--------------
*/ Table /*
--------------
--------------
*/ ListT /*
--------------
--------------
*/ Init /*
--------------
-------------------------------------
|
| EventListener - vJASS
|
|-------------------------------------
|
| Originally based on a previous attempt in Pseudo-Var event,
| which itself was inspired by jesus4lyf's Event, this
| system brings the ability to control the maximum depth of
| recursion of custom events, the maximum callback depth of
| callback functions within custom events, and the ability
| to trace back which callback functions were running at a
| certain depth, in addition to the ability to create and
| register custom events.
|
| The system allows the unregistration of callbacks, albeit
| with some quirks. While an EventListener is executing its
| responders (callback functions), any attempts to unregister
| a given callback will be delayed up until all of the responders
| have been executed and the depth of the EventListener has
| returned to 0. However, the specified callback will
| not run in subsequent executions. This is to avoid data-race
| conditions that may result in the execution of the responders
| ending abruptly.
|
| The system also allows the registration of callbacks as well.
| When registering a callback function to an event while the
| event is executing its responders, the callback function will
| NOT be immediately executed as well. However, it will be
| executed in subsequent attempts.
|
| Events can also be destroyed. When destroying an event which
| is currently executing or is in the callback stack, the event
| will suspend ALL subsequent responder (callback) executions.
| Thus, it is safe to use even in cases where data-races might
| occur.
|
| Also comes with the EventResponder class, which can be
| used to execute callback functions.
|
|-----------------------------------------------------------------
|
| API
|
|-----------------------------------------------------------------
|
| class EventListener {
| static method create()
| method destroy()
| - constructor and destructor functions
|
| method register(code callback) -> EventResponder {int}
| - Returns a generated EventResponder associated with callback.
|
| method unregister(EventResponder response) -> bool
| - Unregisters the EventResponder from the event.
| - Returns false if event is currently in the callback stack.
| - Otherwise, returns false if response is not found in
| the list.
| - Destroys response object if found and returns true/
|
| method run()
| - Executes all EventResponders in the list
| - Respects each EventResponder's state.
|
| method getMaxRecursionDepth() -> int
| method setMaxRecursionDepth(int value)
| - Gets and sets the recursion depth of the event.
|
| method getMaxCallbackDepth() -> int
| method setMaxCallbackDepth(int value)
| - Gets and sets the recursion depth of all callbacks
| associated with the event.
| - Prevents a callback from exceeding the recursion depth
| specified for it, even if the event can recursively
| go deeper.
|
| method getCurrentCallback() -> EventResponder {int}
| - Returns the most recent EventResponder of the event.
|
| method getDepthCallback(int depth) -> EventResponder {int}
| - Returns the EventResponder of the event at the specified
| depth.
| - Has a time complexity of O(N), so use wisely.
|
| method getCurrentDepth() -> int
| - Returns the current depth of the event.
| }
|
| class EventResponder {
| static method create(code callback)
| method destroy()
| - constructor and destructor functions
|
| method refresh(code callback) -> this
| - Changes callback function to specified callback.
| - Also enables the EventResponder if it was disabled
| via enable(false) and vice versa.
|
| method change(code callback) -> this
| - Also changes callback function to specified callback.
| - Respects the active status of the EventResponder.
|
| method run() -> this
| - Evaluates the trigger handling the callback function.
|
| method enable(bool flag) -> this
| - Enables or disables the EventResponder.
| - Operates based on active counter.
| - An active counter of 1 or higher will require
| more calls to enable(false) to effectively disable
| and vice versa.
|
| method conditionalRun() -> this
| - Evaluates the trigger handling the callback function.
| - Respects the active status of the EventResponder.
|
| method isEnabled() -> bool
| - Returns true if enabled; false otherwise.
|
| ----------------------
| |
| | Fields:
| |
| ----------------------
|
| integer data
| - Custom value that can be explicitly defined
| by the user.
| - Consider this field as private when the
| EventResponder is generated by an EventListener.
| }
|
-----------------------------------------------------------------
*/
//! runtextmacro DEFINE_LIST("private", "EventResponderList", "integer")
struct EventResponder extends array
private static TableArray parameterMap = 0
implement AllocT
// ==================================================== //
// Operator API //
// ==================================================== //
method operator data takes nothing returns integer
return parameterMap[0].integer[this]
endmethod
method operator data= takes integer newValue returns nothing
set parameterMap[0].integer[this] = newValue
endmethod
private method clear_data takes nothing returns nothing
call parameterMap[0].integer.remove(this)
endmethod
private method operator trigger takes nothing returns trigger
return parameterMap[1].trigger[this]
endmethod
private method operator trigger= takes trigger newTrigger returns nothing
set parameterMap[1].trigger[this] = newTrigger
endmethod
private method clear_trigger takes nothing returns nothing
call parameterMap[1].trigger.remove(this)
endmethod
private method operator activeCount takes nothing returns integer
return parameterMap[2].integer[this]
endmethod
private method operator activeCount= takes integer newValue returns nothing
set parameterMap[2].integer[this] = newValue
endmethod
private method clear_activeCount takes nothing returns nothing
call parameterMap[2].integer.remove(this)
endmethod
// ==================================================== //
// Public API //
// ==================================================== //
method destroy takes nothing returns nothing
if (not parameterMap[1].trigger.has(this)) then
return
endif
call DisableTrigger(this.trigger)
call DestroyTrigger(this.trigger)
call this.clear_data()
call this.clear_trigger()
call this.clear_activeCount()
call this.deallocate()
endmethod
method refresh takes code newCallback returns thistype
call DestroyTrigger(this.trigger)
set this.trigger = CreateTrigger()
set this.activeCount = 1
call TriggerAddCondition(this.trigger, Condition(newCallback))
return this
endmethod
method change takes code newCallback returns thistype
local integer prevCount = this.activeCount
call this.refresh(newCallback)
set this.activeCount = prevCount
if this.activeCount <= 0 then
call DisableTrigger(this.trigger)
endif
return this
endmethod
method run takes nothing returns thistype
call TriggerEvaluate(this.trigger)
return this
endmethod
method enable takes boolean flag returns thistype
if (flag) then
set this.activeCount = this.activeCount + 1
else
set this.activeCount = this.activeCount - 1
endif
if this.activeCount == 0 then
call DisableTrigger(this.trigger)
elseif this.activeCount == 1 then
call EnableTrigger(this.trigger)
endif
return this
endmethod
method conditionalRun takes nothing returns thistype
if (this.activeCount > 0) then
call TriggerEvaluate(this.trigger)
endif
return this
endmethod
// ==================================================== //
// Getter API //
// ==================================================== //
method isEnabled takes nothing returns boolean
return this.activeCount > 0
endmethod
// ==================================================== //
// Constructor API //
// ==================================================== //
static method create takes code callback returns thistype
local thistype this = allocate()
set this.trigger = CreateTrigger()
set this.activeCount = 1
call TriggerAddCondition(this.trigger, Condition(callback))
return this
endmethod
// ==================================================== //
private static method init takes nothing returns nothing
set parameterMap = TableArray[3]
endmethod
implement Init
endstruct
private struct EVLCallbackData extends array
readonly static constant integer STATUS_CODE_NORMAL = 0
readonly static constant integer STATUS_CODE_HALTED = 1
readonly static constant integer STATUS_CODE_INTERRUPTED = 2
private static EventResponder executor = 0
private static Table activeCallbackStack = 0
private static Table destroyedCallbackStack = 0
// Value is initialized in EventListener.
static code removeCallback = null
static code destroyCallback = null
readonly static integer totalRecursionDepth = 0
readonly static thistype array activeObject
readonly static EventResponder array activeCallback
readonly static thistype deadObject = 0
integer destroyStack
integer recursionDepth
integer maxRecursionDepth
integer maxCallbackStackDepth
static method operator [] takes integer this returns thistype
return thistype(this)
endmethod
static method operator curCallback takes nothing returns EventResponder
return activeCallback[totalRecursionDepth]
endmethod
static method updateCallback takes EventResponder response returns nothing
local EventResponder prevResponse = activeCallback[totalRecursionDepth]
if (prevResponse != 0) then
set activeCallbackStack.integer[prevResponse] = activeCallbackStack.integer[prevResponse] - 1
if (activeCallbackStack.integer[prevResponse] <= 0) then
call activeCallbackStack.remove(prevResponse)
if destroyedCallbackStack.integer[prevResponse] > 0 then
call destroyedCallbackStack.remove(prevResponse)
call executor.refresh(removeCallback).run()
endif
endif
endif
set activeCallback[totalRecursionDepth] = response
if (response != 0) then
set activeCallbackStack.integer[response] = activeCallbackStack.integer[response] + 1
endif
endmethod
static method execute takes nothing returns integer
local thistype this = activeObject[totalRecursionDepth]
local EventResponder response = activeCallback[totalRecursionDepth]
// When the instance is destroyed, prevent any future executions.
// When a callback is queued for termination, prevent
// subsequent execution.
if (this.destroyStack > 0) then
return STATUS_CODE_HALTED
endif
if (destroyedCallbackStack.integer[response] > 0) then
return STATUS_CODE_INTERRUPTED
endif
// Prevent an infinite loop by checking through here.
if ((this.maxRecursionDepth > 0) and (this.recursionDepth > this.maxRecursionDepth)) or /*
*/ ((this.maxCallbackStackDepth > 0) and (activeCallbackStack.integer[response] > this.maxCallbackStackDepth)) then
return STATUS_CODE_INTERRUPTED
endif
call response.conditionalRun()
return STATUS_CODE_NORMAL
endmethod
static method safeCallbackRemove takes thistype this, EventResponder response returns integer
// This function should return a status code.
// Check if EventResponder is currently an active instance.
if activeCallbackStack.integer[response] > 0 then
// Response is definitely active.
set destroyedCallbackStack.integer[response] = destroyedCallbackStack.integer[response] + 1
return 1
endif
return 0
endmethod
static method safeDestroy takes thistype this returns integer
// This function should return a status code.
// Check if EventResponder is currently an active instance.
set this.destroyStack = this.destroyStack + 1
return 0
endmethod
static method push takes thistype this returns nothing
set totalRecursionDepth = totalRecursionDepth + 1
set this.recursionDepth = this.recursionDepth + 1
set activeObject[totalRecursionDepth] = this
endmethod
static method pop takes thistype this returns boolean
local thistype lastDeadObj = deadObject
set activeObject[totalRecursionDepth] = 0
set activeCallback[totalRecursionDepth] = 0
set this.recursionDepth = this.recursionDepth - 1
set totalRecursionDepth = totalRecursionDepth - 1
if (this.destroyStack > 0) and (this.recursionDepth < 1) then
set deadObject = this
call executor.refresh(destroyCallback).run()
set deadObject = lastDeadObj
return false
endif
return true
endmethod
static method queryCurrentCallback takes thistype this returns EventResponderListItem
local integer i = totalRecursionDepth
if this.recursionDepth < 1 then
return 0
endif
loop
exitwhen (i < 1) or (activeObject[i] == this)
set i = i - 1
endloop
return activeCallback[i]
endmethod
static method queryDepthCallback takes thistype this, integer depth returns EventResponderListItem
local integer i = 1
if ((depth < 1) or (depth > this.maxRecursionDepth) or /*
*/ (this.recursionDepth < 1)) then
return 0
endif
loop
exitwhen (i > totalRecursionDepth)
if (activeObject[i] == this) then
set depth = depth - 1
exitwhen (depth < 1)
endif
set i = i + 1
endloop
if (i > totalRecursionDepth) then
return 0
endif
return activeCallback[i]
endmethod
method isActive takes nothing returns boolean
return this.recursionDepth > 0
endmethod
private static method init takes nothing returns nothing
set activeCallbackStack = Table.create()
set destroyedCallbackStack = Table.create()
set executor = EventResponder.create(null)
endmethod
implement Init
endstruct
struct EventListener extends array
implement Alloc
private EventResponderList list
// ==================================================== //
// Operational API //
// ==================================================== //
method register takes code callback returns EventResponder
local EventResponder response = EventResponder.create(callback)
set response.data = this.list.push(response).last
return response
endmethod
method unregister takes EventResponder response returns boolean
local boolean result = false
if EVLCallbackData(this).isActive() then
call EVLCallbackData.safeCallbackRemove(this, response)
return false
endif
set result = this.list.erase(response.data)
if result then
call response.destroy()
endif
return result
endmethod
method run takes nothing returns thistype
local EventResponderListItem iter = this.list.first
local EventResponderListItem iterLast = this.list.last
call EVLCallbackData.push(this)
loop
call EVLCallbackData.updateCallback(EventResponder(iter.data))
exitwhen (EVLCallbackData.execute() == EVLCallbackData.STATUS_CODE_HALTED)
exitwhen (iter == iterLast)
set iter = iter.next
endloop
call EVLCallbackData.updateCallback(0)
// Depending on whether the instance is destroyed mid-execution
// or not, return a valid result regardless.
if (not EVLCallbackData.pop(this)) then
return 0
endif
return this
endmethod
// ==================================================== //
// Constructor API //
// ==================================================== //
static method create takes nothing returns thistype
local thistype this = allocate()
set this.list = EventResponderList.create()
set EVLCallbackData(this).maxRecursionDepth = 0
set EVLCallbackData(this).maxCallbackStackDepth = 0
return this
endmethod
private method destroyResponderList takes nothing returns nothing
local EventResponderListItem iter = this.list.first
local EventResponder response = EventResponder(iter.data)
loop
exitwhen this.list.empty()
call this.list.erase(response.data)
call response.destroy()
set iter = this.list.first
set response = EventResponder(iter.data)
endloop
call this.list.destroy()
endmethod
method destroy takes nothing returns nothing
if this.list == 0 then
return
endif
if EVLCallbackData(this).isActive() then
call EVLCallbackData.safeDestroy(this)
return
endif
call this.destroyResponderList()
set EVLCallbackData(this).destroyStack = 0
set EVLCallbackData(this).recursionDepth = 0
set EVLCallbackData(this).maxRecursionDepth = 0
set EVLCallbackData(this).maxCallbackStackDepth = 0
set this.list = 0
call this.deallocate()
endmethod
// ==================================================== //
// Getter and Setter API //
// ==================================================== //
method getMaxRecursionDepth takes nothing returns integer
return EVLCallbackData(this).maxRecursionDepth
endmethod
method setMaxRecursionDepth takes integer depth returns nothing
set EVLCallbackData(this).maxRecursionDepth = IMaxBJ(depth, 0)
endmethod
method getMaxCallbackDepth takes nothing returns integer
return EVLCallbackData(this).maxCallbackStackDepth
endmethod
method setMaxCallbackDepth takes integer depth returns nothing
set EVLCallbackData(this).maxCallbackStackDepth = IMaxBJ(depth, 0)
endmethod
method getCurrentCallback takes nothing returns EventResponder
return EVLCallbackData.queryCurrentCallback(this)
endmethod
method getDepthCallback takes integer depth returns EventResponder
return EVLCallbackData.queryDepthCallback(this, depth)
endmethod
method getCurrentDepth takes nothing returns integer
return EVLCallbackData(this).recursionDepth
endmethod
// ==================================================== //
// Dynamic Class instances //
// ==================================================== //
static method operator event takes nothing returns thistype
return thistype(EVLCallbackData.activeObject[EVLCallbackData.totalRecursionDepth])
endmethod
static method operator callback takes nothing returns EventResponder
return EVLCallbackData.activeCallback[EVLCallbackData.totalRecursionDepth]
endmethod
// ==================================================== //
private static method onRemoveCallback takes nothing returns nothing
local thistype this = thistype(EVLCallbackData.activeObject[EVLCallbackData.totalRecursionDepth])
local EventResponder response = EVLCallbackData.activeCallback[EVLCallbackData.totalRecursionDepth]
if this.list.remove(EventResponderListItem(response.data)) then
call response.destroy()
endif
endmethod
private static method onDestroyCallback takes nothing returns nothing
local thistype this = thistype(EVLCallbackData.deadObject)
call this.destroy()
endmethod
private static method init takes nothing returns nothing
set EVLCallbackData.removeCallback = function thistype.onRemoveCallback
set EVLCallbackData.destroyCallback = function thistype.onDestroyCallback
endmethod
implement Init
endstruct
endlibrary
library TimerUtils requires /*
--------------
*/ Table /*
--------------
--------------
*/ Init /*
--------------
*/
globals
private Table tData = 0
private timer tempTimer = null
endglobals
private struct S extends array
private static method init takes nothing returns nothing
set tData = Table.create()
endmethod
implement Init
endstruct
function NewTimerEx takes integer data returns timer
set tempTimer = CreateTimer()
set tData[GetHandleId(tempTimer)] = data
return tempTimer
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
function ReleaseTimer takes timer t returns integer
local integer data = tData[GetHandleId(t)]
call tData.remove(GetHandleId(t))
call PauseTimer(t)
call DestroyTimer(t)
return data
endfunction
function GetTimerData takes timer t returns integer
return tData[GetHandleId(t)]
endfunction
function SetTimerData takes timer t, integer value returns nothing
set tData[GetHandleId(t)] = value
endfunction
endlibrary
library QTimer requires /*
==================
*/ Table, /*
==================
- Bribe
- link: https://www.hiveworkshop.com/threads/snippet-new-table.188084/
==================
*/ Alloc, /*
==================
- Sevion
- link: https://www.hiveworkshop.com/threads/snippet-alloc.192348/
==================
*/ ListT, /*
==================
- Linked list library custom built for this system.
- Slightly modified to provide more features than the original.
===================
| |
| QTimer : |
| |
|=======================================================================
|
| A Timer library dedicated to providing queued timer functionality
| within a single object. Useful for creating engaging cinematics,
| or for creating meaningful queue-based game mechanics.
|
| A notable example is Dota's Burning Spear (Huskar). This mechanic
| deals damage over time and stacks indefinitely. This system can
| perfectly recreate that mechanic with a bit of ingenuity, and in
| the test map, a sample script is shown demonstrating the Dota 2's
| iteration of Burning Spear.
|
|=======================================================================
| |
| API |
| |
|=======================================================================
|
| QTimer.create()
| - Creates a new QTimer instance.
|
| QTimer(this).destroy()
| - Destroys the QTimer instance. This also causes any lingering
| callback to be executed immediately. The number of times the
| callback "executes" is provided by the static member curCount.
| - Can safely be called while a callback function is being executed.
|
| QTimer(this).repeat(real dur, code callback, integer data, integer count)
| - Causes the QTimer instance to repeat the callback function
| at most <count> times. If count is <= 0, the callback function
| will be repeated indefinitely.
| - When the QTimer instance is destroyed, callback functions which
| repeat indefinitely will execute only once. Callback functions
| which repeat for a certain number of times will be executed
| for the remaining amount of times.
|
| QTimer(this).start(real dur, code callback, integer data)
| - A wrapper for QTimer(this).repeat with 1 as the last parameter.
|
| QTimer(this).pause()
| - Pauses the QTimer.
|
| QTimer(this).resume()
| - Resumes the QTimer if there are any remaining callbacks to
| be executed and if the QTimer has already started.
|
|===============
| |
| Members |
| |
|=======================================================================
|
| QTimer(this).paused
| - Not to be confused with the method, this tells the user
| if the timer is currently paused or not.
|
| QTimer(this).elapsed <operator>
| - This returns the exact amount of time that has elapsed
| since the start of the timer (does not count time when
| it is paused.)
|
=======================================================================
*/
private keyword QTimerListItem
private function IsEqual takes real a, real b returns boolean
return RAbsBJ(a - b) <= 0.0001
endfunction
// This module is implemented by the generated struct QTimerList
// provided it exists.
private module QTimerListExtras
method switch takes QTimerListItem node, QTimerListItem other returns thistype
local QTimerListItem temp = node.prev
local QTimerListItem oTemp = other.prev
if (not this.inList(node)) or (not this.inList(other)) then
return this
endif
// Consider the case when both nodes are adjacent to each other
if (node.next == other) or (node.prev == other) then
if (node.next == other) then
set other.prev = node.prev
set node.next = other.next
set node.prev = other
set other.next = node
else
set node.prev = other.prev
set other.next = node.next
set other.prev = node
set node.next = other
endif
else
set temp.next = node.next
set oTemp.next = other.next
set node.next = oTemp.next
set other.next = temp.next
set node.prev = oTemp
set other.prev = temp
endif
set node.next.prev = node
set node.prev.next = node
set other.next.prev = other
set other.prev.next = other
return this
endmethod
method move takes QTimerListItem node, QTimerListItem other, boolean shift returns thistype
if (not this.inList(node)) or (not this.inList(other)) then
return this
endif
set node.next.prev = node.prev
set node.prev.next = node.next
if shift then
set node.next = other.next
set node.prev = other
else
set node.next = other
set node.prev = other.prev
endif
set node.next.prev = node
set node.prev.next = node
return this
endmethod
endmodule
//! runtextmacro DEFINE_LIST("private", "QTimerList", "integer")
private module QInit
private static method onInit takes nothing returns nothing
call thistype.init()
endmethod
endmodule
private struct QTimerNode extends array
implement Alloc
private boolean destroying
QTimer parent
QTimerListItem ptr
real timeStamp
real duration
trigger callback
integer data
integer ticks
integer executions
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.callback = CreateTrigger()
return this
endmethod
method destroy takes nothing returns nothing
if this.callback == null then
return
endif
call DestroyTrigger(this.callback)
set this.parent = 0
set this.ptr = 0
set this.data = 0
set this.ticks = 0
set this.executions = 0
set this.timeStamp = 0.0
set this.duration = 0.0
set this.callback = null
call this.deallocate()
endmethod
endstruct
struct QTimer extends array
private static constant boolean DEBUG_ALLOC = true
implement Alloc
private static Table timerMap = 0
// state members
readonly static thistype current = 0
readonly static integer curNode = 0
readonly static integer curData = 0
readonly static integer curCount = 0
readonly static integer execCount = 0
static boolean stopExecution = false
// class members
private QTimerList list
private real curDur
private timer timer
private boolean inCallback
private boolean started
private boolean destroying
readonly boolean paused
boolean singleton
private method sort takes QTimerNode obj returns nothing
local QTimerListItem iter = 0
local QTimerListItem temp = 0
local QTimerNode comp = 0
if this.list.size() < 1 then
set obj.ptr = this.list.push(obj).last
return
endif
if QTimerNode(this.list.last.data).timeStamp <= obj.timeStamp then
set obj.ptr = this.list.push(obj).last
return
elseif QTimerNode(this.list.first.data).timeStamp > obj.timeStamp then
set obj.ptr = this.list.unshift(obj).first
return
endif
set iter = this.list.last.prev
set comp = QTimerNode(iter.data)
loop
exitwhen this.list.terminated(iter)
if comp.timeStamp >= obj.timeStamp then
exitwhen true
endif
set iter = iter.prev
set comp = QTimerNode(iter.data)
endloop
set obj.ptr = this.list.push(obj).last
call this.list.move(obj.ptr, iter, true)
endmethod
private method execute takes QTimerNode obj, integer count, integer data, trigger callback returns boolean
local thistype prevCur = thistype.current
local integer prevData = thistype.curData
local integer prevCount = thistype.curCount
local integer prevExec = thistype.execCount
local integer prevNode = thistype.curNode
local boolean prevStop = thistype.stopExecution
local boolean result
set thistype.current = this
set thistype.curData = data
set thistype.curCount = count
set thistype.stopExecution = prevStop
set thistype.execCount = obj.executions
set thistype.curNode = obj
call TriggerEvaluate(callback)
set result = thistype.stopExecution
set thistype.curNode = prevNode
set thistype.execCount = prevExec
set thistype.stopExecution = prevStop
set thistype.current = prevCur
set thistype.curData = prevData
set thistype.curCount = prevCount
return result
endmethod
method operator elapsed takes nothing returns real
if this.inCallback then
return this.curDur
endif
return this.curDur + TimerGetElapsed(this.timer)
endmethod
private method removeList takes nothing returns nothing
local QTimerListItem iter = 0
local QTimerNode obj = 0
local QTimerNode comp = 0
local integer count = 0
local QTimerList list
if this.list == 0 then
return
endif
set list = this.list
set this.list = 0
if list.size() < 1 then
call list.destroy()
return
endif
if this.singleton then
set iter = this.list.first
set obj = QTimerNode(iter.data)
set count = IMaxBJ(obj.ticks, 1)
set iter = iter.next
set comp = QTimerNode(iter.data)
loop
exitwhen list.terminated(iter)
set iter = iter.next
set count = count + IMaxBJ(comp.ticks, 1)
call list.erase(comp.ptr)
call comp.destroy()
set comp = QTimerNode(iter.data)
endloop
set obj.executions = obj.executions + count
set obj.ticks = 0
call list.erase(obj.ptr)
call this.execute(obj, list.size(), obj.data, obj.callback)
call obj.destroy()
call list.destroy()
return
endif
loop
exitwhen list.empty()
set obj = QTimerNode(list.last.data)
set count = IMaxBJ(obj.ticks, 1)
set obj.executions = obj.executions + count
call list.erase(obj.ptr)
call this.execute(obj, count, obj.data, obj.callback)
call obj.destroy()
endloop
call list.destroy()
endmethod
method destroy takes nothing returns nothing
if this.timer == null then
return
endif
if this.inCallback then
set this.destroying = true
return
endif
call PauseTimer(this.timer)
call DestroyTimer(this.timer)
set this.timer = null
call this.removeList()
call thistype.timerMap.remove(GetHandleId(this.timer))
set this.curDur = 0.0
set this.inCallback = false
set this.started = false
set this.destroying = false
set this.paused = false
set this.singleton = false
call this.deallocate()
endmethod
private method processOnElapsed takes QTimerNode obj returns nothing
local real stamp = obj.timeStamp
local boolean interrupt
set obj.ticks = IMaxBJ(obj.ticks - 1, -1)
set obj.executions = obj.executions + 1
call this.list.erase(obj.ptr)
set interrupt = this.execute(obj, 1, obj.data, obj.callback)
if (this.destroying) or (obj.ticks == 0) or /*
*/ (interrupt) then
call obj.destroy()
return
endif
set obj.timeStamp = stamp + obj.duration
call this.sort(obj)
endmethod
private static method onElapsed takes nothing returns nothing
local thistype this = thistype.timerMap[GetHandleId(GetExpiredTimer())]
local QTimerNode obj = 0
local QTimerNode comp = 0
local real stamp = 0.0
set this.curDur = this.curDur + TimerGetTimeout(this.timer)
set this.inCallback = true
set obj = QTimerNode(this.list.first.data)
set stamp = obj.timeStamp
call this.processOnElapsed(obj)
loop
exitwhen this.list.empty() or this.destroying
set comp = QTimerNode(this.list.first.data)
if not IsEqual(stamp, comp.timeStamp) then
exitwhen true
endif
call this.processOnElapsed(comp)
endloop
set this.inCallback = false
if this.destroying then
call this.destroy()
return
endif
if this.paused then
return
endif
if not this.list.empty() then
set obj = QTimerNode(this.list.first.data)
call TimerStart(this.timer, obj.timeStamp - this.curDur, false, function thistype.onElapsed)
return
endif
set this.started = false
set this.paused = true
set this.curDur = 0.0
endmethod
method pause takes nothing returns boolean
if (this.paused) or (this.destroying) then
return false
endif
set this.paused = true
if (not this.inCallback) then
set this.curDur = this.curDur + TimerGetElapsed(this.timer)
endif
call PauseTimer(this.timer)
call TimerStart(this.timer, 0.00, false, null)
call PauseTimer(this.timer)
return true
endmethod
method resume takes nothing returns boolean
local QTimerNode obj = 0
if (not this.started) or (this.destroying) or /*
*/ (not this.paused) then
return false
endif
set obj = QTimerNode(this.list.first.data)
set this.paused = false
call TimerStart(this.timer, obj.timeStamp - this.curDur, false, function thistype.onElapsed)
return true
endmethod
method repeat takes real dur, code callback, integer data, integer count returns boolean
local QTimerNode obj = 0
// When the number of ticks is less than 0, the callback
// will repeat indefinitely.
if count <= 0 then
set count = -1
endif
if (this.timer == null) or (this.destroying) then
return false
endif
set this.paused = false
set this.started = true
set dur = RMaxBJ(dur, 0.0)
set obj = QTimerNode.create()
set obj.duration = dur
set obj.timeStamp = dur + this.elapsed
set obj.parent = this
set obj.data = data
set obj.ticks = count
call TriggerAddCondition(obj.callback, Condition(callback))
call this.sort(obj)
if (not this.inCallback) then
call this.pause()
call this.resume()
endif
return true
endmethod
method start takes real dur, code callback, integer data returns boolean
return this.repeat(dur, callback, data, 1)
endmethod
static method create takes nothing returns QTimer
local QTimer this = QTimer.allocate()
set this.list = QTimerList.create()
set this.timer = CreateTimer()
set this.curDur = 0.0
set this.paused = true
set this.started = false
set this.inCallback = false
set timerMap[GetHandleId(this.timer)] = this
return this
endmethod
private static method init takes nothing returns nothing
set thistype.timerMap = Table.create()
endmethod
implement QInit
endstruct
endlibrary
library GTimer requires TimerUtils, EventListener, Init
globals
// Use this for things that do not need to be too fluid
// in order to progress, such as auras.
constant integer UPDATE_TICK = 10
// Use this for things that demand fluidity in movement.
constant integer GAME_TICK = 60
endglobals
struct GTimer extends array
private static boolean array isRegistered
private static code onTimerCallback = null
readonly real timeout
private timer timer
private EventListener handler
private integer activeCallbacks
static method operator [] takes integer tick returns GTimer
return GTimer(IMaxBJ(tick, 0))
endmethod
method unregister takes EventResponder resp returns nothing
call this.handler.unregister(resp)
endmethod
method releaseCallback takes EventResponder resp returns GTimer
if (not resp.isEnabled()) then
return this
endif
call resp.enable(false)
set this.activeCallbacks = this.activeCallbacks - 1
if (this.activeCallbacks == 0) then
call PauseTimer(this.timer)
endif
return this
endmethod
method requestCallback takes EventResponder resp returns GTimer
if (resp.isEnabled()) then
return this
endif
call resp.enable(true)
set this.activeCallbacks = this.activeCallbacks + 1
if (this.activeCallbacks == 1) then
call TimerStart(this.timer, this.timeout, true, GTimer.onTimerCallback)
endif
return this
endmethod
static method register takes integer tick, code callback returns EventResponder
local GTimer this = GTimer(tick)
local EventResponder resp = 0
if (tick < 0) then
return EventResponder(0)
endif
if (not GTimer.isRegistered[tick]) then
set GTimer.isRegistered[tick] = true
set this.timeout = 1.0 / I2R(tick)
set this.timer = NewTimerEx(tick)
set this.handler = EventListener.create()
endif
set resp = this.handler.register(callback)
call resp.enable(false)
return resp
endmethod
// ==============================================================
private static method onTimerExecute takes nothing returns nothing
local GTimer this = GetTimerData(GetExpiredTimer())
call this.handler.run()
endmethod
private static method init takes nothing returns nothing
set GTimer.onTimerCallback = function GTimer.onTimerExecute
endmethod
implement Init
endstruct
endlibrary
library SetUnitTypeID requires Init
globals
private constant integer PLACEHOLDER_ABIL_ID = 'STRA'
private constant integer TRANSFORM_ABIL_ID = 'SPAS'
private abilityintegerlevelfield NORMAL_FIELD = null
private abilityintegerlevelfield ALTERNATE_FIELD = null
private constant abilityreallevelfield NORMAL_DURATION = ABILITY_RLF_DURATION_NORMAL
endglobals
private struct UnitTypeID extends array
private static method init takes nothing returns nothing
set NORMAL_FIELD = ConvertAbilityIntegerLevelField('Eme1')
set ALTERNATE_FIELD = ConvertAbilityIntegerLevelField('Emeu')
call BJDebugMsg("Normal field handle: " + I2S(GetHandleId(NORMAL_FIELD)))
call BJDebugMsg("Alternate field handle: " + I2S(GetHandleId(ALTERNATE_FIELD)))
call BJDebugMsg("Normal field id: " + I2S('Eme1'))
call BJDebugMsg("Alternate field id: " + I2S('Emeu'))
endmethod
implement Init
endstruct
// Since this type of functionality was not anticipated by Blizzard,
// this function was never introduced.
function SetUnitTypeId takes unit whichUnit, integer newTypeID returns boolean
local integer unitID = GetUnitTypeId(whichUnit)
local ability tempAbil
if (unitID == newTypeID) or (newTypeID == 0) then
return false
endif
// First, add the placeholder ability.
call UnitAddAbility(whichUnit, PLACEHOLDER_ABIL_ID)
call UnitMakeAbilityPermanent(whichUnit, true, PLACEHOLDER_ABIL_ID)
call BlzUnitDisableAbility(whichUnit, PLACEHOLDER_ABIL_ID, true, false)
// ====================================
// Next, add the transform ability.
call UnitAddAbility(whichUnit, TRANSFORM_ABIL_ID)
call UnitMakeAbilityPermanent(whichUnit, true, TRANSFORM_ABIL_ID)
call BlzUnitDisableAbility(whichUnit, TRANSFORM_ABIL_ID, true, false)
set tempAbil = BlzGetUnitAbility(whichUnit, TRANSFORM_ABIL_ID)
// Change the parameters of the transform ability.
if BlzSetAbilityIntegerLevelField(tempAbil, NORMAL_FIELD, 0, newTypeID) then
call BJDebugMsg("SetUnitTypeId >> NORMAL_FIELD was changed succesfully on TRANSFORM_ABIL_ID.")
else
call BJDebugMsg("SetUnitTypeId >> NORMAL_FIELD was unsuccesfully unchanged on TRANSFORM_ABIL_ID.")
endif
if BlzSetAbilityIntegerLevelField(tempAbil, ALTERNATE_FIELD, 0, unitID) then
call BJDebugMsg("SetUnitTypeId >> ALTERNATE_FIELD was changed succesfully on TRANSFORM_ABIL_ID.")
else
call BJDebugMsg("SetUnitTypeId >> ALTERNATE_FIELD was unsuccesfully unchanged on TRANSFORM_ABIL_ID.")
endif
// Re-enable the transform ability.
call BlzUnitDisableAbility(whichUnit, TRANSFORM_ABIL_ID, false, true)
// ====================================
set tempAbil = BlzGetUnitAbility(whichUnit, PLACEHOLDER_ABIL_ID)
if BlzSetAbilityIntegerLevelField(tempAbil, NORMAL_FIELD, 0, newTypeID) then
call BJDebugMsg("SetUnitTypeId >> NORMAL_FIELD was changed succesfully on PLACEHOLDER_ABIL_ID.")
else
call BJDebugMsg("SetUnitTypeId >> NORMAL_FIELD was unsuccesfully unchanged on PLACEHOLDER_ABIL_ID.")
endif
if BlzSetAbilityIntegerLevelField(tempAbil, ALTERNATE_FIELD, 0, unitID) then
call BJDebugMsg("SetUnitTypeId >> ALTERNATE_FIELD was changed succesfully on PLACEHOLDER_ABIL_ID.")
else
call BJDebugMsg("SetUnitTypeId >> ALTERNATE_FIELD was unsuccesfully unchanged on PLACEHOLDER_ABIL_ID.")
endif
call BlzUnitDisableAbility(whichUnit, PLACEHOLDER_ABIL_ID, false, true)
// Remove the transform ability
call UnitRemoveAbility(whichUnit, TRANSFORM_ABIL_ID)
call UnitRemoveAbility(whichUnit, PLACEHOLDER_ABIL_ID)
call BJDebugMsg("SetUnitTypeId >> Placeholder and transform abilities removed.")
return GetUnitTypeId(whichUnit) == newTypeID
endfunction
endlibrary
library Dummy requires Table, Alloc, Init, WorldBounds, TimerUtils, EventListener
native UnitAlive takes unit id returns boolean
private module DummyModuleOps
method operator x takes nothing returns real
return GetUnitX(this.dummy)
endmethod
method operator y takes nothing returns real
return GetUnitY(this.dummy)
endmethod
method operator z takes nothing returns real
return GetUnitFlyHeight(this.dummy)
endmethod
method operator paused takes nothing returns boolean
return IsUnitPaused(this.dummy)
endmethod
method operator x= takes real value returns nothing
call SetUnitX(this.dummy, value)
endmethod
method operator y= takes real value returns nothing
call SetUnitY(this.dummy, value)
endmethod
method operator z= takes real value returns nothing
call SetUnitFlyHeight(this.dummy, value, 0.0)
endmethod
method operator paused= takes boolean flag returns nothing
call PauseUnit(this.dummy, flag)
endmethod
method disableAbil takes integer abilID, boolean flag, boolean hide returns nothing
call BlzUnitDisableAbility(this.dummy, abilID, flag, hide)
endmethod
method addAbil takes integer abilID returns boolean
return UnitAddAbility(this.dummy, abilID)
endmethod
method removeAbil takes integer abilID returns boolean
return UnitRemoveAbility(this.dummy, abilID)
endmethod
method setAbilLvl takes integer abilID, integer lvl returns nothing
call SetUnitAbilityLevel(this.dummy, abilID, lvl)
endmethod
method incAbilLvl takes integer abilID, integer lvl returns nothing
call IncUnitAbilityLevel(this.dummy, abilID)
endmethod
method decAbilLvl takes integer abilID, integer lvl returns nothing
call DecUnitAbilityLevel(this.dummy, abilID)
endmethod
method issueOrderId takes integer order returns boolean
return IssueImmediateOrderById(this.dummy, order)
endmethod
method issuePointOrderId takes integer order, real x, real y returns boolean
return IssuePointOrderById(this.dummy, order, x, y)
endmethod
method issueTargetOrderId takes integer order, widget targ returns boolean
return IssueTargetOrderById(this.dummy, order, targ)
endmethod
endmodule
private module DummyModule
implement Alloc
private static constant integer DUMMY_ID = 'udum'
private static constant integer DUMMY_ANGLE_COUNT = 8
private static constant real DUMMY_ANGLE = 360.0 / I2R(DUMMY_ANGLE_COUNT)
private static constant player DUMMY_PLAYER = Player(PLAYER_NEUTRAL_PASSIVE)
private static TableArray dummyList = 0
private static Table dummyMap = 0
readonly static thistype current = 0
private timer recycleTimer
private EventResponder recycleResp
private integer listIndex
readonly unit dummy
integer data
implement DummyModuleOps
//! textmacro DUMMY_SHOW_UNIT
call SetUnitOwner(this.dummy, p, true)
call SetUnitFacing(this.dummy, angle)
call SetUnitX(this.dummy, x)
call SetUnitY(this.dummy, y)
call BlzUnitDisableAbility(this.dummy, 'Aloc', false, false)
call ShowUnit(this.dummy, true)
call IssueImmediateOrderById(this.dummy, 851972)
call PauseUnit(this.dummy, false)
//! endtextmacro
//! textmacro DUMMY_HIDE_UNIT
call PauseUnit(this.dummy, true)
call IssueImmediateOrderById(this.dummy, 851972)
call ShowUnit(this.dummy, false)
call BlzUnitDisableAbility(this.dummy, 'Aloc', true, true)
call SetUnitX(this.dummy, WorldBounds.minX)
call SetUnitY(this.dummy, WorldBounds.minY)
call SetUnitFacing(this.dummy, this.listIndex * DUMMY_ANGLE)
call SetUnitOwner(this.dummy, DUMMY_PLAYER, true)
//! endtextmacro
private static method angle2Index takes real angle returns integer
set angle = ModuloReal(angle + DUMMY_ANGLE / 2.0, 360.0)
return R2I(angle / DUMMY_ANGLE)
endmethod
private static method generate takes player p, real x, real y, real angle returns thistype
local thistype this = thistype.allocate()
set this.listIndex = -1
set this.dummy = CreateUnit(p, DUMMY_ID, x, y, angle)
set dummyMap[GetHandleId(this.dummy)] = this
call SetUnitPropWindow(this.dummy, 0.0)
return this
endmethod
static method operator [] takes unit whichDummy returns thistype
return dummyMap[GetHandleId(whichDummy)]
endmethod
private method destroy takes nothing returns nothing
call dummyMap.remove(GetHandleId(this.dummy))
call RemoveUnit(this.dummy)
set this.dummy = null
set this.listIndex = 0
set this.data = 0
call this.deallocate()
endmethod
method recycle takes nothing returns nothing
// Find nearest index from facing angle
local integer index = -1
local integer maxIndex = 0
// Only destroy dummies if they are dead.
if (not UnitAlive(this.dummy)) then
call this.destroy()
return
endif
if (this.listIndex >= 0) then
return
endif
set index = angle2Index(GetUnitFacing(this.dummy))
set maxIndex = dummyList[index].integer[0] + 1
set dummyList[index].integer[0] = maxIndex
set dummyList[index].integer[maxIndex] = this
set this.listIndex = index
set this.data = 0
//! runtextmacro DUMMY_HIDE_UNIT()
endmethod
private static method onRecycle takes nothing returns nothing
local thistype this = ReleaseTimer(GetExpiredTimer())
set current = this
call this.recycleResp.run()
call this.recycleResp.destroy()
set current = 0
set this.recycleTimer = null
set this.recycleResp = 0
call QueueUnitAnimation(this.dummy, "stand")
call this.recycle()
endmethod
method recycleTimed takes real delay, code callback returns boolean
if (this.recycleTimer != null) or (this.listIndex >= 0) then
return false
endif
set this.recycleTimer = NewTimerEx(this)
set this.recycleResp = EventResponder.create(callback)
call SetUnitAnimation(this.dummy, "death")
call TimerStart(this.recycleTimer, delay, false, function thistype.onRecycle)
return true
endmethod
static method request takes player p, real x, real y, real angle returns thistype
local thistype this = 0
local integer index = thistype.angle2Index(angle)
if (dummyList[index].integer[0] <= 0) then
set this = thistype.generate(p, x, y, angle)
else
set this = dummyList[index].integer[dummyList[index].integer[0]]
set this.listIndex = -1
set dummyList[index].integer[0] = dummyList[index].integer[0] - 1
//! runtextmacro DUMMY_SHOW_UNIT()
endif
return this
endmethod
private static method onInit takes nothing returns nothing
set dummyList = TableArray[DUMMY_ANGLE_COUNT]
set dummyMap = Table.create()
endmethod
endmodule
struct Dummy extends array
implement DummyModule
endstruct
endlibrary
library TimedEffect requires Alloc, TimerUtils
private struct TimedEffect extends array
implement Alloc
private effect effect
private static method onEffectDeath takes nothing returns nothing
local thistype this = ReleaseTimer(GetExpiredTimer())
call DestroyEffect(this.effect)
call this.deallocate()
endmethod
static method create takes string model, real x, real y, real dur returns thistype
local thistype this = thistype.allocate()
set this.effect = AddSpecialEffect(model, x, y)
call TimerStart(NewTimerEx(this), dur, false, function thistype.onEffectDeath)
return this
endmethod
static method createTarget takes string model, widget targ, string attach, real dur returns thistype
local thistype this = thistype.allocate()
set this.effect = AddSpecialEffectTarget(model, targ, attach)
call TimerStart(NewTimerEx(this), dur, false, function thistype.onEffectDeath)
return this
endmethod
endstruct
function AddSpecialEffectTimed takes string model, real x, real y, real dur returns nothing
call TimedEffect.create(model, x, y, dur)
endfunction
function AddSpecialEffectTargetTimed takes string model, widget targ, string attach, real dur returns nothing
call TimedEffect.createTarget(model, targ, attach, dur)
endfunction
endlibrary
library MiscUtils requires /*
----------------------------------
*/ GTimer, ListT, Alloc, Init /*
----------------------------------
--------------------------------
*/ BezierEasing, Flagbits /*
--------------------------------
*/
// a -> Starting point
// b -> End point
private function GetMedianValue takes real a, real b, real weight returns real
return a*(1.0 - weight) + b*(weight)
endfunction
struct EffectVisibility extends array
private static Table visCountMap = 0
private static Table visAlphaMap = 0
private static Table effectMap = 0
static method operator [] takes effect whichEffect returns thistype
local integer id = GetHandleId(whichEffect)
set effectMap.effect[id] = whichEffect
return id
endmethod
method operator visible takes nothing returns boolean
return visCountMap.integer[this] >= 0
endmethod
method operator visible= takes boolean flag returns nothing
local integer incr = 1
if (not flag) then
set incr = -1
endif
set visCountMap.integer[this] = visCountMap.integer[this] + incr
if (visCountMap.integer[this] == 0) then
if (not visAlphaMap.integer.has(this)) then
set visAlphaMap.integer[this] = 0xff
endif
call BlzSetSpecialEffectAlpha(effectMap.effect[this], visAlphaMap.integer[this])
elseif (visCountMap.integer[this] == -1) then
call BlzSetSpecialEffectAlpha(effectMap.effect[this], 0)
endif
endmethod
method operator alpha takes nothing returns integer
// This always assumes that nothing else calls BlzSetSpecialEffectAlpha.
if (not visAlphaMap.integer.has(this)) then
set visAlphaMap.integer[this] = 0xff
endif
return visAlphaMap.integer[this]
endmethod
method operator alpha= takes integer value returns nothing
set visAlphaMap.integer[this] = BlzBitAnd(value, 0xff)
if (visCountMap.integer[this] >= 0) then
call BlzSetSpecialEffectAlpha(effectMap.effect[this], visAlphaMap.integer[this])
endif
endmethod
static method clearInfo takes effect whichEffect returns nothing
local thistype this = GetHandleId(whichEffect)
if (effectMap.effect.has(this)) then
call visCountMap.integer.remove(this)
call visAlphaMap.integer.remove(this)
call effectMap.effect.remove(this)
endif
endmethod
private static method init takes nothing returns nothing
set visCountMap = Table.create()
set visAlphaMap = Table.create()
set effectMap = Table.create()
endmethod
implement Init
endstruct
struct VisibilityManager extends array
implement Alloc
private static constant integer VISIBILITY_TICK = 5
private static constant real TICK_INTERVAL = 1.0 / I2R(VISIBILITY_TICK)
private static constant integer MASK_UNIT = 1
private static constant integer MASK_EFFECT = 2
private static EventResponder objectResp = 0
private static IntegerList objectList = 0
private IntegerListItem objectListPtr
private integer objectMask
readonly unit unit
readonly effect effect
private integer startVis
private integer endVis
private integer curTick
private integer maxTick
private real curProg
private real auxChange
BezierEasing easeMode
// ====================================================
// Cleanup.
// ====================================================
private method destroy takes nothing returns nothing
call objectList.erase(this.objectListPtr)
set this.unit = null
set this.objectListPtr = 0
set this.startVis = 0
set this.endVis = 0
set this.curTick = 0
set this.curProg = 0.0
set this.auxChange = 0.0
set this.easeMode = 0
set this.maxTick = 0
set this.objectMask = 0
call this.deallocate()
endmethod
// ====================================================
// Visibility changing function
// ====================================================
private method changeVisibility takes real curValue, real prev returns nothing
local real change = GetMedianValue(this.startVis, this.endVis, curValue) - /*
*/ GetMedianValue(this.startVis, this.endVis, prev)
local integer finalValue = 0
if (this.objectMask == MASK_UNIT) then
set finalValue = BlzGetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_ALPHA) + R2I(change)
call SetUnitVertexColor(this.unit, /*
*/ BlzGetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_RED), /*
*/ BlzGetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_GREEN), /*
*/ BlzGetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_BLUE), /*
*/ finalValue)
call BlzSetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_ALPHA, /*
*/ finalValue)
set this.auxChange = this.auxChange + (change - R2I(change))
// Compensate for any straggling changes.
if (RAbsBJ(auxChange) > 1.0) then
set finalValue = finalValue + R2I(auxChange)
call SetUnitVertexColor(this.unit, /*
*/ BlzGetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_RED), /*
*/ BlzGetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_GREEN), /*
*/ BlzGetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_BLUE), /*
*/ finalValue)
call BlzSetUnitIntegerField(this.unit, UNIT_IF_TINTING_COLOR_ALPHA, /*
*/ finalValue)
set this.auxChange = this.auxChange - R2I(this.auxChange)
endif
elseif (this.objectMask == MASK_EFFECT) then
set this.auxChange = this.auxChange + (change - R2I(change))
set finalValue = EffectVisibility[this.effect].alpha + R2I(change) + R2I(this.auxChange)
set EffectVisibility[this.effect].alpha = finalValue
if (RAbsBJ(auxChange) > 1.0) then
set this.auxChange = this.auxChange - R2I(this.auxChange)
endif
endif
set this.curProg = curValue
endmethod
private static method onVisChange takes nothing returns nothing
local IntegerListItem iter = objectList.first
local thistype this = iter.data
loop
exitwhen iter == 0
set iter = iter.next
// Fill Contents here
// ==================================
set this.curTick = this.curTick + 1
call this.changeVisibility(this.easeMode[I2R(this.curTick) / I2R(this.maxTick)], /*
*/ this.curProg)
if (this.curTick >= this.maxTick) then
call this.destroy()
endif
// ==================================
set this = iter.data
endloop
if (objectList.empty()) then
call GTimer[VISIBILITY_TICK].releaseCallback(objectResp)
endif
endmethod
// ====================================================
// Public API
// ====================================================
private method objectPush takes nothing returns nothing
set this.objectListPtr = objectList.push(this).last
if (objectList.size() == 1) then
call GTimer[VISIBILITY_TICK].requestCallback(objectResp)
endif
endmethod
static method unitApply takes unit whichUnit, integer start, integer finish, real dur returns nothing
local thistype this = thistype.allocate()
set this.unit = whichUnit
set this.startVis = start
set this.endVis = finish
set this.curTick = 0
set this.curProg = 0.0
set this.easeMode = BezierEase.linear
set this.maxTick = IMaxBJ(R2I(dur / TICK_INTERVAL + 0.50), 1)
set this.objectMask = MASK_UNIT
call this.objectPush()
endmethod
static method effectApply takes effect whichEffect, integer start, integer finish, real dur returns nothing
local thistype this = thistype.allocate()
set this.effect = whichEffect
set this.startVis = start
set this.endVis = finish
set this.curTick = 0
set this.curProg = 0.0
set this.easeMode = BezierEase.linear
set this.maxTick = IMaxBJ(R2I(dur / TICK_INTERVAL + 0.50), 1)
set this.objectMask = MASK_EFFECT
call this.objectPush()
endmethod
// ====================================================
// Initialization Function
// ====================================================
private static method init takes nothing returns nothing
set objectList = IntegerList.create()
set objectResp = GTimer.register(VISIBILITY_TICK, function thistype.onVisChange)
endmethod
implement Init
endstruct
struct UnitAbilityDisable extends array
implement Alloc
unit unit
integer abilID
private static method onUnapply takes nothing returns nothing
local thistype this = ReleaseTimer(GetExpiredTimer())
call BlzUnitDisableAbility(this.unit, this.abilID, false, false)
call this.deallocate()
endmethod
static method apply takes unit whichUnit, integer abilID, real dur returns nothing
local thistype this = thistype.allocate()
set this.unit = whichUnit
set this.abilID = abilID
call BlzUnitDisableAbility(whichUnit, abilID, true, true)
call TimerStart(NewTimerEx(this), dur, false, function thistype.onUnapply)
endmethod
endstruct
function UnitDisableAbilityTimed takes unit whichUnit, integer abilID, real dur returns nothing
call UnitAbilityDisable.apply(whichUnit, abilID, dur)
endfunction
endlibrary
library IsDestructableTree uses optional UnitIndexer /* v1.3.1
*************************************************************************************
*
* Detect whether a destructable is a tree or not.
*
***************************************************************************
*
* Credits
*
* To PitzerMike
* -----------------------
*
* for IsDestructableTree
*
*************************************************************************************
*
* Functions
*
* function IsDestructableTree takes destructable d returns boolean
*
* function IsDestructableAlive takes destructable d returns boolean
*
* function IsDestructableDead takes destructable d returns boolean
*
* function IsTreeAlive takes destructable tree returns boolean
* - May only return true for trees.
*
* function KillTree takes destructable tree returns boolean
* - May only kill trees.
*
*/
globals
private constant integer HARVESTER_UNIT_ID = 'hpea' //* human peasant
private constant integer HARVEST_ABILITY = 'Ahrl' //* ghoul harvest
private constant integer HARVEST_ORDER_ID = 0xD0032 //* harvest order ( 852018 )
private constant player NEUTRAL_PLAYER = Player(PLAYER_NEUTRAL_PASSIVE)
private unit harvester = null
endglobals
function IsDestructableTree takes destructable d returns boolean
//* 851973 is the order id for stunned, it will interrupt the preceding harvest order.
return (IssueTargetOrderById(harvester, HARVEST_ORDER_ID, d)) and (IssueImmediateOrderById(harvester, 851973))
endfunction
function IsDestructableDead takes destructable d returns boolean
return (GetWidgetLife(d) <= 0.405)
endfunction
function IsDestructableAlive takes destructable d returns boolean
return (GetWidgetLife(d) > .405)
endfunction
function IsTreeAlive takes destructable tree returns boolean
return IsDestructableAlive(tree) and IsDestructableTree(tree)
endfunction
function KillTree takes destructable tree returns boolean
if (IsTreeAlive(tree)) then
call KillDestructable(tree)
return true
endif
return false
endfunction
private function Init takes nothing returns nothing
static if LIBRARY_UnitIndexer then//* You may adapt this to your own indexer.
set UnitIndexer.enabled = false
endif
set harvester = CreateUnit(NEUTRAL_PLAYER, HARVESTER_UNIT_ID, 0, 0, 0)
static if LIBRARY_UnitIndexer then
set UnitIndexer.enabled = true
endif
call UnitAddAbility(harvester, HARVEST_ABILITY)
call UnitAddAbility(harvester, 'Aloc')
call ShowUnit(harvester, false)
endfunction
//* Seriously?
private module Inits
private static method onInit takes nothing returns nothing
call Init()
endmethod
endmodule
private struct I extends array
implement Inits
endstruct
endlibrary
library ResearchChecker requires Init, WorldBounds
globals
private unit checker = null
private constant integer ABIL_ID = '^^^^'
private constant integer BUFF_ID = 'BOwk'
private constant integer UNIT_ID = 'udet'
private constant integer ORDER_ID = 852129
endglobals
function IsSynergyActivated takes nothing returns boolean
local boolean result = false
call PauseUnit(checker, false)
set result = IssueImmediateOrderById(checker, UNIT_ID) and IssueImmediateOrderById(checker, 851976)
call PauseUnit(checker, true)
return result
endfunction
function IsWhoisJohnGaltActivated takes nothing returns boolean
local boolean result = false
call PauseUnit(checker, false)
set result = IssueImmediateOrderById(checker, ORDER_ID) and IssueImmediateOrderById(checker, 851972)
call PauseUnit(checker, true)
return result
endfunction
private struct ResearchCheck extends array
private static method init takes nothing returns nothing
set checker = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), UNIT_ID, 0.0, 0.0, 0.0)
call ShowUnit(checker, false)
call PauseUnit(checker, true)
endmethod
implement Init
endstruct
function IsTechResearched takes player p, integer techID, integer level returns boolean
return GetPlayerTechCount(p, techID, true) >= level
endfunction
function IsAbilityReqLifted takes player p, integer techID, integer level returns boolean
return IsWhoisJohnGaltActivated() or IsTechResearched(p, techID, level)
endfunction
endlibrary
library CustomUnitStatFactory requires /*
--------------
*/ Init /*
--------------
--------------
*/ Alloc /*
--------------
--------------
*/ ListT /*
--------------
------------------
*/ UnitDex /*
------------------
----------------------
*/ EventListener /*
----------------------
*/
globals
private integer requestFlag = 0
private EventListener onEnterHandler = 0
private EventListener onLeaveHandler = 0
// Modifier types
constant integer STAT_ADD = 1
constant integer STAT_MULT = 2
// Request types
public constant integer REQUEST_DIRECT_AMOUNT = 1
public constant integer REQUEST_MODIFY_ATTR = 2
public constant integer REQUEST_CHANGE_COUNTER = 4
public constant integer REQUEST_DESTROY_STAT = 8
// Event types
private constant integer EVENT_STAT_MODIFY = 1
private constant integer EVENT_STAT_ACTIVATE = 2
private constant integer EVENT_STAT_DEACTIVATE = 4
endglobals
// ================================================================
private struct CUnitStatHandler extends array
// This is defined through the generated module below.
static EventResponder array modifyResponse
static EventResponder array activationResponse
static EventResponder array deactivationResponse
// Modify event values
readonly static integer eventIndex = 0
readonly static integer array eventType
readonly static unit array curUnit
readonly static real array prevAmt
readonly static real array curAmt
readonly static CStat array curStat
// Activation and deactivation event values
readonly static boolean array isActivating
// Called by the operator CStat:amount=
static method prepModifyHandler takes unit whichUnit, real prev, real cur, CStat stat returns nothing
set eventIndex = eventIndex + 1
set eventType[eventIndex] = EVENT_STAT_MODIFY
set curUnit[eventIndex] = whichUnit
set prevAmt[eventIndex] = prev
set curAmt[eventIndex] = cur
set curStat[eventIndex] = stat
endmethod
// Called by the method incActiveCounter and decActiveCounter.
static method prepActivationHandler takes unit whichUnit, boolean state, CStat stat returns nothing
set eventIndex = eventIndex + 1
set isActivating[eventIndex] = state
set curUnit[eventIndex] = whichUnit
set curStat[eventIndex] = stat
if (state) then
set eventType[eventIndex] = EVENT_STAT_ACTIVATE
else
set eventType[eventIndex] = EVENT_STAT_DEACTIVATE
endif
endmethod
static method callModifyHandler takes integer modClass returns nothing
call modifyResponse[modClass].run()
endmethod
static method callActivationHandler takes integer modClass returns nothing
call activationResponse[modClass].run()
endmethod
static method callDeactivationHandler takes integer modClass returns nothing
call deactivationResponse[modClass].run()
endmethod
static method releaseHandler takes nothing returns nothing
set eventIndex = IMaxBJ(eventIndex - 1, 0)
endmethod
private static method onEnter takes nothing returns nothing
call onEnterHandler.run()
endmethod
private static method onLeave takes nothing returns nothing
call onLeaveHandler.run()
endmethod
private static method init takes nothing returns nothing
set onEnterHandler = EventListener.create()
set onLeaveHandler = EventListener.create()
call OnUnitIndex(function thistype.onEnter)
call OnUnitDeindex(function thistype.onLeave)
endmethod
implement Init
endstruct
// ================================================================
private function IsFlagRequested takes integer flag returns boolean
return BlzBitAnd(requestFlag, flag) != 0
endfunction
// ================================================================
// While technically public functions, these should not be used
// manually at any point, since the system requires that these
// functions be accessible from the module side, which cannot be
// possible when such functions are declared private.
// ================================================================
public function AssignModifuncToClass takes integer classID, code callback returns nothing
set CUnitStatHandler.modifyResponse[classID] = EventResponder.create(callback)
endfunction
public function AssignActifuncToClass takes integer classID, code callback returns nothing
set CUnitStatHandler.activationResponse[classID] = EventResponder.create(callback)
endfunction
public function AssignDeactifuncToClass takes integer classID, code callback returns nothing
set CUnitStatHandler.deactivationResponse[classID] = EventResponder.create(callback)
endfunction
public function GetCurrentUnit takes nothing returns unit
return CUnitStatHandler.curUnit[CUnitStatHandler.eventIndex]
endfunction
public function GetPrevAmount takes nothing returns real
return CUnitStatHandler.prevAmt[CUnitStatHandler.eventIndex]
endfunction
public function GetCurrentAmount takes nothing returns real
return CUnitStatHandler.curAmt[CUnitStatHandler.eventIndex]
endfunction
public function GetCurrentStat takes nothing returns CStat
return CUnitStatHandler.curStat[CUnitStatHandler.eventIndex]
endfunction
public function GetStatActivationState takes nothing returns boolean
return CUnitStatHandler.isActivating[CUnitStatHandler.eventIndex]
endfunction
public function SetRequestFlag takes integer flag returns nothing
set requestFlag = BlzBitOr(requestFlag, flag)
endfunction
public function UnsetRequestFlag takes integer flag returns nothing
set requestFlag = BlzBitAnd(requestFlag, -flag - 1)
endfunction
public function RegisterEnterHandler takes code func returns nothing
call onEnterHandler.register(func)
endfunction
public function RegisterLeaveHandler takes code func returns nothing
call onLeaveHandler.register(func)
endfunction
// ================================================================
// ================================================================
//! runtextmacro DEFINE_LIST("", "StatList", "integer")
struct CStat extends array
implement AllocT
private static TableArray hashMap = 0
method operator amount takes nothing returns real
if (hashMap[3].integer[this] > 0) then
return 0.0
endif
return hashMap[0].real[this]
endmethod
method operator modType takes nothing returns integer
return hashMap[1].integer[this]
endmethod
private method operator activeCounter takes nothing returns integer
return hashMap[2].integer[this]
endmethod
private method operator zeroCounter takes nothing returns integer
return hashMap[3].integer[this]
endmethod
method operator owner takes nothing returns unit
return hashMap[4].unit[this]
endmethod
method operator listPtr takes nothing returns StatListItem
return hashMap[5].integer[this]
endmethod
method operator modClass takes nothing returns integer
return hashMap[6].integer[this]
endmethod
method operator amount= takes real value returns nothing
if (value == this.amount) then
return
endif
// If REQUEST_DIRECT_AMOUNT is flagged, that means
// the handlers that observe any changes will not
// be notified.
// Otherwise, if the modifier is suppressed via
// activate(false), any attempts at changing
// the value will not reflect on the target unit,
// but will still be honored.
if ((IsFlagRequested(REQUEST_DIRECT_AMOUNT)) or /*
*/ (hashMap[2].integer[this] <= 0)) then
set hashMap[0].real[this] = value
return
endif
call CUnitStatHandler.prepModifyHandler(hashMap[4].unit[this], hashMap[0].real[this], value, this)
set hashMap[0].real[this] = value
call CUnitStatHandler.callModifyHandler(hashMap[6].integer[this])
call CUnitStatHandler.releaseHandler()
endmethod
method operator modType= takes integer value returns nothing
if (not IsFlagRequested(REQUEST_MODIFY_ATTR)) then
return
endif
set hashMap[1].integer[this] = value
endmethod
method operator activeCounter= takes integer value returns nothing
if (not IsFlagRequested(REQUEST_MODIFY_ATTR)) then
return
endif
set hashMap[2].integer[this] = value
endmethod
method operator zeroCounter= takes integer value returns nothing
if (not IsFlagRequested(REQUEST_CHANGE_COUNTER)) then
return
endif
set hashMap[3].integer[this] = value
endmethod
method operator owner= takes unit value returns nothing
if (not IsFlagRequested(REQUEST_MODIFY_ATTR)) then
return
endif
set hashMap[4].unit[this] = value
endmethod
method operator listPtr= takes StatListItem value returns nothing
if (not IsFlagRequested(REQUEST_MODIFY_ATTR)) then
return
endif
set hashMap[5].integer[this] = value
endmethod
method operator modClass= takes integer whichClass returns nothing
if (not IsFlagRequested(REQUEST_MODIFY_ATTR)) then
return
endif
set hashMap[6].integer[this] = whichClass
endmethod
private method incActiveCounter takes nothing returns nothing
local integer value = hashMap[2].integer[this] + 1
set hashMap[2].integer[this] = value
if (value == 1) then
call CUnitStatHandler.prepActivationHandler(hashMap[4].unit[this], true, this)
call CUnitStatHandler.callActivationHandler(hashMap[6].integer[this])
call CUnitStatHandler.releaseHandler()
endif
endmethod
private method decActiveCounter takes nothing returns nothing
local integer value = hashMap[2].integer[this] - 1
set hashMap[2].integer[this] = value
// Call activation and deactivation handlers.
if (value == 0) then
call CUnitStatHandler.prepActivationHandler(hashMap[4].unit[this], false, this)
call CUnitStatHandler.callDeactivationHandler(hashMap[6].integer[this])
call CUnitStatHandler.releaseHandler()
endif
endmethod
method nullifiesProduct takes nothing returns boolean
return this.zeroCounter > 0
endmethod
method isActive takes nothing returns boolean
return this.activeCounter > 0
endmethod
method activate takes boolean flag returns nothing
if (flag) then
call this.incActiveCounter()
else
call this.decActiveCounter()
endif
endmethod
method nullify takes boolean flag returns nothing
if (not IsFlagRequested(REQUEST_CHANGE_COUNTER)) then
return
endif
if (flag) then
set hashMap[3].integer[this] = hashMap[3].integer[this] + 1
else
set hashMap[3].integer[this] = hashMap[3].integer[this] - 1
endif
endmethod
method destroy takes nothing returns nothing
if (not IsFlagRequested(REQUEST_DESTROY_STAT)) or /*
*/ (this.modType == 0) then
return
endif
call SetRequestFlag(REQUEST_MODIFY_ATTR)
if (this.activeCounter > 0) then
set this.activeCounter = 1
call this.decActiveCounter()
endif
// Clear out all mapped data.
call hashMap[6].integer.remove(this)
call hashMap[5].integer.remove(this)
call hashMap[4].unit.remove(this)
call hashMap[3].integer.remove(this)
call hashMap[2].integer.remove(this)
call hashMap[1].integer.remove(this)
call hashMap[0].real.remove(this)
call this.deallocate()
call UnsetRequestFlag(REQUEST_MODIFY_ATTR)
endmethod
static method create takes unit whichUnit, integer manifest returns thistype
local thistype this = thistype.allocate()
call SetRequestFlag(REQUEST_DIRECT_AMOUNT + REQUEST_MODIFY_ATTR)
if (manifest == STAT_ADD) then
set this.amount = 0
// else if (manifest == STAT_MULT)
else
set this.amount = 1
endif
set this.modType = manifest
set this.activeCounter = 1
set this.zeroCounter = 0
set this.owner = whichUnit
set this.modClass = 0
call UnsetRequestFlag(REQUEST_DIRECT_AMOUNT + REQUEST_MODIFY_ATTR)
return this
endmethod
private static method init takes nothing returns nothing
set hashMap = TableArray[7]
endmethod
implement Init
endstruct
// ================================================================
module CUnitStatFactory
private boolean registered
private StatList addList
private StatList multList
private StatList zeroList
private real pSum
private real pProduct
private real pBaseValue
static method apply takes unit whichUnit, real value, integer modType returns CStat
local CStat newStat = 0
local thistype this = thistype(GetUnitId(whichUnit))
if ((not this.registered) or (this == 0)) or /*
*/ ((modType != STAT_ADD) and (modType != STAT_MULT)) then
return CStat(0)
endif
set newStat = CStat.create(whichUnit, modType)
call CustomUnitStatFactory_SetRequestFlag(REQUEST_MODIFY_ATTR)
set newStat.modClass = thistype.typeid
call CustomUnitStatFactory_UnsetRequestFlag(REQUEST_MODIFY_ATTR)
set newStat.amount = value
return newStat
endmethod
static method getProduct takes unit whichUnit returns real
return thistype(GetUnitId(whichUnit)).pProduct
endmethod
static method getSum takes unit whichUnit returns real
return thistype(GetUnitId(whichUnit)).pSum
endmethod
static method getBaseValue takes unit whichUnit returns real
return thistype(GetUnitId(whichUnit)).pBaseValue
endmethod
static method setBaseValueEx takes unit whichUnit, real value, boolean silentUpdate returns nothing
local thistype this = thistype(GetUnitId(whichUnit))
set this.pBaseValue = value
if (not silentUpdate) then
call this.bonusCalc(whichUnit)
endif
endmethod
static method setBaseValue takes unit whichUnit, real value returns nothing
call thistype.setBaseValueEx(whichUnit, value, false)
endmethod
static method register takes unit whichUnit returns boolean
local thistype this = thistype(GetUnitId(whichUnit))
if ((this.registered) or (this == 0)) then
return false
endif
set this.registered = true
set this.pBaseValue = 0.0
set this.pSum = 0.0
set this.pProduct = 1.0
set this.addList = StatList.create()
set this.multList = StatList.create()
set this.zeroList = StatList.create()
// Perhaps the base value is overridden here, eh?
static if thistype.onRegister.exists then
call thistype.onRegister(whichUnit)
endif
return true
endmethod
// @stub
static if (not thistype.onApply.exists) then
method onApply takes unit whichUnit, real amount returns nothing
endmethod
endif
// @stub
static if not thistype.onBonusCalc.exists then
method onBonusCalc takes real base, real sum, real product, boolean zeroMultiple returns real
if (zeroMultiple) then
return sum
endif
return base*product + sum
endmethod
endif
private method bonusCalc takes unit whichUnit returns nothing
call this.onApply(whichUnit, this.onBonusCalc(this.pBaseValue, this.pSum, this.pProduct, this.zeroList.empty()))
endmethod
private static method onModify takes unit whichUnit, real pastValue, real curValue, CStat curStat returns nothing
local thistype this = thistype(GetUnitId(whichUnit))
if (not curStat.isActive()) then
return
endif
if (curStat.modType == STAT_ADD) then
call CustomUnitStatFactory_SetRequestFlag(REQUEST_MODIFY_ATTR)
if (curStat.listPtr == 0) then
set curStat.listPtr = this.addList.push(curStat).last
set this.pSum = this.pSum + curValue
else
set this.pSum = this.pSum - pastValue + curValue
endif
call CustomUnitStatFactory_UnsetRequestFlag(REQUEST_MODIFY_ATTR)
call this.bonusCalc(whichUnit)
return
endif
if (curValue == 0.0) and (pastValue != 0.0) then
call CustomUnitStatFactory_SetRequestFlag(REQUEST_CHANGE_COUNTER)
if (not curStat.nullifiesProduct()) then
call CustomUnitStatFactory_SetRequestFlag(REQUEST_MODIFY_ATTR)
call this.multList.erase(curStat.listPtr)
set curStat.listPtr = this.zeroList.push(curStat).last
call CustomUnitStatFactory_UnsetRequestFlag(REQUEST_MODIFY_ATTR)
set this.pProduct = this.pProduct / pastValue
call curStat.nullify(true)
endif
call CustomUnitStatFactory_UnsetRequestFlag(REQUEST_CHANGE_COUNTER)
elseif (pastValue == 0.0) and (curValue != 0.0) then
call CustomUnitStatFactory_SetRequestFlag(REQUEST_CHANGE_COUNTER)
if (curStat.nullifiesProduct()) then
call CustomUnitStatFactory_SetRequestFlag(REQUEST_MODIFY_ATTR)
call this.zeroList.erase(curStat.listPtr)
set curStat.listPtr = this.multList.push(curStat).last
call CustomUnitStatFactory_UnsetRequestFlag(REQUEST_MODIFY_ATTR)
set this.pProduct = this.pProduct * curValue
call curStat.nullify(false)
endif
call CustomUnitStatFactory_UnsetRequestFlag(REQUEST_CHANGE_COUNTER)
else
set this.pProduct = this.pProduct / pastValue * curValue
endif
call this.bonusCalc(whichUnit)
endmethod
private static method modify takes nothing returns nothing
call thistype.onModify(CustomUnitStatFactory_GetCurrentUnit(), /*
*/ CustomUnitStatFactory_GetPrevAmount(), /*
*/ CustomUnitStatFactory_GetCurrentAmount(), /*
*/ CustomUnitStatFactory_GetCurrentStat())
endmethod
private static method onActivate takes unit whichUnit, CStat curStat, thistype this returns nothing
call CustomUnitStatFactory_SetRequestFlag(CustomUnitStatFactory_REQUEST_MODIFY_ATTR)
if (curStat.modType == STAT_ADD) then
set curStat.listPtr = this.addList.push(curStat).last
set this.pSum = this.pSum + curStat.amount
else
if (curStat.nullifiesProduct()) then
set curStat.listPtr = this.zeroList.push(curStat).last
else
set curStat.listPtr = this.multList.push(curStat).last
set this.pProduct = this.pProduct * curStat.amount
endif
endif
call this.bonusCalc(whichUnit)
set curStat.listPtr = 0
call CustomUnitStatFactory_UnsetRequestFlag(CustomUnitStatFactory_REQUEST_MODIFY_ATTR)
endmethod
private static method onDeactivate takes unit whichUnit, CStat curStat, thistype this returns nothing
local StatListItem ptr = curStat.listPtr
call CustomUnitStatFactory_SetRequestFlag(CustomUnitStatFactory_REQUEST_MODIFY_ATTR)
if (curStat.modType == STAT_ADD) then
call this.addList.erase(curStat.listPtr)
set this.pSum = this.pSum - curStat.amount
else
// if curStat.zeroCounter > 0
if (curStat.nullifiesProduct()) then
call this.zeroList.erase(curStat.listPtr)
else
call this.multList.erase(curStat.listPtr)
set this.pProduct = this.pProduct / curStat.amount
endif
endif
call this.bonusCalc(whichUnit)
set curStat.listPtr = 0
call CustomUnitStatFactory_UnsetRequestFlag(CustomUnitStatFactory_REQUEST_MODIFY_ATTR)
endmethod
private static method activate takes nothing returns nothing
local unit whichUnit = CustomUnitStatFactory_GetCurrentUnit()
local CStat curStat = CustomUnitStatFactory_GetCurrentStat()
local boolean state = CustomUnitStatFactory_GetStatActivationState()
local thistype this = thistype(GetUnitId(whichUnit))
if (state) then
call thistype.onActivate(whichUnit, curStat, this)
else
call thistype.onDeactivate(whichUnit, curStat, this)
endif
set whichUnit = null
endmethod
private static method onUnitEnter takes nothing returns nothing
call thistype.register(GetIndexedUnit())
endmethod
private static method onUnitLeave takes nothing returns nothing
local unit whichUnit = GetIndexedUnit()
local thistype this = thistype(GetIndexedUnitId())
local CStat curStat
call CustomUnitStatFactory_SetRequestFlag(REQUEST_DESTROY_STAT)
// Empty all the nodes from each list
// ====================================================
loop
exitwhen this.addList.empty()
set curStat = CStat(this.addList.first.data)
call curStat.destroy()
endloop
loop
exitwhen this.multList.empty()
set curStat = CStat(this.multList.first.data)
call curStat.destroy()
endloop
loop
exitwhen this.zeroList.empty()
set curStat = CStat(this.zeroList.first.data)
call curStat.destroy()
endloop
// ====================================================
call CustomUnitStatFactory_UnsetRequestFlag(REQUEST_DESTROY_STAT)
call this.zeroList.destroy()
call this.multList.destroy()
call this.addList.destroy()
set this.registered = false
set this.pBaseValue = 0.0
set this.pSum = 0.0
set this.pProduct = 0.0
set this.addList = 0
set this.multList = 0
set this.zeroList = 0
static if thistype.onUnregister.exists then
call thistype.onUnregister(whichUnit)
endif
set whichUnit = null
endmethod
private static method onInit takes nothing returns nothing
call CustomUnitStatFactory_AssignModifuncToClass(thistype.typeid, function thistype.modify)
call CustomUnitStatFactory_AssignActifuncToClass(thistype.typeid, function thistype.activate)
call CustomUnitStatFactory_AssignDeactifuncToClass(thistype.typeid, function thistype.activate)
call CustomUnitStatFactory_RegisterEnterHandler(function thistype.onUnitEnter)
call CustomUnitStatFactory_RegisterLeaveHandler(function thistype.onUnitLeave)
endmethod
endmodule
endlibrary
library UnitAttackSpeedStat requires CustomUnitStatFactory
globals
private constant integer ABILITY_ID = '!000'
private constant abilityreallevelfield ABILITY_FIELD = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1
endglobals
struct AttackSpeedStat extends array
private static ability array abilHandle
private method onApply takes unit whichUnit, real amount returns nothing
call IncUnitAbilityLevel(whichUnit, ABILITY_ID)
call BlzSetAbilityRealLevelField(abilHandle[GetUnitId(whichUnit)], ABILITY_FIELD, /*
*/ 0, amount)
call DecUnitAbilityLevel(whichUnit, ABILITY_ID)
endmethod
static method onRegister takes unit whichUnit returns nothing
call UnitAddAbility(whichUnit, ABILITY_ID)
call UnitMakeAbilityPermanent(whichUnit, true, ABILITY_ID)
set abilHandle[GetUnitId(whichUnit)] = BlzGetUnitAbility(whichUnit, ABILITY_ID)
endmethod
implement CUnitStatFactory
endstruct
endlibrary
library UnitArmorStat requires CustomUnitStatFactory
globals
private constant integer ABILITY_ID = '!001'
private constant abilityintegerlevelfield ABILITY_FIELD = ABILITY_ILF_DEFENSE_BONUS_IDEF
endglobals
struct ArmorStat extends array
private static ability array abilHandle
private method onApply takes unit whichUnit, real amount returns nothing
call IncUnitAbilityLevel(whichUnit, ABILITY_ID)
if (amount < 0) then
set amount = amount - 0.5
else
set amount = amount + 0.5
endif
call BlzSetAbilityIntegerLevelField(abilHandle[GetUnitId(whichUnit)], ABILITY_FIELD, /*
*/ 0, R2I(amount))
call DecUnitAbilityLevel(whichUnit, ABILITY_ID)
endmethod
static method onRegister takes unit whichUnit returns nothing
call UnitAddAbility(whichUnit, ABILITY_ID)
call UnitMakeAbilityPermanent(whichUnit, true, ABILITY_ID)
set abilHandle[GetUnitId(whichUnit)] = BlzGetUnitAbility(whichUnit, ABILITY_ID)
endmethod
implement CUnitStatFactory
endstruct
endlibrary
library UnitPathCheck requires Init, WorldBounds
globals
private constant real DISP_THRESHOLD = 100.0
private constant real RECT_LENGTH = 32.0
private rect pathRect = null
private item pathChecker = null
private item array tempHiddenItems
private integer tempIndex = 0
private real pathX = 0.0
private real pathY = 0.0
endglobals
private struct UnitPathCheck extends array
static method hideItems takes nothing returns nothing
set tempIndex = tempIndex + 1
set tempHiddenItems[tempIndex] = GetEnumItem()
call SetItemVisible(tempHiddenItems[tempIndex], false)
endmethod
private static method init takes nothing returns nothing
set pathChecker = CreateItem('ratf', WorldBounds.minX, WorldBounds.minY)
set pathRect = Rect(-RECT_LENGTH, -RECT_LENGTH, RECT_LENGTH, RECT_LENGTH)
call SetItemVisible(pathChecker, false)
endmethod
implement Init
endstruct
function IsValidGroundPathing takes real cx, real cy returns boolean
local real dist = 0.0
// Hide all present items
call MoveRectTo(pathRect, cx, cy)
call EnumItemsInRect(pathRect, null, function UnitPathCheck.hideItems)
// Place item.
call SetItemVisible(pathChecker, true)
call SetItemPosition(pathChecker, cx, cy)
set pathX = GetItemX(pathChecker)
set pathY = GetItemY(pathChecker)
set dist = (cx - pathX)*(cx - pathX) + /*
*/ (cy - pathY)*(cy - pathY)
call SetItemPosition(pathChecker, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(pathChecker, false)
// Show all present items
loop
exitwhen tempIndex <= 0
call SetItemVisible(tempHiddenItems[tempIndex], true)
set tempIndex = tempIndex - 1
endloop
return dist < DISP_THRESHOLD
endfunction
function GetGroundPathX takes nothing returns real
return pathX
endfunction
function GetGroundPathY takes nothing returns real
return pathY
endfunction
endlibrary
library ObjectMovement requires /*
-----------------------------------
*/ Table, Alloc, Init, ListT, /*
-----------------------------------
--------------------------------------
*/ GTimer, UnitDex, BezierEasing /*
--------------------------------------
----------------------
*/ UnitPathCheck /*
----------------------
---------------------------------------------------------------------------
|
| ObjectMovement
| - MyPad
|
|---------------------------------------------------------------------------
|
| A library that facilitates triggered object movement
| without the strict coupling of one's position to
| its' in-game position. Since this system was designed
| for one thing, there isn't a lot of features that the
| current version (v.1.0.0.0) offers out of the box.
|
|---------------------------------------------------------------------------
|
| API:
|
| class ObjectMovement {
| ========================================================================
| ---------------
| | Methods: |
| ---------------
| static method create(unit whichUnit, ObjectMovementResponse resp)
| - The ObjectMovementResponse instance will be explained below.
|
| method destroy()
| - Destroys an instance.
| - If called while the instance was still moving, a
| STOP response will be triggered.
| - Fires a DESTROY response.
|
| method setTargetArea(real x, y, z) -> ObjectMovement
| method setTargetAreaXY(real x, y) -> ObjectMovement
| - Sets the target area to the specified coordinates.
| - setTargetAreaXY internally calls setTargetAreaXYZ with 0.0
| as the third parameter.
| - If used while moving, the movement might become janky.
| - Returns itself for chained calls.
|
| method setTargetUnit(unit target) -> ObjectMovement
| - Sets the target to the specified unit.
| method setTargetDest(destructable target) -> ObjectMovement
| - Sets the target to the specified destructable.
| method setTargetItem(item target) -> ObjectMovement
| - Sets the target to the specified item.
| - Returns itself for chained calls.
|
| method launch() -> bool
| - Launches an instance if and only if
| both the velocity and the target type have
| been defined.
| - Returns true if it launches the instance,
| false otherwise.
| - Fires a RESUME response if instance was
| paused.
| - Otherwise fires a LAUNCH response.
|
| method stop() -> bool
| - Stops an instance dead in its tracks.
| - If the ObjectMovementResponse reference the
| instance is based off of has ticked the
| FLAG_DESTROY_ON_STOP, then the instance is
| destroyed afterwards.
| - If FLAG_NO_TARGET_ON_STOP is ticked, the
| movement instance will behave as though
| it has never been launched when stopped.
| - Fires a STOP response.
|
| method pause() -> bool
| - Stops the movement of an instance.
| - Fires a PAUSE instance.
|
| ---------------
| | Members: |
| ---------------
| readonly static ObjectMovement current
| - The current instance inside a response execution.
| readonly unit unit
| - The unit being moved.
| readonly item item
| - The item being moved.
| readonly effect effect
| - The generated special effect being moved.
| readonly integer moveStatus
| - Holds numerous flags about the status of the movement instance.
| readonly integer instanceFlags
| - Holds numerous flags about the behavior of the movement instance.
| readonly integer targType
| - Holds numerous flags about the target type of the movement instance.
| readonly unit targUnit
| readonly item targItem
| readonly destructable targDest
| - Should be self-explanatory (These are target objects).
|
| integer data
| - Should be self-explanatory (This is user-specified data).
| BezierEase easeMode
| - The Bezier curve which governs how the instance will move.
| real veloc
| - The speed of the Movement instance.
| - Must be defined by the user as a requirement for launching.
| ========================================================================
| }
|
| Ideally, the ObjectMovementResponse object is generated once
| per class / struct, but if one wants to create it directly,
| the API is provided below.
|
| WARNING: These objects cannot be destroyed as it is presumed
| that these are static.
|
| class ObjectMovementResponse {
| ========================================================================
| ---------------
| | Methods: |
| ---------------
| static method create(code onLaunch, onMove, onPause, onResume, onStop, onDest) -> ObjectMovementResponse
| - Returns a unique ObjectMovementResponse object.
| - Note that the responses can still be altered after definition.
|
| ---------------
| | Members: |
| ---------------
| integer flag
| - Dictates how a ObjectMovement instance will behave when
| using this object as a response handler.
| readonly EventResponder launchResponse
| readonly EventResponder moveResponse
| readonly EventResponder pauseResponse
| readonly EventResponder resumeResponse
| readonly EventResponder stopResponse
| readonly EventResponder destroyResponse
| - Objects which execute a certain function depending
| on the situation.
| - Can be made to call different functions using
| <resp>.change(code callback)
| ========================================================================
| }
|
| Now, if one doesn't want to worry about having to create the static
| object beforehand, populating its responses. they can just import
| the module below.
|
| module ObjectMovementTemplate {
| ========================================================================
| interface static method onLaunch()
| interface static method onMove()
| interface static method onStop()
| interface static method onResume()
| interface static method onPause()
| interface static method onDest()
| - These methods can be optionally defined.
|
| static method applyUnitMovement(unit whichUnit)
| - A shortcut for creating a ObjectMovement instance
| with its' responses associated with the parent
| class, and coupling the movement of the object
| to a unit.
|
| static method applyMovement(real cx, real cy)
| - A shortcut for creating a ObjectMovement instance
| with its' responses associated with the parent
| class, and coupling the movement of the object
| to a generated effect.
|
| static method applyItemMovement(item whichItem)
| - A shortcut for creating a ObjectMovement instance
| with its' responses associated with the parent
| class, and coupling the movement of the object
| to an item.
|
| ---------------
| | Members: |
| ---------------
| - static ObjectMovementResponse resp
| - This is your generated ObjectMovementResponse object.
| It is defined at map initialization.
|
| - stub static integer FLAGSET
| - This is a generated method operator if not
| specified directly beforehand.
| - Has an initial value of FLAG_NO_TARGET_ON_STOP +
| FLAG_DESTROY_ON_OBJECT_DEATH + FLAG_IGNORE_GROUND_PATHING
|
| - stub static string MISSILE_MODEL
| - This is another generated method operator if
| not specified directly beforehand.
| - Determines the model of the generated effect
| associated with the class.
|
| ========================================================================
| }
---------------------------------------------------------------------------
*/
// ============================================================================
// Ideally, we want to keep this as small as possible
// while still allowing enough time to pass to lessen
// the burden of distance recomputation.
private constant function TICKS_FOR_CALC_DISTANCE takes nothing returns integer
return 6
endfunction
// Not sure if doing it this way is less computationally expensive.
private function GetDistanceXY takes real x1, real y1, real x2, real y2 returns real
set x1 = (x1 - x2)
set y1 = (y1 - y2)
return SquareRoot(x1*x1 + y1*y1)
endfunction
private function GetDistanceXYZ takes real x1, real y1, real z1, real x2, real y2, real z2 returns real
set x1 = (x1 - x2)
set y1 = (y1 - y2)
set z1 = (z1 - z2)
return SquareRoot(x1*x1 + y1*y1 + z1*z1)
endfunction
private function GetSquareDistanceXY takes real x1, real y1, real x2, real y2 returns real
set x1 = (x1 - x2)
set y1 = (y1 - y2)
return (x1*x1 + y1*y1)
endfunction
private function GetSquareDistanceXYZ takes real x1, real y1, real z1, real x2, real y2, real z2 returns real
set x1 = (x1 - x2)
set y1 = (y1 - y2)
set z1 = (z1 - z2)
return (x1*x1 + y1*y1 + z1*z1)
endfunction
// factor is a value in the interval [0, 1]
// When factor is 0, the value returned is a.
// When factor is 1, the value returned is b.
private function GetValueInRange takes real a, real b, real factor returns real
return a*(1 - factor) + b*factor
endfunction
private function GetBezierGradient takes BezierEasing ease, real cur, real prev returns real
return ease[cur] - ease[prev]
endfunction
// ============================================================================
struct ObjectMovementResponse extends array
implement Alloc
readonly EventResponder launchResponse
readonly EventResponder moveResponse
readonly EventResponder pauseResponse
readonly EventResponder resumeResponse
readonly EventResponder stopResponse
readonly EventResponder destroyResponse
integer flag
static method create takes code onLaunch, code onMove, code onPause, code onResume, code onStop, code onDest returns thistype
local thistype this = thistype.allocate()
set this.launchResponse = EventResponder.create(onLaunch)
set this.moveResponse = EventResponder.create(onMove)
set this.pauseResponse = EventResponder.create(onPause)
set this.resumeResponse = EventResponder.create(onResume)
set this.stopResponse = EventResponder.create(onStop)
set this.destroyResponse = EventResponder.create(onDest)
return this
endmethod
endstruct
// ============================================================================
//! runtextmacro DEFINE_LIST("private", "MovementList", "integer")
struct ObjectCoords extends array
real cx
real cy
real cz
real tx
real ty
real tz
real tzOff
real ox
real oy
real oz
real sourceDist
real objectDist
real curBezierValue
real bezierSkipValue
real angle
// Since the allocation scheme is based on parent inheritance,
// this method is ubiquitous with cleanup instead.
method destroy takes nothing returns nothing
set this.cx = 0.0
set this.cy = 0.0
set this.cz = 0.0
set this.ox = 0.0
set this.oy = 0.0
set this.oz = 0.0
set this.tx = 0.0
set this.ty = 0.0
set this.tz = 0.0
set this.tzOff = 0.0
set this.sourceDist = 0.0
set this.objectDist = 0.0
set this.curBezierValue = 0.0
set this.bezierSkipValue = 0.0
set this.angle = 0.0
endmethod
private static location zLoc = null
static method getZ takes real x, real y returns real
call MoveLocation(zLoc, x, y)
return GetLocationZ(zLoc)
endmethod
private static method init takes nothing returns nothing
set zLoc = Location(0.0, 0.0)
endmethod
implement Init
endstruct
private struct ObjectMovementData extends array
static EventResponder movementResp = 0
static MovementList moveList = 0
static MovementList array instanceList
static method addMovingInstance takes ObjectMovement instance returns MovementListItem
local MovementListItem result = moveList.push(instance).last
// Add unit in movingGroup
if (moveList.size() == 1) then
call GTimer[GAME_TICK].requestCallback(movementResp)
endif
return result
endmethod
static method removeMovingInstance takes MovementListItem ptr returns nothing
call moveList.erase(ptr)
if (moveList.empty()) then
call GTimer[GAME_TICK].releaseCallback(movementResp)
endif
endmethod
static method addUnitInstance takes unit whichUnit, ObjectMovement instance returns MovementListItem
local integer id = GetUnitId(whichUnit)
local MovementListItem result = 0
if (instanceList[id] == 0) then
set instanceList[id] = MovementList.create()
endif
set result = instanceList[id].push(instance).last
return result
endmethod
static method removeUnitInstance takes unit whichUnit, MovementListItem ptr returns nothing
local integer id = GetUnitId(whichUnit)
if (instanceList[id] == 0) then
return
endif
call instanceList[id].erase(ptr)
if (instanceList[id].empty()) then
call instanceList[id].destroy()
set instanceList[id] = 0
endif
endmethod
endstruct
// ============================================================================
struct ObjectMovement extends array
implement Alloc
// ==================================================
private static constant real INTERVAL = 1.0 / I2R(GAME_TICK)
private static constant integer STATUS_ALLOCATED = 1
private static constant integer STATUS_UNUSED = 2
private static constant integer STATUS_MOVING = 4
private static constant integer STATUS_PAUSED = 8
private static constant integer STATUS_STOPPED = 16
private static constant integer STATUS_BEING_REMOVED = 32
private static constant integer STATUS_READDED = 64
private static constant integer TARGET_TYPE_STATIC = 1
private static constant integer TARGET_TYPE_MOVING = 2
private static constant integer TARGET_TYPE_UNIT = 4
private static constant integer TARGET_TYPE_DESTRUCTABLE = 8
private static constant integer TARGET_TYPE_ITEM = 12
private static constant integer TARGET_TYPE_MASK = TARGET_TYPE_UNIT + TARGET_TYPE_DESTRUCTABLE
readonly static constant integer OBJECT_TYPE_UNIT = 1
readonly static constant integer OBJECT_TYPE_ITEM = 2
readonly static constant integer OBJECT_TYPE_EFFECT = 4
private static constant integer EVENT_LAUNCHED = 1
private static constant integer EVENT_MOVING = 2
private static constant integer EVENT_PAUSED = 4
private static constant integer EVENT_RESUMED = 8
private static constant integer EVENT_STOPPED = 16
private static constant integer EVENT_DESTROYED = 32
static constant integer FLAG_DESTROY_ON_STOP = 1
static constant integer FLAG_DESTROY_ON_TARGET_REMOVE = 2
static constant integer FLAG_DESTROY_ON_TARGET_DEATH = 4
static constant integer FLAG_DESTROY_ON_OBJECT_DEATH = 8
static constant integer FLAG_STOP_ON_UNIT_ROOT = 16
static constant integer FLAG_NO_TARGET_ON_STOP = 32
static constant integer FLAG_IGNORE_GROUND_PATHING = 64
private static boolean inCallback = false
private static integer loopIterCount = 0
readonly static thistype current = 0
readonly static BezierEase linear = 0
readonly integer moveStatus
readonly integer instanceFlags
// The object type to be moved.
readonly integer objectType
readonly unit unit
readonly effect effect
readonly destructable destructable
readonly item item
readonly integer targType
readonly unit targUnit
readonly item targItem
readonly destructable targDest
integer data
BezierEase easeMode
private ObjectMovementResponse respHandler
private real pVeloc
private MovementListItem moveListPtr
private MovementListItem instanceListPtr
method operator coords takes nothing returns ObjectCoords
return ObjectCoords(this)
endmethod
method operator veloc takes nothing returns real
return this.pVeloc * GAME_TICK
endmethod
method operator veloc= takes real value returns nothing
local real srcDist = 0.0
local real objDist = 0.0
set this.moveStatus = BlzBitAnd(this.moveStatus, -STATUS_UNUSED - 1)
set this.pVeloc = value / I2R(GAME_TICK)
if (this.targType == 0) then
return
endif
set srcDist = GetDistanceXY(this.coords.cx, this.coords.cy, /*
*/ this.coords.tx, this.coords.ty)
set objDist = GetDistanceXY(this.coords.cx, this.coords.cy, /*
*/ this.coords.ox, this.coords.oy)
set this.coords.sourceDist = srcDist
set this.coords.objectDist = objDist
set srcDist = 1.0 / srcDist
set this.coords.curBezierValue = objDist * srcDist
set this.coords.bezierSkipValue = this.pVeloc * srcDist
endmethod
// ==================================================
private method invokeCallback takes integer eventType returns nothing
local thistype prev = current
set current = this
if (eventType == EVENT_LAUNCHED) then
call this.respHandler.launchResponse.run()
elseif (eventType == EVENT_MOVING) then
call this.respHandler.moveResponse.run()
elseif (eventType == EVENT_PAUSED) then
call this.respHandler.pauseResponse.run()
elseif (eventType == EVENT_RESUMED) then
call this.respHandler.resumeResponse.run()
elseif (eventType == EVENT_STOPPED) then
call this.respHandler.stopResponse.run()
elseif (eventType == EVENT_DESTROYED) then
call this.respHandler.destroyResponse.run()
endif
set current = prev
endmethod
private method onLaunchPopData takes nothing returns nothing
if (this.objectType == OBJECT_TYPE_UNIT) then
set this.coords.cx = GetUnitX(this.unit)
set this.coords.cy = GetUnitY(this.unit)
set this.coords.cz = GetUnitFlyHeight(this.unit)
elseif (this.objectType == OBJECT_TYPE_ITEM) then
set this.coords.cx = GetItemX(this.item)
set this.coords.cy = GetItemY(this.item)
set this.coords.cz = 0.0
set this.coords.tz = 0.0 // It's senseless trying to move an item in the z-axis.
else
set this.coords.cx = BlzGetLocalSpecialEffectX(this.effect)
set this.coords.cy = BlzGetLocalSpecialEffectY(this.effect)
set this.coords.cz = BlzGetLocalSpecialEffectZ(this.effect)
endif
set this.coords.ox = this.coords.cx
set this.coords.oy = this.coords.cy
set this.coords.oz = this.coords.cz
set this.coords.angle = Atan2(this.coords.ty - this.coords.cy, /*
*/ this.coords.tx - this.coords.cx)
set this.coords.sourceDist = GetDistanceXY(this.coords.cx, this.coords.cy, /*
*/ this.coords.tx, this.coords.ty)
set this.coords.objectDist = 0.0
set this.coords.curBezierValue = 0.0
if (this.coords.sourceDist == 0.0) then
set this.coords.bezierSkipValue = 1.0
else
set this.coords.bezierSkipValue = RMinBJ(this.pVeloc / this.coords.sourceDist, 1.0)
endif
endmethod
method launch takes nothing returns boolean
// Check if instance is already moving.
if (BlzBitAnd(this.moveStatus, STATUS_MOVING) != 0) or /*
*/ (BlzBitAnd(this.moveStatus, STATUS_ALLOCATED) == 0) then
return false
endif
// Check if missile is ready to launch.
if (BlzBitAnd(this.moveStatus, STATUS_UNUSED) != 0) then
// call BJDebugMsg("ObjectMovement.launch >> Movement instance is not ready to launch yet!")
// call BJDebugMsg("ObjectMovement.launch >> Please define the instance's velocity.")
return false
endif
if (this.targType == 0) then
// call BJDebugMsg("ObjectMovement.launch >> Movement instance is not ready to launch yet!")
// call BJDebugMsg("ObjectMovement.launch >> Please define the instance's target type.")
return false
endif
// Missile is ready to launch.
set this.moveStatus = BlzBitOr(this.moveStatus, STATUS_MOVING)
call this.onLaunchPopData()
if (BlzBitAnd(this.moveStatus, STATUS_PAUSED) != 0) then
call this.invokeCallback(EVENT_RESUMED)
else
call this.invokeCallback(EVENT_LAUNCHED)
endif
set this.moveStatus = BlzBitAnd(this.moveStatus, -(STATUS_STOPPED + STATUS_PAUSED) - 1)
set this.moveListPtr = ObjectMovementData.addMovingInstance(this)
if (inCallback) then
set this.moveStatus = BlzBitOr(this.moveStatus, STATUS_READDED)
endif
return true
endmethod
method pause takes nothing returns boolean
if (BlzBitAnd(this.moveStatus, STATUS_PAUSED) != 0) then
return false
endif
set this.moveStatus = BlzBitAnd(this.moveStatus, -STATUS_MOVING - 1)
set this.moveStatus = BlzBitOr(this.moveStatus, STATUS_PAUSED)
call ObjectMovementData.removeMovingInstance(this.moveListPtr)
set this.moveListPtr = 0
call this.invokeCallback(EVENT_PAUSED)
return true
endmethod
method resume takes nothing returns boolean
if (BlzBitAnd(this.moveStatus, STATUS_PAUSED) == 0) then
return false
endif
return this.launch()
endmethod
method destroy takes nothing returns nothing
if (BlzBitAnd(this.moveStatus, STATUS_ALLOCATED) == 0) then
return
endif
set this.moveStatus = BlzBitAnd(this.moveStatus, -STATUS_ALLOCATED - 1)
// If the instance is moving, stop it
if (BlzBitAnd(this.moveStatus, STATUS_MOVING) != 0) then
call this.invokeCallback(EVENT_STOPPED)
call ObjectMovementData.removeMovingInstance(this.moveListPtr)
set this.moveListPtr = 0
endif
if (BlzBitAnd(this.moveStatus, STATUS_BEING_REMOVED) == 0) then
set this.moveStatus = this.moveStatus + STATUS_BEING_REMOVED
call this.invokeCallback(EVENT_DESTROYED)
set this.moveStatus = this.moveStatus - STATUS_BEING_REMOVED
endif
if (this.objectType == OBJECT_TYPE_EFFECT) then
call DestroyEffect(this.effect)
elseif (this.instanceListPtr != 0) then
call ObjectMovementData.removeUnitInstance(this.unit, this.instanceListPtr)
set this.instanceListPtr = 0
endif
call this.coords.destroy()
set this.unit = null
set this.item = null
set this.effect = null
set this.objectType = 0
set this.targItem = null
set this.targDest = null
set this.targUnit = null
set this.easeMode = 0
set this.moveStatus = 0
set this.targType = 0
set this.instanceFlags = 0
set this.pVeloc = 0.0
set this.respHandler = 0
call this.deallocate()
endmethod
method stop takes nothing returns boolean
if (BlzBitAnd(this.moveStatus, STATUS_STOPPED) != 0) or /*
*/ (BlzBitAnd(this.moveStatus, STATUS_ALLOCATED) == 0) then
return false
endif
set this.moveStatus = BlzBitAnd(this.moveStatus, -STATUS_MOVING - 1)
set this.moveStatus = BlzBitOr(this.moveStatus, STATUS_STOPPED)
if (BlzBitAnd(this.instanceFlags, FLAG_NO_TARGET_ON_STOP + FLAG_DESTROY_ON_STOP) != 0) then
set this.moveStatus = BlzBitOr(this.moveStatus, STATUS_UNUSED)
set this.targType = 0
set this.targUnit = null
set this.targDest = null
set this.targItem = null
set this.pVeloc = 0.0
set this.targType = 0
endif
call ObjectMovementData.removeMovingInstance(this.moveListPtr)
set this.moveListPtr = 0
call this.invokeCallback(EVENT_STOPPED)
if ((BlzBitAnd(this.instanceFlags, FLAG_DESTROY_ON_STOP) != 0) and /*
*/ (BlzBitAnd(this.moveStatus, STATUS_UNUSED) != 0)) then
call this.destroy()
endif
return true
endmethod
method setTargetArea takes real x, real y, real z returns thistype
set this.targType = TARGET_TYPE_STATIC
set this.coords.tx = x
set this.coords.ty = y
set this.coords.tz = z
set this.targUnit = null
set this.targDest = null
set this.targItem = null
if (BlzBitAnd(this.objectType, OBJECT_TYPE_EFFECT) != 0) then
set this.coords.tz = this.coords.tz + ObjectCoords.getZ(x, y)
endif
// Whenever target is repointed while missile isn't moving, force a recalculation.
// Hopefully, this doesn't need to be done too often.
if (BlzBitAnd(this.moveStatus, STATUS_MOVING) == 0) then
return this
endif
call this.onLaunchPopData()
return this
endmethod
method setTargetAreaXY takes real x, real y returns thistype
return this.setTargetArea(x, y, 0.0 + GetUnitFlyHeight(this.unit))
endmethod
method setTargetUnitOffset takes unit target, real offset returns thistype
set this.targType = TARGET_TYPE_MOVING + TARGET_TYPE_UNIT
set this.targUnit = target
set this.targDest = null
set this.targItem = null
set this.coords.tx = GetUnitX(target)
set this.coords.ty = GetUnitY(target)
set this.coords.tz = GetUnitFlyHeight(target)
set this.coords.tzOff = offset
return this
endmethod
method setTargetUnit takes unit target returns thistype
return this.setTargetUnitOffset(target, 0.0)
endmethod
method setTargetDest takes destructable target returns thistype
set this.targType = TARGET_TYPE_MOVING + TARGET_TYPE_DESTRUCTABLE
set this.targDest = target
set this.targItem = null
set this.targUnit = null
set this.coords.tx = GetDestructableX(target)
set this.coords.ty = GetDestructableY(target)
set this.coords.tz = 0.0
return this
endmethod
method setTargetItem takes item target returns thistype
set this.targType = TARGET_TYPE_MOVING + TARGET_TYPE_ITEM
set this.targItem = target
set this.targDest = null
set this.targUnit = null
set this.coords.tx = GetItemX(target)
set this.coords.ty = GetItemY(target)
set this.coords.tz = 0.0
return this
endmethod
static method create takes string model, real cx, real cy, ObjectMovementResponse resp returns thistype
local thistype this = thistype.allocate()
set this.effect = AddSpecialEffect(model, cx, cy)
set this.easeMode = thistype.linear
set this.moveStatus = STATUS_ALLOCATED + STATUS_UNUSED + STATUS_STOPPED
set this.targType = 0
set this.instanceFlags = resp.flag
set this.pVeloc = 0.0
set this.respHandler = resp
set this.objectType = OBJECT_TYPE_EFFECT
return this
endmethod
static method createForUnit takes unit whichUnit, ObjectMovementResponse resp returns thistype
local thistype this = thistype.allocate()
set this.unit = whichUnit
set this.easeMode = thistype.linear
set this.moveStatus = STATUS_ALLOCATED + STATUS_UNUSED + STATUS_STOPPED
set this.targType = 0
set this.instanceFlags = resp.flag
set this.pVeloc = 0.0
set this.respHandler = resp
set this.instanceListPtr = ObjectMovementData.addUnitInstance(whichUnit, this)
set this.objectType = OBJECT_TYPE_UNIT
return this
endmethod
static method createForItem takes item whichItem, ObjectMovementResponse resp returns thistype
local thistype this = thistype.allocate()
set this.item = whichItem
set this.easeMode = thistype.linear
set this.moveStatus = STATUS_ALLOCATED + STATUS_UNUSED + STATUS_STOPPED
set this.targType = 0
set this.instanceFlags = resp.flag
set this.pVeloc = 0.0
set this.respHandler = resp
set this.objectType = OBJECT_TYPE_ITEM
return this
endmethod
// ==================================================
// Find a way to compute for distance as computationally
// non-taxing as possible.
private static real dx = 0.0
private static real dy = 0.0
private static real dz = 0.0
private static real normalFactor = 0.0
private static real gradFactor = 0.0
private static real srcDist2 = 0.0
private static real objDist2 = 0.0
private static real lastBezier = 0.0
private static unit tempUnit = null
//! textmacro_once UNIT_MOVEMENT_PROCESS_TARG_STATE takes TYPE, TYPEFUNC, TARGETTYPE, COND
if (Get$TYPEFUNC$TypeId(this.targ$TYPE$) == 0) then
if (BlzBitAnd(this.instanceFlags, FLAG_DESTROY_ON_TARGET_REMOVE) != 0) then
call this.destroy()
else
set this.targType = BlzBitAnd(this.targType, -($TARGETTYPE$) - 1)
set this.targType = BlzBitOr(this.targType, TARGET_TYPE_STATIC)
endif
elseif ($COND$) then
if (BlzBitAnd(this.instanceFlags, FLAG_DESTROY_ON_TARGET_DEATH) != 0) then
call this.destroy()
else
set this.targType = BlzBitAnd(this.targType, -($TARGETTYPE$) - 1)
set this.targType = BlzBitOr(this.targType, TARGET_TYPE_STATIC)
endif
endif
//! endtextmacro
private method processTargState takes nothing returns nothing
// Turns out, I forgot to mask the bits I wanted to inspect in
// the first place, leading to a dangerous situation where the
// null item would cause the targeting scheme to become static
local integer mask = BlzBitAnd(this.targType, TARGET_TYPE_MASK)
if (mask == TARGET_TYPE_UNIT) then
//! runtextmacro UNIT_MOVEMENT_PROCESS_TARG_STATE("Unit", "Unit", "TARGET_TYPE_UNIT", "not UnitAlive(this.targUnit)")
endif
if (mask == TARGET_TYPE_DESTRUCTABLE) then
//! runtextmacro UNIT_MOVEMENT_PROCESS_TARG_STATE("Dest", "Destructable", "TARGET_TYPE_DESTRUCTABLE", "GetWidgetLife(this.targDest) <= 0.0")
endif
if (mask == TARGET_TYPE_ITEM) then
//! runtextmacro UNIT_MOVEMENT_PROCESS_TARG_STATE("Item", "Item", "TARGET_TYPE_ITEM", "GetWidgetLife(this.targItem) <= 0.0")
endif
endmethod
private method processTargMovement takes nothing returns nothing
// Target unit
local integer mask = BlzBitAnd(this.targType, TARGET_TYPE_MASK)
if (mask == TARGET_TYPE_UNIT) then
set this.coords.tx = GetUnitX(this.targUnit)
set this.coords.ty = GetUnitY(this.targUnit)
set this.coords.tz = GetUnitFlyHeight(this.targUnit) + this.coords.tzOff
endif
if (mask == TARGET_TYPE_DESTRUCTABLE) then
set this.coords.tx = GetDestructableX(this.targDest)
set this.coords.ty = GetDestructableY(this.targDest)
set this.coords.tz = 0.0
endif
if (mask == TARGET_TYPE_ITEM) then
set this.coords.tx = GetItemX(this.targItem)
set this.coords.ty = GetItemY(this.targItem)
set this.coords.tz = 0.0
endif
if (BlzBitAnd(this.objectType, OBJECT_TYPE_EFFECT) != 0) then
set this.coords.tz = this.coords.tz + ObjectCoords.getZ(this.coords.tx, this.coords.ty)
endif
set this.coords.angle = Atan2(this.coords.ty - this.coords.oy, this.coords.tx - this.coords.ox)
endmethod
private method processMovement takes boolean computeDist, integer id, MovementListItem iter returns MovementListItem
local real temp
set iter = iter.next
if (BlzBitAnd(this.moveStatus, STATUS_READDED) != 0) then
return iter
endif
if (BlzBitAnd(this.instanceFlags, FLAG_DESTROY_ON_OBJECT_DEATH) != 0) then
if ((this.objectType == OBJECT_TYPE_UNIT) and /*
*/ (not UnitAlive(this.unit))) or /*
*/ ((this.objectType == OBJECT_TYPE_ITEM) and /*
*/ (GetWidgetLife(this.item) < 0.405)) then
call this.destroy()
return iter
endif
endif
if (this.objectType == OBJECT_TYPE_UNIT) and /*
*/ (BlzBitAnd(this.instanceFlags, FLAG_STOP_ON_UNIT_ROOT) != 0) and /*
*/ (IsUnitType(this.unit, UNIT_TYPE_SNARED)) then
call this.stop()
return iter
endif
if (BlzBitAnd(this.targType, TARGET_TYPE_STATIC) != 0) then
if (this.coords.objectDist + this.pVeloc > this.coords.sourceDist) then
// The object has reached its' destination
set gradFactor = GetBezierGradient(this.easeMode, 1.0, this.coords.curBezierValue)
set normalFactor = this.coords.sourceDist * gradFactor
set this.coords.objectDist = this.coords.sourceDist
set dx = normalFactor * Cos(this.coords.angle)
set dy = normalFactor * Sin(this.coords.angle)
set dz = this.coords.tz - this.coords.oz
set this.coords.ox = this.coords.tx
set this.coords.oy = this.coords.ty
set this.coords.oz = this.coords.tz
// Move unit.
call this.stop()
else
// The object is still moving
set lastBezier = this.coords.curBezierValue
set this.coords.curBezierValue = lastBezier + this.coords.bezierSkipValue
set gradFactor = GetBezierGradient(this.easeMode, this.coords.curBezierValue, lastBezier)
set normalFactor = this.coords.sourceDist * gradFactor
set this.coords.objectDist = this.coords.objectDist + this.pVeloc
set dx = normalFactor * Cos(this.coords.angle)
set dy = normalFactor * Sin(this.coords.angle)
set dz = (this.coords.tz - this.coords.cz)*(gradFactor)
set this.coords.ox = this.coords.ox + this.pVeloc * Cos(this.coords.angle)
set this.coords.oy = this.coords.oy + this.pVeloc * Sin(this.coords.angle)
set this.coords.oz = this.coords.oz + (this.coords.tz - this.coords.oz) * (this.pVeloc / this.coords.sourceDist)
// Move unit.
call this.invokeCallback(EVENT_MOVING)
endif
// Check if instance is already cleaned up.
if ((BlzBitAnd(this.moveStatus, STATUS_ALLOCATED) == 0) and /*
*/ (ObjectMovementData.instanceList[id] == 0)) then
// Instance was removed.
return 0
endif
return iter
endif
// Check each target type either for death or removal.
call this.processTargState()
if (BlzBitAnd(this.moveStatus, STATUS_ALLOCATED) == 0) then
return iter
// Check if target type has been changed. If so, call function
// again.
elseif (BlzBitAnd(this.targType, TARGET_TYPE_STATIC) != 0) then
return this.processMovement(computeDist, id, iter.prev)
endif
call this.processTargMovement()
// Location is tagged. Calculate the distance when prompted
if (computeDist) then
set srcDist2 = GetDistanceXY(this.coords.cx, this.coords.cy, /*
*/ this.coords.tx, this.coords.ty)
set objDist2 = GetDistanceXY(this.coords.cx, this.coords.cy, /*
*/ this.coords.ox, this.coords.oy)
if (objDist2 > srcDist2) then
set temp = srcDist2
set srcDist2 = objDist2
set objDist2 = temp
endif
set temp = 1.0 / srcDist2
set this.coords.sourceDist = srcDist2
set this.coords.objectDist = objDist2
set lastBezier = this.coords.curBezierValue
set this.coords.curBezierValue = objDist2 * temp
set this.coords.bezierSkipValue = this.pVeloc * temp
set srcDist2 = srcDist2 * srcDist2
set objDist2 = objDist2 * objDist2
else
set lastBezier = this.coords.curBezierValue
set this.coords.curBezierValue = lastBezier + this.coords.bezierSkipValue
set this.coords.objectDist = this.coords.objectDist + this.pVeloc
set srcDist2 = this.coords.sourceDist * this.coords.sourceDist
set objDist2 = this.coords.objectDist * this.coords.objectDist
endif
// Compare square distance
if (objDist2 >= srcDist2) then
// The object has reached its' destination
set gradFactor = GetBezierGradient(this.easeMode, 1.0, this.coords.curBezierValue)
set normalFactor = this.coords.sourceDist * gradFactor
set this.coords.objectDist = this.coords.sourceDist
set dx = normalFactor * Cos(this.coords.angle)
set dy = normalFactor * Sin(this.coords.angle)
set dz = this.coords.tz - this.coords.oz
set this.coords.ox = this.coords.tx
set this.coords.oy = this.coords.ty
set this.coords.oz = this.coords.tz
// Move unit.
call this.stop()
else
set gradFactor = GetBezierGradient(this.easeMode, this.coords.curBezierValue, lastBezier)
set normalFactor = this.coords.sourceDist * gradFactor
set this.coords.objectDist = this.coords.objectDist + this.pVeloc
set dx = normalFactor * Cos(this.coords.angle)
set dy = normalFactor * Sin(this.coords.angle)
set dz = (this.coords.tz - this.coords.cz)*(gradFactor)
set this.coords.ox = this.coords.ox + this.pVeloc * Cos(this.coords.angle)
set this.coords.oy = this.coords.oy + this.pVeloc * Sin(this.coords.angle)
set this.coords.oz = this.coords.oz + (this.coords.tz - this.coords.oz) * (this.pVeloc / this.coords.sourceDist)
// Move unit.
call this.invokeCallback(EVENT_MOVING)
endif
// Check if instance is already cleaned up.
if ((BlzBitAnd(this.moveStatus, STATUS_ALLOCATED) == 0) and /*
*/ (ObjectMovementData.instanceList[id] == 0)) then
// Instance was removed.
return 0
endif
return iter
endmethod
private method applyMovement takes nothing returns nothing
local real pitch = 0.5
local boolean factorGround = (BlzBitAnd(this.instanceFlags, FLAG_IGNORE_GROUND_PATHING) != 0) and /*
*/ (this.objectType == OBJECT_TYPE_UNIT) and /*
*/ (BlzBitAnd(BlzGetUnitIntegerField(this.unit, UNIT_IF_MOVE_TYPE), 61) != 0)
if (BlzBitAnd(this.moveStatus, STATUS_READDED) != 0) then
set this.moveStatus = BlzBitAnd(this.moveStatus, -STATUS_READDED - 1)
return
endif
if (this.objectType == OBJECT_TYPE_UNIT) then
set dx = dx + GetUnitX(this.unit)
set dy = dy + GetUnitY(this.unit)
set dz = dz + GetUnitFlyHeight(this.unit)
if (factorGround) and (not IsValidGroundPathing(dx, dy)) then
set dx = GetGroundPathX()
set dy = GetGroundPathY()
endif
call SetUnitX(this.unit, dx)
call SetUnitY(this.unit, dy)
call SetUnitFlyHeight(this.unit, dz, 0.0)
return
endif
if (this.objectType == OBJECT_TYPE_EFFECT) then
if (dz < 0) then
set pitch = -0.5
endif
set pitch = ModuloReal(Atan2(dz*dz, dx*dx + dy*dy) * pitch, 2*bj_PI)
set dx = dx + BlzGetLocalSpecialEffectX(this.effect)
set dy = dy + BlzGetLocalSpecialEffectY(this.effect)
set dz = dz + BlzGetLocalSpecialEffectZ(this.effect)
//call PanCameraTo(this.coords.ox, this.coords.oy)
// call BlzSetSpecialEffectX(this.effect, dx)
// call BlzSetSpecialEffectY(this.effect, dy)
// call BlzSetSpecialEffectZ(this.effect, dz)
call BlzSetSpecialEffectPosition(this.effect, dx, dy, dz)
call BlzSetSpecialEffectYaw(this.effect, ModuloReal(this.coords.angle, 2*bj_PI))
call BlzSetSpecialEffectPitch(this.effect, pitch)
return
endif
set dx = dx + GetItemX(this.item)
set dy = dy + GetItemY(this.item)
call SetItemPosition(this.item, dx, dy)
endmethod
private static method onMovementUpdate takes nothing returns nothing
local integer unitIter = 0
local integer id = 0
local integer ttype = 0
local MovementListItem iter = ObjectMovementData.moveList.first
local thistype this = iter.data
local boolean computeDist = false
set loopIterCount = ModuloInteger(loopIterCount + 1, TICKS_FOR_CALC_DISTANCE())
set computeDist = (loopIterCount == 0)
set inCallback = true
loop
exitwhen (iter == 0)
// =======================
// Do Actions here
// =======================
set iter = this.processMovement(computeDist, id, iter)
// =======================
// Apply Movement here
// =======================
call this.applyMovement()
set this = iter.data
endloop
set inCallback = false
endmethod
// ==================================================
private static method onExit takes nothing returns nothing
// Fix this later
local integer id = GetIndexedUnitId()
// If unit doesn't have any instances in the first place, do not proceed.
if (ObjectMovementData.instanceList[id] == 0) then
return
endif
loop
// The destroy method calls removeInstance, which cleans up the instanceList[id]
// for us if the list in question is empty.
exitwhen (ObjectMovementData.instanceList[id] == 0)
call thistype(ObjectMovementData.instanceList[id].first.data).destroy()
endloop
endmethod
private static method onEnter takes nothing returns nothing
if ((UnitAddAbility(GetIndexedUnit(), 'Amrf')) and /*
*/ (UnitRemoveAbility(GetIndexedUnit(), 'Amrf'))) then
endif
endmethod
private static method init takes nothing returns nothing
set linear = BezierEase.linear
set ObjectMovementData.moveList = MovementList.create()
set ObjectMovementData.movementResp = GTimer.register(GAME_TICK, function thistype.onMovementUpdate)
call OnUnitIndex(function thistype.onEnter)
call OnUnitDeindex(function thistype.onExit)
endmethod
implement Init
endstruct
module ObjectMovementTemplate
static ObjectMovementResponse resp = 0
// Stub method operator.
static if not thistype.FLAGSET.exists then
static method FLAGSET takes nothing returns integer
return ObjectMovement.FLAG_NO_TARGET_ON_STOP + ObjectMovement.FLAG_DESTROY_ON_OBJECT_DEATH + /*
*/ ObjectMovement.FLAG_IGNORE_GROUND_PATHING
endmethod
endif
static if not thistype.MISSILE_MODEL.exists then
static method MISSILE_MODEL takes nothing returns string
return ""
endmethod
endif
static method applyUnitMovement takes unit whichUnit returns ObjectMovement
return ObjectMovement.createForUnit(whichUnit, thistype.resp)
endmethod
static method applyItemMovement takes item whichItem returns ObjectMovement
return ObjectMovement.createForItem(whichItem, thistype.resp)
endmethod
static method applyMovement takes real cx, real cy returns ObjectMovement
return ObjectMovement.create(thistype.MISSILE_MODEL(), cx, cy, thistype.resp)
endmethod
static method applyCustomMovement takes string model, real cx, real cy returns ObjectMovement
return ObjectMovement.create(model, cx, cy, thistype.resp)
endmethod
private static method onInit takes nothing returns nothing
set resp = ObjectMovementResponse.create(null, null, null, null, null, null)
set resp.flag = thistype.FLAGSET()
static if (thistype.onLaunch.exists) then
call resp.launchResponse.change(function thistype.onLaunch)
endif
static if (thistype.onMove.exists) then
call resp.moveResponse.change(function thistype.onMove)
endif
static if (thistype.onStop.exists) then
call resp.stopResponse.change(function thistype.onStop)
endif
static if (thistype.onResume.exists) then
call resp.resumeResponse.change(function thistype.onResume)
endif
static if (thistype.onPause.exists) then
call resp.pauseResponse.change(function thistype.onPause)
endif
static if (thistype.onDest.exists) then
call resp.destroyResponse.change(function thistype.onDest)
endif
endmethod
endmodule
function GetSpecialEffectHeight takes effect whichEffect returns real
return BlzGetLocalSpecialEffectZ(whichEffect) - ObjectCoords.getZ(BlzGetLocalSpecialEffectX(whichEffect), BlzGetLocalSpecialEffectY(whichEffect))
endfunction
function SetSpecialEffectHeight takes effect whichEffect, real height returns nothing
call BlzSetSpecialEffectZ(whichEffect, ObjectCoords.getZ(BlzGetLocalSpecialEffectX(whichEffect), BlzGetLocalSpecialEffectY(whichEffect) + height))
endfunction
endlibrary
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
* v1.2.2, by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex assigns every unit an unique integer. It attempts to make that number within the
* maximum array limit so you can associate it with one.
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the script to your map, save it, then restart the editor and comment out the line below.
*/
///! external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/* ________________________________________________________________________
* 2. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
private module UnitDexConfig
// The raw code of the leave detection ability.
static constant integer DETECT_LEAVE_ABILITY = 'uDex'
// Allow debug messages (debug mode must also be on)
static constant boolean ALLOW_DEBUGGING = true
// Uncomment the lines below to define a global filter for units being indexed
/*static method onFilter takes unit u returns boolean
return true
endmethod*/
endmodule
/* _________________________________________________________________________
* 3. Function API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Every function inlines.
*
* function GetUnitId takes unit whichUnit returns integer
* function GetUnitById takes integer index returns unit
*
* function UnitDexEnable takes boolean flag returns nothing
* function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
* function IsUnitIndexed takes unit u returns boolean
* function IsIndexingEnabled takes nothing returns boolean
*
* function GetIndexedUnit takes nothing returns unit
* function GetIndexedUnitId takes nothing returns integer
*
* function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
* function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
* function OnUnitIndex takes code func returns triggercondition
* function OnUnitDeidex takes code func returns triggercondition
* _________________________________________________________________________
* 4. Struct API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex.Enabled = false // toggle the indexer
* UnitDex.Initialized // returns true if the preload timer has finished
* UnitDex.Count // returns the amount of units indexed
* UnitDex.Unit[0] // access the UnitDex array directly
* UnitDex.Group // a unit group containing every indexed unit (for enumeration)
* UnitDex.LastIndex // returns the last indexed unit's id
* _________________________________________________________________________
* 5. Public Variables
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* These are to be used with the "eventtype" argument of the event API:
*
* constant integer EVENT_UNIT_INDEX = 0
* constant integer EVENT_UNIT_DEINDEX = 1
* _________________________________________________________________________
* 6. Examples
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Associate a unit with a variable
*
* set UnitFlag[GetUnitId(yourUnit)] = true
*
* 2. Allocate a struct instance using a units assigned ID
*
* local somestruct data = GetUnitId(yourUnit)
*
* 3. Detect when a unit leaves the map
*
* function Exit takes nothing returns nothing
* call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
* endfunction
*
* call OnUnitDeindex(function Exit)
* // or
* call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
* _________________________________________________________________________
* 7. How it works
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Enumerate all preplaced units
* 2. TriggerRegisterEnterRegion native to detect when a unit enters the map
* 3. Assign each unit that enters the map a unique integer
* 4. Give every unit an ability based off of defend. There is no native to accurately
* detect when a unit leaves the map but when a unit dies or is removed from the game
* they are issued the "undefend" order
* 5. Catch the "undefend" order and remove unit from the queue
* _________________________________________________________________________
* 8. Notes
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
* - The object merger line should be commented out after saving and restarting.
*
* -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
// Event types
constant integer EVENT_UNIT_INDEX = 0
constant integer EVENT_UNIT_DEINDEX = 1
// System variables
private trigger array IndexTrig
private integer Index = 0
private real E=-1
private boolexpr FilterEnter
endglobals
function GetUnitId takes unit whichUnit returns integer
return GetUnitUserData(whichUnit)
endfunction
function GetUnitById takes integer index returns unit
return UnitDex.Unit[index]
endfunction
function GetIndexedUnit takes nothing returns unit
return UnitDex.Unit[UnitDex.LastIndex]
endfunction
function GetIndexedUnitId takes nothing returns integer
return UnitDex.LastIndex
endfunction
function IsUnitIndexed takes unit u returns boolean
return (GetUnitById(GetUnitId(u)) != null)
endfunction
function UnitDexEnable takes boolean flag returns nothing
set UnitDex.Enabled = flag
endfunction
function IsIndexingEnabled takes nothing returns boolean
return UnitDex.Enabled
endfunction
function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
return TriggerAddCondition(IndexTrig[eventtype], func)
endfunction
function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
call TriggerRemoveCondition(IndexTrig[eventtype], c)
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
endfunction
function OnUnitIndex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
endfunction
function OnUnitDeindex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
endfunction
/****************************************************************/
private keyword UnitDexCore
struct UnitDex extends array
static boolean Enabled = true
readonly static integer LastIndex
readonly static boolean Initialized=false
readonly static group Group=CreateGroup()
readonly static unit array Unit
readonly static integer Count = 0
readonly static integer array List
implement UnitDexConfig
private static integer Counter = 0
implement UnitDexCore
endstruct
function UnitDexRemove takes unit u, boolean runEvents returns boolean
return UnitDex.Remove(u, runEvents)
endfunction
/****************************************************************/
private module UnitDexCore
static method Remove takes unit u, boolean runEvents returns boolean
local integer i
if (IsUnitIndexed(u)) then
set i = GetUnitId(u)
set UnitDex.List[i] = Index
set Index = i
call GroupRemoveUnit(UnitDex.Group, u)
call SetUnitUserData(u, 0)
if (runEvents) then
set UnitDex.LastIndex = i
set E = EVENT_UNIT_DEINDEX
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
set E = -1
endif
set UnitDex.Unit[i] = null
set UnitDex.Count = UnitDex.Count - 1
return true
endif
return false
endmethod
private static method onGameStart takes nothing returns nothing
local integer i = 1
loop
exitwhen i > Counter
set LastIndex = i
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
set E = EVENT_UNIT_INDEX
set E = -1
set i = i + 1
endloop
set LastIndex = Counter
set Initialized = true
set FilterEnter = null
call DestroyTimer(GetExpiredTimer())
endmethod
private static method onEnter takes nothing returns boolean
local unit u = GetFilterUnit()
local integer i = GetUnitId(u)
local integer t = Index
if (i == 0 and thistype.Enabled) then
// If a filter was defined pass the unit through it.
static if (thistype.onFilter.exists) then
if (not thistype.onFilter(u)) then
set u = null
return false // check failed
endif
endif
// Handle debugging
static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
set u = null
return false
endif
endif
// Add to group of indexed units
call GroupAddUnit(thistype.Group, u)
// Give unit the leave detection ability
call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
// Allocate index
if (Index != 0) then
set Index = List[t]
else
set Counter = Counter + 1
set t = Counter
endif
set List[t] = -1
set LastIndex = t
set thistype.Unit[t] = u
set Count = Count + 1
// Store the index
call SetUnitUserData(u, t)
if (thistype.Initialized) then
// Execute custom events registered with RegisterUnitIndexEvent
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_INDEX
// Reset so the event can occur again
set E = -1
endif
endif
set u = null
return false
endmethod
private static method onLeave takes nothing returns boolean
local unit u
local integer i
// Check if order is undefend.
if (thistype.Enabled and GetIssuedOrderId() == 852056) then
set u = GetTriggerUnit()
// If unit was killed (not removed) then don't continue
if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
set u = null
return false
endif
set i = GetUnitId(u)
// If unit has been indexed then deindex it
if (i > 0 and i <= Counter and u == GetUnitById(i)) then
// Recycle the index
set List[i] = Index
set Index = i
set LastIndex = i
// Remove to group of indexed units
call GroupRemoveUnit(thistype.Group, u)
// Execute custom events without any associated triggers
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_DEINDEX
// Remove entry
call SetUnitUserData(u, 0)
set thistype.Unit[i] = null
// Decrement unit count
set Count = Count - 1
// Reset so the event can occur again
set E = -1
endif
set u = null
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local player p
local unit u
static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
local rect world = GetWorldBounds()
endif
static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
local group ENUM_GROUP = CreateGroup() // If not, create the group.
endif
set FilterEnter = Filter(function thistype.onEnter)
// Begin to index units when they enter the map
static if (LIBRARY_WorldBounds) then
call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
else
call RegionAddRect(reg, world)
call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
call RemoveRect(world)
set world = null
endif
call TriggerAddCondition(t, Filter(function thistype.onLeave))
set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
loop
set p = Player(i)
// Detect "undefend"
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
// Hide the detect ability from players
call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
// Index preplaced units
set i = 0
loop
call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
static if (not LIBRARY_GroupUtils) then
call DestroyGroup(ENUM_GROUP)
set ENUM_GROUP = null
endif
set LastIndex = Counter
// run init triggers
call TimerStart(CreateTimer(), 0.00, false, function thistype.onGameStart)
endmethod
endmodule
endlibrary
library UnitAuxEvents requires /*
------------------
*/ UnitDex /*
------------------
----------------------
*/ EventListener /*
----------------------
--------------
*/ Init /*
--------------
------------------
*/ Flagbits /*
------------------
----------------------
*/ WorldBounds /*
----------------------
-------------------------------------------------------------------
|
| UnitExtraEvents
| v.1.0.0
|
|-------------------------------------------------------------------
|
*/
globals
private constant integer DETECT_ABIL_ID = 'uDey'
private constant integer DETECT_ORDER = 852056 // undefend
endglobals
// For debug purposes only.
public function FourCC takes integer value returns string
local string charMap = "..................................!.#$%&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
local string result = ""
local integer remainingValue = value
local integer charValue
local integer byteno
set byteno = 0
loop
set charValue = ModuloInteger(remainingValue, 256)
set remainingValue = remainingValue / 256
set result = SubString(charMap, charValue, charValue + 1) + result
set byteno = byteno + 1
exitwhen byteno == 4
endloop
return result
endfunction
private module UnitAuxOps
static method operator unit takes nothing returns unit
return unitA[index]
endmethod
static method operator curTransport takes nothing returns unit
return transportA[index]
endmethod
static method operator prevTransformID takes nothing returns integer
return prevUnitID[GetUnitId(unitA[index])]
endmethod
static method operator curTransformID takes nothing returns integer
return GetUnitTypeId(unitA[index])
endmethod
static method operator [] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
method operator isDead takes nothing returns boolean
return IsFlagSet(statusFlags[this], STATUS_DEAD)
endmethod
method operator isTransforming takes nothing returns boolean
return IsFlagSet(statusFlags[this], STATUS_TRANSFORMING)
endmethod
method operator wasKilled takes nothing returns boolean
return IsFlagSet(statusFlags[this], STATUS_KILLED)
endmethod
method operator isTransported takes nothing returns boolean
return IsFlagSet(statusFlags[this], STATUS_TRANSPORTED)
endmethod
method operator transportCount takes nothing returns integer
return BlzGroupGetSize(this.transportGroup)
endmethod
endmodule
struct UnitAuxHandler extends array
readonly static EventListener ON_TRANSFORM = 0
readonly static EventListener ON_DEATH = 0
readonly static EventListener ON_RESURRECT = 0
readonly static EventListener ON_LOAD = 0
readonly static EventListener ON_UNLOAD = 0
// While STATUS_DEAD and STATUS_KILLED are similar,
// STATUS_DEAD is flagged when the unit is considered dead
// by UnitAlive(), whereas STATUS_KILLED is flagged when
// the unit triggers a native EVENT_PLAYER_UNIT_DEATH event.
private static constant integer STATUS_USED = 1
readonly static constant integer STATUS_DEAD = 2
readonly static constant integer STATUS_KILLED = 4
readonly static constant integer STATUS_TRANSFORMING = 8
readonly static constant integer STATUS_TRANSPORTED = 16
private static integer array statusFlags
readonly static integer array prevUnitID
private static integer index = 0
private static unit array unitA
private static unit array transportA
readonly unit transport
readonly group transportGroup
// ENUM types for system handling of queued instance information
private static constant integer QUEUE_RESTORE_ABIL = 1
private static constant integer QUEUE_THROW_DEATH = 2
private static boolean queueInCallback = false
private static timer queueDetectTimer = CreateTimer()
private static integer queueIndex = 0
private static integer queueSIndex = 0
private static integer array queueInfo
private static unit array queueUnitA
implement UnitAuxOps
// ========================================================
// Event Handling functions
// ========================================================
private static method releaseEvent takes nothing returns nothing
set unitA[index] = null
set index = index - 1
endmethod
private static method prepEvent takes unit source returns nothing
set index = index + 1
set unitA[index] = source
endmethod
private static method throwEvent takes unit source, EventListener whichEvent returns nothing
call thistype.prepEvent(source)
call whichEvent.run()
call thistype.releaseEvent()
endmethod
// ========================================================
// System functions
// ========================================================
private static method enqueueActions takes nothing returns nothing
local integer i = 1
local integer queueID = 0
set queueInCallback = true
loop
exitwhen i > queueIndex
set queueID = GetUnitId(queueUnitA[i])
if queueInfo[i] == QUEUE_RESTORE_ABIL then
call thistype.throwEvent(queueUnitA[i], ON_TRANSFORM)
set statusFlags[queueID] = UnsetFlag(statusFlags[queueID], STATUS_TRANSFORMING)
set prevUnitID[queueID] = GetUnitTypeId(queueUnitA[i])
call UnitAddAbility(queueUnitA[i], DETECT_ABIL_ID)
elseif queueInfo[i] == QUEUE_THROW_DEATH then
call throwEvent(queueUnitA[i], ON_DEATH)
endif
set i = i + 1
endloop
set queueInCallback = false
set queueIndex = 0
loop
exitwhen queueIndex >= queueSIndex
set queueIndex = queueIndex + 1
set queueInfo[queueIndex] = queueInfo[queueIndex + 0x4000]
set queueUnitA[queueIndex] = queueUnitA[queueIndex + 0x4000]
set queueInfo[queueIndex + 0x4000] = 0
set queueUnitA[queueIndex + 0x4000] = null
endloop
set queueSIndex = 0
if (queueIndex != 0) then
call TimerStart(queueDetectTimer, 0.0, false, function thistype.enqueueActions)
endif
endmethod
private static method incrementQueue takes nothing returns nothing
if (queueInCallback) then
set queueSIndex = queueSIndex + 1
endif
set queueIndex = queueIndex + 1
if (queueIndex == 1) then
call TimerStart(queueDetectTimer, 0.0, false, function thistype.enqueueActions)
endif
endmethod
private static method queueDetectAbility takes unit source returns nothing
call thistype.incrementQueue()
if (queueInCallback) then
set queueUnitA[queueSIndex + 0x4000] = source
set queueInfo[queueSIndex + 0x4000] = QUEUE_RESTORE_ABIL
return
endif
set queueUnitA[queueIndex] = source
set queueInfo[queueIndex] = QUEUE_RESTORE_ABIL
endmethod
private static method queueDeathEvent takes unit source returns nothing
call thistype.incrementQueue()
if (queueInCallback) then
set queueUnitA[queueSIndex + 0x4000] = source
set queueInfo[queueSIndex + 0x4000] = QUEUE_THROW_DEATH
return
endif
set queueUnitA[queueIndex] = source
set queueInfo[queueIndex] = QUEUE_THROW_DEATH
endmethod
// ========================================================
// Event Responses
// ========================================================
private static method onUndefendOrder takes nothing returns nothing
local unit source
local integer srcID
local boolean isAlive
if (GetIssuedOrderId() != DETECT_ORDER) then
return
endif
set source = GetTriggerUnit()
set srcID = GetUnitId(source)
// Since this will run as well, check if the statusFlags
// of the unit are defined.
if (statusFlags[srcID] == 0) then
set source = null
return
endif
// Check for transformation.
if (GetUnitAbilityLevel(source, DETECT_ABIL_ID) == 0) and /*
*/ (GetUnitTypeId(source) != prevUnitID[srcID]) and /*
*/ (not IsFlagSet(statusFlags[srcID], STATUS_TRANSFORMING)) then
set statusFlags[srcID] = SetFlag(statusFlags[srcID], STATUS_TRANSFORMING)
call thistype.queueDetectAbility(source)
// Hopefully, this won't trigger a death event as well.
return
endif
// Check if unit is alive
set isAlive = UnitAlive(source)
if ((not isAlive) and (not IsFlagSet(statusFlags[srcID], STATUS_DEAD))) then
// The unit has died.
set statusFlags[srcID] = SetFlag(statusFlags[srcID], STATUS_DEAD)
call thistype.queueDeathEvent(source)
elseif ((isAlive) and (IsFlagSet(statusFlags[srcID], STATUS_DEAD))) then
set statusFlags[srcID] = UnsetFlag(statusFlags[srcID], STATUS_DEAD + STATUS_KILLED)
call thistype.throwEvent(source, ON_RESURRECT)
endif
set source = null
endmethod
private static method onDeathEvent takes nothing returns nothing
local unit source
local integer srcID
local boolean isAlive
set source = GetTriggerUnit()
set srcID = GetUnitId(source)
// Since this will run as well, check if the statusFlags
// of the unit are defined.
if (statusFlags[srcID] == 0) then
set source = null
return
endif
set statusFlags[srcID] = SetFlag(statusFlags[srcID], STATUS_KILLED)
set source = null
endmethod
private static method onTransportEvent takes nothing returns nothing
local unit passenger = GetTriggerUnit()
local unit vehicle = GetTransportUnit()
local integer passID = GetUnitId(passenger)
local thistype vehicleID = thistype[vehicle]
set thistype[passenger].transport = vehicle
if (vehicleID.transportGroup == null) then
set vehicleID.transportGroup = CreateGroup()
endif
call GroupAddUnit(vehicleID.transportGroup, passenger)
set statusFlags[passID] = SetFlag(statusFlags[passID], STATUS_TRANSPORTED)
// Ripped from Bribe's version of jesus4lyf's Transport
call SetUnitX(passenger, WorldBounds.maxX)
call SetUnitY(passenger, WorldBounds.maxY)
// Throw event
call thistype.prepEvent(passenger)
set transportA[index] = vehicle
call ON_LOAD.run()
set transportA[index] = null
call thistype.releaseEvent()
set vehicle = null
set passenger = null
endmethod
private static method onUnloadEvent takes nothing returns nothing
local unit passenger = GetTriggerUnit()
local unit vehicle
local integer passID = GetUnitId(passenger)
local thistype vehicleID = 0
if (not IsFlagSet(statusFlags[passID], STATUS_TRANSPORTED)) then
set passenger = null
return
endif
set vehicle = thistype(passID).transport
set vehicleID = thistype[vehicle]
call GroupRemoveUnit(vehicleID.transportGroup, passenger)
if (vehicleID.transportCount == 0) then
call DestroyGroup(vehicleID.transportGroup)
set vehicleID.transportGroup = null
endif
set statusFlags[passID] = UnsetFlag(statusFlags[passID], STATUS_TRANSPORTED)
set thistype(passID).transport = null
call thistype.prepEvent(passenger)
set transportA[index] = vehicle
call ON_UNLOAD.run()
set transportA[index] = null
call thistype.releaseEvent()
set passenger = null
set vehicle = null
endmethod
private static method onForcedUnloadEvent takes nothing returns nothing
local unit vehicle = GetIndexedUnit()
local unit passenger
local thistype vehicleID = GetIndexedUnitId()
local integer passID = 0
if (IsFlagSet(statusFlags[vehicleID], STATUS_TRANSPORTED)) then
set passenger = vehicle
set passID = integer(vehicleID)
set vehicle = vehicleID.transport
set vehicleID.transport = null
set vehicleID = thistype[vehicle]
// The vehicle has become the passenger
set statusFlags[passID] = UnsetFlag(statusFlags[passID], STATUS_TRANSPORTED)
call GroupRemoveUnit(vehicleID.transportGroup, passenger)
if (vehicleID.transportCount == 0) then
call DestroyGroup(vehicleID.transportGroup)
set vehicleID.transportGroup = null
endif
// Throw event here.
call thistype.prepEvent(passenger)
set transportA[index] = vehicle
call ON_UNLOAD.run()
set transportA[index] = null
call thistype.releaseEvent()
// Reset variables
set vehicle = passenger
set vehicleID = thistype(passID)
set passenger = null
set passID = 0
endif
set statusFlags[vehicleID] = 0
if (vehicleID.transportCount == 0) then
set vehicle = null
return
endif
call thistype.prepEvent(vehicle)
set transportA[index] = vehicle
loop
set passenger = FirstOfGroup(vehicleID.transportGroup)
call GroupRemoveUnit(vehicleID.transportGroup, passenger)
exitwhen (passenger == null)
set passID = GetUnitId(passenger)
set statusFlags[passID] = UnsetFlag(statusFlags[passID], STATUS_TRANSPORTED)
set thistype(passID).transport = null
set unitA[index] = passenger
call ON_UNLOAD.run()
endloop
set transportA[index] = null
call thistype.releaseEvent()
call DestroyGroup(vehicleID.transportGroup)
set vehicleID.transportGroup = null
set vehicle = null
set passenger = null
endmethod
private static method onUnitLeave takes nothing returns nothing
local integer id = GetIndexedUnitId()
set statusFlags[id] = 0
set prevUnitID[id] = 0
endmethod
private static method onUnitEnter takes nothing returns nothing
local unit u = GetIndexedUnit()
local integer id = GetUnitId(u)
set statusFlags[id] = STATUS_USED
set prevUnitID[id] = GetUnitTypeId(u)
// Unit was created as a corpse.
call UnitAddAbility(u, DETECT_ABIL_ID)
if (not UnitAlive(u)) then
set statusFlags[id] = SetFlag(statusFlags[id], STATUS_DEAD)
endif
set u = null
endmethod
// ========================================================
// Initializing functions
// ========================================================
private static method initDetector takes nothing returns nothing
local trigger orderTrig = CreateTrigger()
local trigger deathTrig = CreateTrigger()
local trigger transTrig = CreateTrigger()
local trigger unloadTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(orderTrig, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerRegisterAnyUnitEventBJ(deathTrig, EVENT_PLAYER_UNIT_DEATH)
call TriggerRegisterAnyUnitEventBJ(transTrig, EVENT_PLAYER_UNIT_LOADED)
call TriggerRegisterEnterRegion(unloadTrig, WorldBounds.worldRegion, null)
call TriggerAddCondition(orderTrig, Condition(function thistype.onUndefendOrder))
call TriggerAddCondition(deathTrig, Condition(function thistype.onDeathEvent))
call TriggerAddCondition(transTrig, Condition(function thistype.onTransportEvent))
call TriggerAddCondition(unloadTrig, Condition(function thistype.onUnloadEvent))
call OnUnitDeindex(function thistype.onForcedUnloadEvent)
endmethod
private static method initVars takes nothing returns nothing
set ON_TRANSFORM = EventListener.create()
set ON_DEATH = EventListener.create()
set ON_RESURRECT = EventListener.create()
set ON_LOAD = EventListener.create()
set ON_UNLOAD = EventListener.create()
endmethod
private static method init takes nothing returns nothing
call OnUnitIndex(function thistype.onUnitEnter)
call OnUnitDeindex(function thistype.onUnitLeave)
call thistype.initVars()
call thistype.initDetector()
endmethod
implement Init
endstruct
endlibrary
library SpellHandler requires /*
------------------
*/ UnitDex /*
------------------
------------------
*/ Init /*
------------------
------------------
*/ ListT /*
------------------
---------------------------
*/ EventListener /*
---------------------------
-------------------------------------
|
| SpellHandler - vJASS
|
|-------------------------------------
|
| A library that handles the processing
| and information derived from spell events.
|
|-------------------------------------
|
| API
|
|-------------------------------------
|
| Good luck, lol
|
-------------------------------------
*/
globals
constant integer EVENT_CAST = 0
constant integer EVENT_CHANNEL = 1
constant integer EVENT_EFFECT = 2
constant integer EVENT_ENDCAST = 3
constant integer EVENT_FINISH = 4
constant integer EVENT_SKILL = 5
endglobals
private module SpellHandlerEventHandles
readonly static EventListener ON_CAST = 0
readonly static EventListener ON_CHANNEL = 0
readonly static EventListener ON_EFFECT = 0
readonly static EventListener ON_ENDCAST = 0
readonly static EventListener ON_FINISH = 0
readonly static EventListener ON_SKILL = 0
private static method onInit takes nothing returns nothing
set ON_CAST = EventListener.create()
set ON_CHANNEL = EventListener.create()
set ON_EFFECT = EventListener.create()
set ON_ENDCAST = EventListener.create()
set ON_FINISH = EventListener.create()
set ON_SKILL = EventListener.create()
endmethod
endmodule
private module SpellHandlerInfoModule
readonly static constant integer MASK_NO_TARGET = 1
readonly static constant integer MASK_POINT_TARGET = 2
readonly static constant integer MASK_TARGET = 4
readonly static constant integer MASK_UNIT_TARGET = 8
readonly static constant integer MASK_DEST_TARGET = 16
readonly static constant integer MASK_ITEM_TARGET = 32
static method requestGroup takes nothing returns group
if (recycleGIndex > 0) then
set recycleGIndex = recycleGIndex - 1
return recycleGroup[recycleGIndex + 1]
endif
return CreateGroup()
endmethod
static method releaseGroup takes group g returns nothing
call GroupClear(g)
set recycleGIndex = recycleGIndex + 1
set recycleGroup[recycleGIndex] = g
endmethod
private static method onInit takes nothing returns nothing
local integer i = 1
loop
exitwhen i > 12
set recycleGIndex = recycleGIndex + 1
set recycleGroup[i] = CreateGroup()
set i = i + 1
endloop
endmethod
endmodule
//! runtextmacro DEFINE_LIST("private", "SpellHandlerInfoList", "integer")
private struct SpellHandlerInfo extends array
implement Alloc
private static integer recycleGIndex = 0
private static group array recycleGroup
readonly integer curAbility
readonly integer curAbilityLevel
readonly boolean isCasting
readonly boolean channelFinished
readonly integer curTargetType
readonly unit curTargetUnit
readonly item curTargetItem
readonly destructable curTargetDest
readonly real curTargetX
readonly real curTargetY
readonly real curTargetAOE
readonly integer curOrderType
readonly integer curOrder
static method operator [] takes unit whichUnit returns thistype
return thistype(GetUnitId(whichUnit))
endmethod
static method pushAbilityInfo takes unit whichUnit, integer abil, integer level, boolean checkPrevious returns nothing
local thistype self = thistype[whichUnit]
if ((checkPrevious) and (self.curAbility == abil)) then
return
endif
set self.curAbility = abil
set self.curAbilityLevel = level
set self.isCasting = true
set self.channelFinished = false
set self.curOrder = GetUnitCurrentOrder(whichUnit)
set self.curTargetAOE = BlzGetAbilityRealLevelField(BlzGetUnitAbility(whichUnit, abil), ABILITY_RLF_AREA_OF_EFFECT, level - 1)
set self.curTargetUnit = GetSpellTargetUnit()
set self.curTargetDest = GetSpellTargetDestructable()
set self.curTargetItem = GetSpellTargetItem()
if (self.curTargetUnit != null) then
set self.curTargetType = BlzBitOr(MASK_TARGET, MASK_UNIT_TARGET)
elseif (self.curTargetDest != null) then
set self.curTargetType = BlzBitOr(MASK_TARGET, MASK_DEST_TARGET)
elseif (self.curTargetItem != null) then
set self.curTargetType = BlzBitOr(MASK_TARGET, MASK_ITEM_TARGET)
endif
if (BlzBitAnd(self.curTargetType, MASK_TARGET) != 0) then
set self.curOrderType = MASK_TARGET
set self.curTargetType = BlzBitAnd(self.curTargetType, -MASK_TARGET - 1)
return
endif
set self.curTargetX = GetSpellTargetX()
set self.curTargetY = GetSpellTargetY()
if ((self.curTargetX == 0) and (self.curTargetY == 0)) then
set self.curOrderType = MASK_NO_TARGET
return
endif
set self.curOrderType = MASK_POINT_TARGET
endmethod
static method markAbilityAsFinished takes unit whichUnit returns nothing
set thistype[whichUnit].channelFinished = true
endmethod
static method popAbilityInfo takes unit whichUnit returns nothing
local thistype self = thistype[whichUnit]
set self.curAbility = 0
set self.curAbilityLevel = 0
set self.isCasting = false
set self.channelFinished = false
set self.curOrder = 0
set self.curTargetAOE = 0.0
set self.curTargetUnit = null
set self.curTargetDest = null
set self.curTargetItem = null
set self.curTargetX = 0.0
set self.curTargetY = 0.0
set self.curOrderType = 0
endmethod
implement SpellHandlerInfoModule
endstruct
struct SpellHandler extends array
private static TableArray localEventMap = 0
private static integer stackLevel = 0
private static unit array unitStack
private static integer array abilityStack
private static integer array levelStack
readonly static unit unit = null
readonly static integer ability = 0
readonly static integer level = 0
implement SpellHandlerEventHandles
// ========================================================= //
// Available Public Operators //
// ========================================================= //
static method operator [] takes unit whichUnit returns SpellHandlerInfo
return SpellHandlerInfo(GetUnitId(whichUnit))
endmethod
static method operator current takes nothing returns SpellHandlerInfo
return SpellHandlerInfo(GetUnitId(unit))
endmethod
// ========================================================= //
// Public API //
// ========================================================= //
static method register takes integer eventType, integer abilID, code callback returns EventResponder
local EventListener listener = 0
// Check if eventType is invalid
if ((eventType > 5) or (eventType < 0)) then
return 0
endif
if (not localEventMap[eventType].has(abilID)) then
set listener = EventListener.create()
set localEventMap[eventType][abilID] = listener
else
set listener = EventListener(localEventMap[eventType][abilID])
endif
return listener.register(callback)
endmethod
// ========================================================= //
// Global value handlers //
// ========================================================= //
private static method pushStack takes nothing returns nothing
set stackLevel = stackLevel + 1
set unitStack[stackLevel] = unit
set abilityStack[stackLevel] = ability
set levelStack[stackLevel] = level
endmethod
private static method popStack takes nothing returns nothing
set unit = unitStack[stackLevel]
set ability = abilityStack[stackLevel]
set level = levelStack[stackLevel]
set stackLevel = stackLevel - 1
endmethod
// ========================================================= //
// Event Handling function //
// ========================================================= //
private static method onEventHandle takes nothing returns nothing
local eventid eventID = GetTriggerEventId()
local integer unitID
local EventListener listener
call thistype.pushStack()
set unit = GetTriggerUnit()
set unitID = GetUnitId(unit)
if (eventID == EVENT_PLAYER_HERO_SKILL) then
set ability = GetLearnedSkill()
set level = GetLearnedSkillLevel()
else
set ability = GetSpellAbilityId()
set level = GetUnitAbilityLevel(thistype.unit, ability)
endif
if (eventID == EVENT_PLAYER_UNIT_SPELL_CAST) then
set listener = EventListener(localEventMap[EVENT_CAST][ability])
call SpellHandlerInfo.pushAbilityInfo(unit, ability, level, true)
if (listener != 0) then
call listener.run()
endif
call ON_CAST.run()
elseif (eventID == EVENT_PLAYER_UNIT_SPELL_CHANNEL) then
set listener = EventListener(localEventMap[EVENT_CHANNEL][ability])
call SpellHandlerInfo.pushAbilityInfo(unit, ability, level, false)
if (listener != 0) then
call listener.run()
endif
call ON_CHANNEL.run()
elseif (eventID == EVENT_PLAYER_UNIT_SPELL_EFFECT) then
set listener = EventListener(localEventMap[EVENT_EFFECT][ability])
call SpellHandlerInfo.pushAbilityInfo(unit, ability, level, true)
if (listener != 0) then
call listener.run()
endif
call ON_EFFECT.run()
elseif (eventID == EVENT_PLAYER_UNIT_SPELL_ENDCAST) then
set listener = EventListener(localEventMap[EVENT_ENDCAST][ability])
if (listener != 0) then
call listener.run()
endif
call ON_ENDCAST.run()
call SpellHandlerInfo.popAbilityInfo(unit)
elseif (eventID == EVENT_PLAYER_UNIT_SPELL_FINISH) then
set listener = EventListener(localEventMap[EVENT_FINISH][ability])
call SpellHandlerInfo.markAbilityAsFinished(unit)
if (listener != 0) then
call listener.run()
endif
call ON_FINISH.run()
elseif (eventID == EVENT_PLAYER_HERO_SKILL) then
set listener = EventListener(localEventMap[EVENT_SKILL][ability])
call SpellHandlerInfo.pushAbilityInfo(unit, ability, level, true)
if (listener != 0) then
call listener.run()
endif
call ON_SKILL.run()
call SpellHandlerInfo.popAbilityInfo(unit)
endif
call thistype.popStack()
endmethod
// ========================================================= //
// Initialization API //
// ========================================================= //
private static method initEvents takes nothing returns nothing
local trigger trig = CreateTrigger()
local integer i = 0
local player p = null
call TriggerAddCondition(trig, Condition(function thistype.onEventHandle))
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(trig, p, EVENT_PLAYER_UNIT_SPELL_CAST, null)
call TriggerRegisterPlayerUnitEvent(trig, p, EVENT_PLAYER_UNIT_SPELL_CHANNEL, null)
call TriggerRegisterPlayerUnitEvent(trig, p, EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
call TriggerRegisterPlayerUnitEvent(trig, p, EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
call TriggerRegisterPlayerUnitEvent(trig, p, EVENT_PLAYER_UNIT_SPELL_FINISH, null)
call TriggerRegisterPlayerUnitEvent(trig, p, EVENT_PLAYER_HERO_SKILL, null)
set i = i + 1
endloop
endmethod
private static method initVars takes nothing returns nothing
set localEventMap = TableArray[6]
endmethod
private static method init takes nothing returns nothing
call initVars()
call initEvents()
endmethod
implement Init
endstruct
endlibrary
library DamageHandler requires /*
------------------
*/ UnitDex /*
------------------
------------------
*/ Init /*
------------------
---------------------------
*/ EventListener /*
---------------------------
---------------------------
*/ Flagbits /*
---------------------------
-------------------------------------
|
| DamageHandler - vJASS
| v.1.1.0.0
|
|-------------------------------------
|
| A library that handles the processing
| and modification of damage events.
|
|-------------------------------------
|
| API
|
|-------------------------------------
|
| class DamageHandler {
|
| ------------------------------------------
| Event Hierarchy: (Highest to Lowest Order)
| All of the following events are members of the class.
|
| - MODIFIER_SYSTEM - (Will always run)
| - MODIFIER_OUTGOING - (Runs on DAMAGING_EVENT)
| - MODIFIER_INCOMING - (Runs on DAMAGED_EVENT)
| - ON_DAMAGE - (Runs on DAMAGED_EVENT. Cannot modify dmg amount at this point.)
| - ON_LETHAL_DAMAGE - (Runs on DAMAGED_EVENT. Sets the final health of the target if damage dealt will kill.)
| - AFTER_DAMAGE - (Runs after DAMAGED_EVENT. Be careful with this event, though.)
| ------------------------------------------
|
| ------------------------------------------
| Operators:
| static real dmg
| - The amount of damage dealt. Cannot be changed
| from an ON_DAMAGE event onwards.
|
| static attacktype attacktype
| static damagetype damagetype
| static weapontype weapontype
| - The amount of damage dealt. Cannot be changed
| from a MODIFIER_INCOMING event onwards.
|
| static integer dmgFlags
| - The flag mask of the damage instance. By
| changing this value, certain properties of
| the damage instance can be changed.
|
| static real finalHP
| - The target's health after damage has been
| applied. Only meaningful in ON_LETHAL_DAMAGE.
|
| readonly static unit source
| readonly static unit target
| - Damage source and target respectively.
|
| readonly static unit pureDmg
| - Damage value before triggered modifications.
|
| readonly static unit outgoingDmg
| - Damage value after triggered modifications, but before
| any game modifications.
|
| readonly static unit incomingDmg
| - Immediate damage value after game modifications.
|
| readonly static unit calcFactor
| - Ratio between incomingDmg and outgoingDmg
|
| ------------------------------------------
|
| ------------------------------------------
| Methods:
| static method isTriggeredDmg() -> bool
| - Returns true if damage is dealt via UnitDamageTarget.
|
| static method isGameDmg() -> bool
| - Returns true if damage is dealt by the game.
| It assumes the first damage instance comes from
| the game.
|
| static method isDamageAttack() -> bool
| - Returns true if damage came from an attack.
| (Primarily checked through the comparison
| pDamageTypeA[index] == DAMAGE_TYPE_NORMAL)
|
| static method isDamagePhysical() -> bool
| static method isDamageMagical() -> bool
| - Pretty self-explanatory.
|
| -------------------------------------
| | Added in v.1.1.0.0
| -------------------------------------
| static method isDamageMelee() -> bool
| - Only sensible in direct attacks
| - Always returns false otherwise.
|
| static method isDamageRanged() -> bool
| - Only sensible in direct attacks
| - Always returns true otherwise.
| -------------------------------------
|
| static method isDamagePure() -> bool
| - Returns true if damage instance was applied
| via dealPureDamage(src, targ, amt)
|
| static method setPureDamage(real value)
| - Changes the value of pure damage dealt.
| - Only works when damage instance is pure damage.
|
| static method dealPureDamage(unit src, unit targ, real amt) -> bool
| - Deals a pure damage instance. Still fires damage handlers.
| - Returns the result from UnitDamageTarget.
|
| static method dealSilentDamage(unit src, unit targ, real amt,
| bool isAtk, bool ranged, attacktype a,
| damagetype d, weapontype w) -> bool
| - Deals a damage instance that does not fire most damage handlers.
| - Callbacks registered to the MODIFIER_SYSTEM event will
| still run with this type of damage instance.
| ------------------------------------------
| }
|-------------------------------------
|
| Event Registration:
|
|-------------------------------------
|
| Copy the following line:
| - call DamageHandler.{YOUR_DESIRED_EVENT}.register({YOUR_CALLBACK})
| - See Event Hierarchy for the list of events.
| - YOUR_CALLBACK must be a function parameter
|
-------------------------------------
*/
native UnitAlive takes unit id returns boolean
private struct DamageType extends array
readonly static constant integer PHYSICAL_DAMAGE = 1
readonly static constant integer MAGIC_DAMAGE = 2
readonly static constant integer UNIVERSAL_DAMAGE = 4
private static integer array damageType
static method operator [] takes damagetype d returns integer
return damageType[GetHandleId(d)]
endmethod
private static method init takes nothing returns nothing
set damageType[0] = UNIVERSAL_DAMAGE
set damageType[24] = UNIVERSAL_DAMAGE
set damageType[26] = UNIVERSAL_DAMAGE
set damageType[8] = MAGIC_DAMAGE
set damageType[9] = MAGIC_DAMAGE
set damageType[10] = MAGIC_DAMAGE
set damageType[13] = MAGIC_DAMAGE
set damageType[14] = MAGIC_DAMAGE
set damageType[15] = MAGIC_DAMAGE
set damageType[17] = MAGIC_DAMAGE
set damageType[18] = MAGIC_DAMAGE
set damageType[19] = MAGIC_DAMAGE
set damageType[20] = MAGIC_DAMAGE
set damageType[21] = MAGIC_DAMAGE
set damageType[25] = MAGIC_DAMAGE
set damageType[4] = PHYSICAL_DAMAGE
set damageType[5] = PHYSICAL_DAMAGE
set damageType[11] = PHYSICAL_DAMAGE
set damageType[12] = PHYSICAL_DAMAGE
set damageType[16] = PHYSICAL_DAMAGE
set damageType[22] = PHYSICAL_DAMAGE
set damageType[23] = PHYSICAL_DAMAGE
endmethod
implement Init
endstruct
private struct AttackType extends array
readonly static constant integer NORMAL_DAMAGE = 1
readonly static constant integer SPELLS_DAMAGE = 2
readonly static constant integer MAGIC_DAMAGE = 4
private static integer array attackType
static method operator [] takes attacktype a returns integer
return attackType[GetHandleId(a)]
endmethod
private static method init takes nothing returns nothing
set attackType[1] = NORMAL_DAMAGE
set attackType[2] = NORMAL_DAMAGE
set attackType[3] = NORMAL_DAMAGE
set attackType[5] = NORMAL_DAMAGE
set attackType[6] = NORMAL_DAMAGE
set attackType[0] = SPELLS_DAMAGE
set attackType[4] = MAGIC_DAMAGE
endmethod
implement Init
endstruct
globals
constant integer DAMAGE_FLAG_IS_ATTACK = 1
constant integer DAMAGE_FLAG_IS_PHYSICAL = 2
constant integer DAMAGE_FLAG_IS_MAGICAL = 4
constant integer DAMAGE_FLAG_IS_UNIVERSAL = DAMAGE_FLAG_IS_PHYSICAL + DAMAGE_FLAG_IS_MAGICAL
constant integer DAMAGE_FLAG_IS_GAME = 8
constant integer DAMAGE_FLAG_IS_CODE = 0x10
constant integer DAMAGE_FLAG_IS_MELEE = 0x20
constant integer DAMAGE_FLAG_IS_PURE = 0x40 // When a damage instance has this flag, it will override wc3's damage calculations.
constant integer DAMAGE_FLAG_OVERRIDE_PURE = 0x80 // Tells the system that the amount of pure damage dealt can be changed. Will immediately be unflagged after changing the value of pure damage.
constant integer DAMAGE_FLAG_USES_ARMOR = 0x100
private constant integer DAMAGE_FLAG_LOCK_INFO = 0x200 // Tells the system that the damagetype, weapontype or the attacktype of the damage instance cannot be changed.
private constant integer DAMAGE_FLAG_SUPPRESS_EVENTS = 0x400 // When a damage instance has this flag, it will not proc all events associated with damage detection (except system modifiers).
private constant integer DAMAGE_FLAG_WILL_BE_CODE = 0x800 // An anticipatory flag that tells the system that the next source of (recursive) damage is from triggers/code.
private constant integer DAMAGE_FLAG_WILL_BE_GAME = 0x1000 // An anticipatory flag that tells the system that the next source of (recursive) damage is from the game.
private constant integer DAMAGE_FLAG_PHASE_OUTGOING = 0x2000
private constant integer DAMAGE_FLAG_PHASE_INCOMING = 0x4000
private constant integer DAMAGE_FLAG_PHASE_RECEIVED = DAMAGE_FLAG_PHASE_OUTGOING + DAMAGE_FLAG_PHASE_INCOMING
private constant integer DAMAGE_FLAG_LOCK_DAMAGE = 0x8000 // Indicates that the damage observation phase has begun. One can no longer change the damage dealt directly.
endglobals
private struct DamageHandlerData extends array
endstruct
private module DamageHandlerEvents
readonly static EventListener MODIFIER_SYSTEM = 0
readonly static EventListener MODIFIER_OUTGOING = 0
readonly static EventListener MODIFIER_INCOMING = 0
readonly static EventListener ON_DAMAGE = 0
readonly static EventListener ON_LETHAL_DAMAGE = 0
readonly static EventListener AFTER_DAMAGE = 0
private static method onInit takes nothing returns nothing
set MODIFIER_SYSTEM = EventListener.create()
set MODIFIER_OUTGOING = EventListener.create()
set MODIFIER_INCOMING = EventListener.create()
set ON_DAMAGE = EventListener.create()
set ON_LETHAL_DAMAGE = EventListener.create()
set AFTER_DAMAGE = EventListener.create()
call MODIFIER_SYSTEM.setMaxRecursionDepth(MAX_RECURSION)
call MODIFIER_OUTGOING.setMaxRecursionDepth(MAX_RECURSION)
call MODIFIER_INCOMING.setMaxRecursionDepth(MAX_RECURSION)
call ON_DAMAGE.setMaxRecursionDepth(MAX_RECURSION)
call ON_LETHAL_DAMAGE.setMaxRecursionDepth(MAX_RECURSION)
call AFTER_DAMAGE.setMaxRecursionDepth(MAX_RECURSION)
call MODIFIER_SYSTEM.setMaxCallbackDepth(MAX_CALLBACK_DEPTH)
call MODIFIER_OUTGOING.setMaxCallbackDepth(MAX_CALLBACK_DEPTH)
call MODIFIER_INCOMING.setMaxCallbackDepth(MAX_CALLBACK_DEPTH)
call ON_DAMAGE.setMaxCallbackDepth(MAX_CALLBACK_DEPTH)
call ON_LETHAL_DAMAGE.setMaxRecursionDepth(MAX_CALLBACK_DEPTH)
call AFTER_DAMAGE.setMaxCallbackDepth(MAX_CALLBACK_DEPTH)
endmethod
endmodule
private module DamageHandlerOperators
static method operator dmg takes nothing returns real
return pDmgA[index]
endmethod
static method operator dmg= takes real newValue returns nothing
// If damage can no longer be changed, terminate immediately.
if (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_LOCK_DAMAGE) != 0) then
return
endif
if ((BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_PURE) == 0) or /*
*/ (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_OVERRIDE_PURE) != 0)) then
set pDmgA[index] = newValue
set dmgFlagsA[index] = BlzBitAnd(dmgFlagsA[index], -DAMAGE_FLAG_OVERRIDE_PURE - 1)
if (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_PURE) != 0) then
set pureDmgA[index] = newValue
endif
endif
endmethod
static method operator attacktype takes nothing returns attacktype
return pAttacktypeA[index]
endmethod
static method operator attacktype= takes attacktype newAtk returns nothing
if (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_LOCK_INFO + DAMAGE_FLAG_PHASE_INCOMING) != 0) then
return
endif
set pAttacktypeA[index] = newAtk
endmethod
static method operator damagetype takes nothing returns damagetype
return pDamagetypeA[index]
endmethod
static method operator damagetype= takes damagetype newDmg returns nothing
local integer temp = 0
if (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_LOCK_INFO + DAMAGE_FLAG_PHASE_INCOMING) != 0) then
return
endif
set pDamagetypeA[index] = newDmg
set dmgFlagsA[index] = BlzBitAnd(dmgFlagsA[index], -DAMAGE_FLAG_IS_UNIVERSAL - 1)
set temp = DamageType[pDamagetypeA[index]]
if (temp == DamageType.PHYSICAL_DAMAGE) then
set temp = DAMAGE_FLAG_IS_PHYSICAL
elseif (temp == DamageType.MAGIC_DAMAGE) then
set temp = DAMAGE_FLAG_IS_MAGICAL
elseif (temp == DamageType.UNIVERSAL_DAMAGE) then
set temp = DAMAGE_FLAG_IS_UNIVERSAL
endif
set dmgFlagsA[index] = dmgFlagsA[index] + temp
endmethod
static method operator weapontype takes nothing returns weapontype
return pWeapontypeA[index]
endmethod
static method operator weapontype= takes weapontype newWpn returns nothing
if (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_LOCK_INFO + DAMAGE_FLAG_PHASE_INCOMING) != 0) then
return
endif
set pWeapontypeA[index] = newWpn
endmethod
static method operator dmgFlags takes nothing returns integer
return dmgFlagsA[index]
endmethod
static method operator dmgFlags= takes integer x returns nothing
set dmgFlagsA[index] = x
endmethod
static method operator finalHP takes nothing returns real
return pFinalHPA[index]
endmethod
static method operator finalHP= takes real x returns nothing
set pFinalHPA[index] = x
endmethod
static method operator source takes nothing returns unit
return sourceA[index]
endmethod
static method operator target takes nothing returns unit
return targetA[index]
endmethod
static method operator pureDmg takes nothing returns real
return pureDmgA[index]
endmethod
static method operator outgoingDmg takes nothing returns real
return outgoingDmgA[index]
endmethod
static method operator incomingDmg takes nothing returns real
return incomingDmgA[index]
endmethod
static method operator calcFactor takes nothing returns real
return calcFactorA[index]
endmethod
endmodule
struct DamageHandler extends array
private static constant integer MAX_RECURSION = 8
private static constant integer MAX_CALLBACK_DEPTH = 1
private static Table detectorMap = 0
private static code onCleanupInstances = null
private static code onInflictDamage = null
private static timer cleanupTimer = null
private static integer index = 0
private static unit array sourceA
private static unit array targetA
private static real array pureDmgA
private static real array outgoingDmgA
private static real array incomingDmgA
private static real array calcFactorA
private static real array pDmgA
private static real array pFinalHPA
private static attacktype array pAttacktypeA
private static damagetype array pDamagetypeA
private static weapontype array pWeapontypeA
private static integer array dmgFlagsA
private static trigger array detectorA
private static integer dreamFlags = DAMAGE_FLAG_WILL_BE_GAME // Indicates several flags that tell the handler how to handle the next nested damage instance.
implement DamageHandlerEvents
implement DamageHandlerOperators
// ========================================================= //
// Public API //
// ========================================================= //
static method isTriggeredDmg takes nothing returns boolean
return BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_CODE) != 0
endmethod
static method isGameDmg takes nothing returns boolean
return BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_GAME) != 0
endmethod
static method isDamageAttack takes nothing returns boolean
return BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_ATTACK) != 0
endmethod
static method isDamagePhysical takes nothing returns boolean
return BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_PHYSICAL) != 0
endmethod
static method isDamageMagical takes nothing returns boolean
return BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_MAGICAL) != 0
endmethod
static method isDamagePure takes nothing returns boolean
return BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_PURE) != 0
endmethod
static method isDamageMelee takes nothing returns boolean
return (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_MELEE) != 0)
endmethod
static method isDamageRanged takes nothing returns boolean
return (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_MELEE) == 0)
endmethod
static method setPureDamage takes real newValue returns nothing
set dmgFlagsA[index] = BlzBitOr(dmgFlagsA[index], DAMAGE_FLAG_OVERRIDE_PURE)
set dmg = newValue
endmethod
static method dealPureDamage takes unit src, unit targ, real amt returns boolean
local integer prevDream = dreamFlags
local boolean result = false
set dreamFlags = DAMAGE_FLAG_WILL_BE_CODE + DAMAGE_FLAG_IS_PURE + DAMAGE_FLAG_LOCK_INFO
set result = UnitDamageTarget(src, targ, amt, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
set dreamFlags = prevDream
return result
endmethod
static method dealSilentDamage takes unit src, unit targ, real amt, boolean isAtk, boolean ranged, attacktype atk, damagetype dmg, weapontype wpn returns boolean
local integer prevDream = dreamFlags
local boolean result = false
set dreamFlags = DAMAGE_FLAG_WILL_BE_CODE + DAMAGE_FLAG_LOCK_INFO + DAMAGE_FLAG_SUPPRESS_EVENTS
set result = UnitDamageTarget(src, targ, amt, isAtk, ranged, atk, dmg, wpn)
set dreamFlags = prevDream
return result
endmethod
// ============================================================= //
// Global State Managers //
// ============================================================= //
private static method removeDamageInfoRaw takes integer i returns nothing
set pDmgA[i] = 0.0
set pureDmgA[i] = 0.0
set outgoingDmgA[i] = 0.0
set incomingDmgA[i] = 0.0
set pFinalHPA[i] = 0.0
set sourceA[i] = null
set targetA[i] = null
set pDamagetypeA[i] = null
set pAttacktypeA[i] = null
set pWeapontypeA[i] = null
set dmgFlagsA[i] = 0
if (detectorA[i] != null) then
call detectorMap.integer.remove(GetHandleId(detectorA[i]))
call DisableTrigger(detectorA[i])
call DestroyTrigger(detectorA[i])
endif
set detectorA[i] = null
endmethod
private static method removeDamageInfo takes integer i returns nothing
if ((i > index) or (i <= 0)) then
return
endif
call removeDamageInfoRaw(i)
endmethod
private static method pushDamageInfo takes nothing returns nothing
local integer temp = dreamFlags
set index = index + 1
set sourceA[index] = GetEventDamageSource()
set targetA[index] = GetTriggerUnit()
set pureDmgA[index] = GetEventDamage()
set pDmgA[index] = pureDmgA[index]
set outgoingDmgA[index] = pureDmgA[index]
set pDamagetypeA[index] = BlzGetEventDamageType()
set pAttacktypeA[index] = BlzGetEventAttackType()
set pWeapontypeA[index] = BlzGetEventWeaponType()
// Determine the starting value for dmgFlagsA[index]
if (BlzBitAnd(temp, DAMAGE_FLAG_WILL_BE_GAME) != 0) then
set dmgFlagsA[index] = DAMAGE_FLAG_WILL_BE_GAME + DAMAGE_FLAG_IS_GAME + DAMAGE_FLAG_PHASE_OUTGOING
elseif (BlzBitAnd(temp, DAMAGE_FLAG_WILL_BE_CODE) != 0) then
set dmgFlagsA[index] = DAMAGE_FLAG_WILL_BE_CODE + DAMAGE_FLAG_IS_CODE + DAMAGE_FLAG_PHASE_OUTGOING
endif
// Tick additional flags as needed.
if (BlzBitAnd(temp, DAMAGE_FLAG_IS_PURE) != 0) then
set dmgFlagsA[index] = dmgFlagsA[index] + DAMAGE_FLAG_IS_PURE
endif
if (BlzBitAnd(temp, DAMAGE_FLAG_SUPPRESS_EVENTS) != 0) then
set dmgFlagsA[index] = dmgFlagsA[index] + DAMAGE_FLAG_SUPPRESS_EVENTS
endif
if (BlzBitAnd(temp, DAMAGE_FLAG_LOCK_INFO) != 0) then
set dmgFlagsA[index] = dmgFlagsA[index] + DAMAGE_FLAG_LOCK_INFO
endif
if (index == 1) then
call TimerStart(cleanupTimer, 0.00, false, onCleanupInstances)
endif
// Check if damage came from an attack.
if (pDamagetypeA[index] == DAMAGE_TYPE_NORMAL) then
set dmgFlagsA[index] = dmgFlagsA[index] + DAMAGE_FLAG_IS_ATTACK + DAMAGE_FLAG_USES_ARMOR
endif
if (BlzBitAnd(dmgFlagsA[index], DAMAGE_FLAG_IS_ATTACK) != 0) and /*
*/ ((IsUnitType(sourceA[index], UNIT_TYPE_MELEE_ATTACKER)) and /*
*/ ((not IsUnitType(sourceA[index], UNIT_TYPE_RANGED_ATTACKER)) or /*
*/ (pWeapontypeA[index] != null))) then
set dmgFlagsA[index] = dmgFlagsA[index] + DAMAGE_FLAG_IS_MELEE
endif
// Check if damage is either physical, magic, or universal.
set temp = DamageType[pDamagetypeA[index]]
if (temp == DamageType.PHYSICAL_DAMAGE) then
set temp = DAMAGE_FLAG_IS_PHYSICAL
elseif (temp == DamageType.MAGIC_DAMAGE) then
set temp = DAMAGE_FLAG_IS_MAGICAL
elseif (temp == DamageType.UNIVERSAL_DAMAGE) then
set temp = DAMAGE_FLAG_IS_UNIVERSAL
endif
set dmgFlagsA[index] = dmgFlagsA[index] + temp
endmethod
private static method popDamageInfo takes nothing returns nothing
call removeDamageInfoRaw(index)
if (index > 0) then
set index = index - 1
endif
endmethod
// ========================================================= //
// Cleanup Callback //
// ========================================================= //
private static method onCleanup takes nothing returns nothing
call PauseTimer(cleanupTimer)
loop
exitwhen index <= 0
call popDamageInfo()
endloop
endmethod
// ========================================================= //
// Event Handlers //
// ========================================================= //
private static method onDamagingEvent takes nothing returns nothing
local integer i
call pushDamageInfo()
// Throw events when appropriate
set i = index
set dreamFlags = DAMAGE_FLAG_WILL_BE_CODE
call MODIFIER_SYSTEM.run()
if (BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_SUPPRESS_EVENTS) == 0) then
// Throw a modifier event here.
call MODIFIER_OUTGOING.run()
endif
set dreamFlags = BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_WILL_BE_GAME + DAMAGE_FLAG_WILL_BE_CODE)
if (BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_IS_PURE) != 0) then
set pDmgA[i] = pureDmgA[i]
endif
call BlzSetEventDamage(pDmgA[i])
if (BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_LOCK_INFO) == 0) then
call BlzSetEventAttackType(pAttacktypeA[i])
call BlzSetEventDamageType(pDamagetypeA[i])
call BlzSetEventWeaponType(pWeapontypeA[i])
set dmgFlagsA[i] = dmgFlagsA[i] + DAMAGE_FLAG_LOCK_INFO
endif
set outgoingDmgA[i] = pDmgA[i]
set dmgFlagsA[i] = dmgFlagsA[i] - DAMAGE_FLAG_PHASE_OUTGOING + DAMAGE_FLAG_PHASE_INCOMING
endmethod
private static method onDamagedEvent takes nothing returns nothing
local integer i = index
local real curHP = 0.0
set incomingDmgA[index] = GetEventDamage()
set pDmgA[i] = incomingDmgA[i]
// Ensure that calcFactor does not divide by 0.0
// To make it more safe, the equal comparison
// operation was used instead.
if (not (outgoingDmgA[i] == 0.0)) then
set calcFactorA[i] = pDmgA[i] / outgoingDmgA[i]
else
set calcFactorA[i] = 1.0
endif
set dreamFlags = DAMAGE_FLAG_WILL_BE_CODE
if (BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_SUPPRESS_EVENTS) == 0) then
call MODIFIER_INCOMING.run()
endif
set dmgFlagsA[i] = dmgFlagsA[i] + DAMAGE_FLAG_LOCK_DAMAGE
if (BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_SUPPRESS_EVENTS) == 0) then
call ON_DAMAGE.run()
endif
set curHP = GetWidgetLife(targetA[i])
set pFinalHPA[i] = curHP - pDmgA[i]
if ((curHP - pDmgA[i] <= 0.406) and /*
*/ (BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_SUPPRESS_EVENTS) == 0)) then
call ON_LETHAL_DAMAGE.run()
endif
set pDmgA[i] = GetWidgetLife(targetA[i]) - pFinalHPA[i]
call BlzSetEventDamage(pDmgA[i])
set dreamFlags = BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_WILL_BE_GAME + DAMAGE_FLAG_WILL_BE_CODE)
// Check if the unit is dead before proceeding
// with the after damage event.
if (not UnitAlive(targetA[i])) then
return
endif
set dmgFlagsA[i] = dmgFlagsA[i] - DAMAGE_FLAG_PHASE_INCOMING + DAMAGE_FLAG_PHASE_RECEIVED
set detectorA[i] = CreateTrigger()
set curHP = RMaxBJ(GetWidgetLife(targetA[i]) - pDmgA[i], 0.406)
set detectorMap.integer[GetHandleId(detectorA[i])] = i
call TriggerAddCondition(detectorA[i], Condition(onInflictDamage))
call TriggerRegisterUnitStateEvent(detectorA[i], targetA[i], UNIT_STATE_LIFE, LESS_THAN, curHP)
call TriggerRegisterUnitStateEvent(detectorA[i], targetA[i], UNIT_STATE_LIFE, GREATER_THAN, curHP)
endmethod
private static method onInflictEvent takes nothing returns nothing
local integer i = detectorMap.integer[GetHandleId(GetTriggeringTrigger())]
local integer pIndex = index
if (i <= 0) then
return
endif
set index = i
set dreamFlags = DAMAGE_FLAG_WILL_BE_CODE
if (BlzBitAnd(dmgFlagsA[i], DAMAGE_FLAG_SUPPRESS_EVENTS) == 0) then
call AFTER_DAMAGE.run()
endif
if (i <= 1) then
set dreamFlags = DAMAGE_FLAG_WILL_BE_GAME
else
set dreamFlags = BlzBitAnd(dmgFlagsA[i - 1], DAMAGE_FLAG_WILL_BE_GAME + DAMAGE_FLAG_WILL_BE_CODE)
endif
set index = pIndex
set dmgFlagsA[i] = dmgFlagsA[i] - DAMAGE_FLAG_PHASE_RECEIVED
call removeDamageInfo(i)
if (i == index) then
set index = index - 1
endif
endmethod
private static method onEventHandle takes nothing returns nothing
local eventid eventID = GetTriggerEventId()
if (eventID == EVENT_PLAYER_UNIT_DAMAGING) then
call thistype.onDamagingEvent()
else
call thistype.onDamagedEvent()
endif
endmethod
// ========================================================= //
// Initialization API //
// ========================================================= //
private static method initVars takes nothing returns nothing
set onCleanupInstances = function thistype.onCleanup
set onInflictDamage = function thistype.onInflictEvent
set cleanupTimer = CreateTimer()
set detectorMap = Table.create()
endmethod
private static method initEvents takes nothing returns nothing
local trigger trig = CreateTrigger()
local integer i = 0
local player p = null
call TriggerAddCondition(trig, Condition(function thistype.onEventHandle))
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(trig, p, EVENT_PLAYER_UNIT_DAMAGING, null)
call TriggerRegisterPlayerUnitEvent(trig, p, EVENT_PLAYER_UNIT_DAMAGED, null)
set i = i + 1
endloop
endmethod
private static method init takes nothing returns nothing
call initVars()
call initEvents()
endmethod
implement Init
endstruct
endlibrary
library DNCycle requires EventListener, Init /*
--------------------------
|
| DNCycle
|
|--------------------------
|
| API:
|
| class DNCycle {
| =======================
| readonly static EventListener ON_DAY
| - EventListener that triggers on the onset
| of dawn.
|
| readonly static EventListener ON_NIGHT
| - EventListener that triggers on the onset
| of dusk.
|
| readonly static boolean isDay
| - A flag telling us if it is day or night
| if one can't be bothered to use the
| GetFloatGameState native.
| =======================
}
*/
struct DNCycle extends array
readonly static constant real DAY_TIME = 6.00
readonly static constant real NIGHT_TIME = 18.00
readonly static EventListener ON_DAY = 0
readonly static EventListener ON_NIGHT = 0
readonly static boolean isDay = false
private static method initVars takes nothing returns nothing
set ON_DAY = EventListener.create()
set ON_NIGHT = EventListener.create()
endmethod
private static method onTimeChange takes nothing returns nothing
local real time = R2I(GetFloatGameState(GAME_STATE_TIME_OF_DAY) + 0.5)
local boolean daytime = ((time >= DAY_TIME) and (time < NIGHT_TIME))
if (isDay == daytime) then
return
endif
set isDay = daytime
if (isDay) then
call ON_DAY.run()
else
call ON_NIGHT.run()
endif
endmethod
private static method initEvents takes nothing returns nothing
local trigger dncTrig = CreateTrigger()
call TriggerRegisterGameStateEvent(dncTrig, GAME_STATE_TIME_OF_DAY, LESS_THAN_OR_EQUAL, DAY_TIME)
call TriggerRegisterGameStateEvent(dncTrig, GAME_STATE_TIME_OF_DAY, GREATER_THAN_OR_EQUAL, DAY_TIME)
call TriggerRegisterGameStateEvent(dncTrig, GAME_STATE_TIME_OF_DAY, GREATER_THAN_OR_EQUAL, NIGHT_TIME)
call TriggerAddCondition(dncTrig, Condition(function thistype.onTimeChange))
endmethod
private static method init takes nothing returns nothing
call thistype.initVars()
call thistype.initEvents()
endmethod
implement Init
endstruct
endlibrary
library StructureEnterLeave requires /*
--------------------------------------
*/ UnitDex, EventListener, Init /*
--------------------------------------
------------------------------
*/ Table, UnitAuxEvents /*
------------------------------
----------------------------------------------------------------
|
| StructureEnterLeave
| - v.1.1.0
|
|----------------------------------------------------------------
|
| A snippet that handles the moments when a structure is placed,
| constructed, or destroyed. The underlying system assumes that
| the structure is already available for use when its own ENTER
| event runs, hence why it cannot fully rely on the events provided
| by UnitDex.
|
|----------------------------------------------------------------
|
| API:
|
| class StructureHandler {
| ===============================================
| readonly static EventListener ON_ENTER
| - Runs when a structure has finished construction
| or is created via CreateUnit.
|
| readonly static EventListener ON_LEAVE
| - Runs when a structure is removed via destruction
| or RemoveUnit.
|
| readonly static unit structure
| - The current affected building in either event.
|
| ===============================================
| -----------------------
| | Added in v.1.1.0 |
| -----------------------
| static method isConstructing(unit whichStructure) -> bool
| - Returns true if the unit is still being constructed.
| }
----------------------------------------------------------------
*/
struct StructureHandler extends array
readonly static EventListener ON_ENTER = 0
readonly static EventListener ON_START = 0
readonly static EventListener ON_LEAVE = 0
readonly static EventListener ON_CANCEL = 0
readonly static EventListener ON_DEATH = 0
private static Table constructMap = 0
readonly static unit structure = null
private static method throwEvent takes unit whichUnit, EventListener whichEvent returns nothing
local unit prevStruct = structure
set structure = whichUnit
call whichEvent.run()
set structure = prevStruct
set prevStruct = null
endmethod
// ====================================================
// Event callback functions.
// ====================================================
private static method onConstructHandler takes nothing returns nothing
local eventid evID = GetTriggerEventId()
local integer unitHandle = 0
local unit building = GetTriggerUnit()
set unitHandle = GetHandleId(building)
if (evID == EVENT_PLAYER_UNIT_CONSTRUCT_START) then
set constructMap.boolean[unitHandle] = true
call thistype.throwEvent(building, ON_START)
else
set constructMap.boolean[unitHandle] = false
call thistype.throwEvent(building, ON_ENTER)
endif
set building = null
endmethod
private static method onStructureEnter takes nothing returns nothing
local unit building = GetIndexedUnit()
local integer unitHandle = GetHandleId(building)
if (IsUnitType(building, UNIT_TYPE_STRUCTURE)) and /*
*/ (not constructMap.boolean.has(unitHandle)) then
// Building was placed via CreateUnit
set constructMap.boolean[unitHandle] = false
call thistype.throwEvent(building, ON_ENTER)
endif
set building = null
endmethod
private static method onStructureLeave takes nothing returns nothing
local unit building = GetIndexedUnit()
local integer unitHandle = GetHandleId(building)
if (constructMap.boolean.has(unitHandle)) then
// Building was detected. Throw event.
call constructMap.boolean.remove(unitHandle)
call thistype.throwEvent(building, ON_LEAVE)
endif
set building = null
endmethod
private static method onCancelHandler takes nothing returns nothing
local eventid evID = GetTriggerEventId()
local unit building = GetTriggerUnit()
local integer unitHandle = GetHandleId(building)
set constructMap.boolean[unitHandle] = false
call thistype.throwEvent(building, ON_CANCEL)
set building = null
endmethod
private static method onStructureDeath takes nothing returns nothing
// Don't throw an event for units. Reserved for structures.
if (not constructMap.boolean.has(GetHandleId(UnitAuxHandler.unit))) then
return
endif
call thistype.throwEvent(UnitAuxHandler.unit, ON_DEATH)
endmethod
// ====================================================
// Initializing functions.
// ====================================================
private static method initVars takes nothing returns nothing
set ON_ENTER = EventListener.create()
set ON_START = EventListener.create()
set ON_LEAVE = EventListener.create()
set ON_DEATH = EventListener.create()
set ON_CANCEL = EventListener.create()
set constructMap = Table.create()
endmethod
private static method initEvents takes nothing returns nothing
local trigger trig = CreateTrigger()
local trigger cancelTrig = CreateTrigger()
call OnUnitIndex(function thistype.onStructureEnter)
call OnUnitDeindex(function thistype.onStructureLeave)
call UnitAuxHandler.ON_DEATH.register(function thistype.onStructureDeath)
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_CONSTRUCT_START)
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
call TriggerRegisterAnyUnitEventBJ(cancelTrig, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL)
call TriggerAddCondition(trig, Condition(function thistype.onConstructHandler))
call TriggerAddCondition(cancelTrig, Condition(function thistype.onCancelHandler))
endmethod
private static method init takes nothing returns nothing
call thistype.initVars()
call thistype.initEvents()
endmethod
implement Init
endstruct
endlibrary
library CBuff requires /*
--------------
*/ Table /*
--------------
--------------
*/ ListT /*
--------------
--------------
*/ Alloc /*
--------------
------------------
*/ UnitDex /*
------------------
----------------------
*/ GTimer /*
----------------------
---------------------------------------------------------------
|
| CBuff
| - MyPad
|---------------------------------------------------------------
|
| A library that makes handling custom buffs relatively easy
| with much of the groundwork automated by the system.
|
| All metadata information regarding the buff itself is handled
| by the CustomBuff class, such as stacking, unstacking, buff
| monitoring, static interval ticks, dynamic interval ticks,
| moment of buff inclusion, and removal.
|
| On the other hand, all in-game processes such as timed duration
| of buffs are handled by the Buff class.
|
|---------------------------------------------------------------
|
| API:
|
| class CustomBuff {
| ================================================================
| static method register(int abilID, int buffID) -> CustomBuff
| - Creates a new Custom Buff instance.
| - It is recommended to do this at the start of the game,
| since these objects are permanent.
| - One instance per class is enough for your needs.
|
| method setIntervalListener(real interval, code callback)
| - Tells the system to run the specified callback function
| every interval seconds, as long as the target unit
| still has the buff.
|
| method setStackIntervalListener(real interval, code callback)
| - Tells the system to run the specified callback function
| every interval seconds for each stack added.
| - The timer for this starts at the moment a stack is added
| and ends when the stack is removed.
|
| method setAddListener(code callback)
| - Runs at the moment the buff is added to the unit.
|
| method setStackListener(code callback)
| - Runs at the moment a stack of the buff is added
| to the unit.
| - Runs after setAddListener if the buff is newly
| added to the unit.
|
| method setStackListener(code callback)
| - Runs at the moment a stack of the buff is added
| to the unit.
| - Runs after setAddListener if the buff is newly
| added to the unit.
|
| method setUnstackListener(code callback)
| - Runs at the moment a stack of the buff is removed
| from the unit.
| - Runs before setRemoveListener if the buff is removed
| from the unit.
|
| method setMonitorListener(code callback)
| - Runs UPDATE_TICK (10) times every second while the buff
| is still present on the unit.
| ================================================================
| }
|
| class Buff {
| ================================================================
| -----------
| | Methods: |
| -----------
| static method apply(unit whichUnit, CustomBuff whichBuff)
| - Applies a new Buff instance based on the CustomBuff
| object.
|
| static method applyTimed(unit whichUnit, CustomBuff whichBuff, real dur)
| - Applies a timed new Buff instance based on the CustomBuff
| object. Expires after dur seconds.
| - Setting dur to a negative value will make this behave
| as apply.
|
| static method has(unit whichUnit, CustomBuff whichBuff) -> bool
| - Checks if unit has a Buff instance based on the CustomBuff.
|
| -----------
| | Members: |
| -----------
| readonly integer stack
| - The number of stacks a Buff currently has.
| integer data
| - Misc data associated with the Buff.
|
| readonly unit unit
| - The affected unit of a Buff.
| readonly CustomBuff cbuff
| - The CustomBuff object the Buff is based on.
|
| readonly static Buff current
| - The affected Buff instance in every callback.
|
| readonly static integer prevStackCount
| - The amount of stacks the current Buff had
| before the event.
| - Only meaningful in stack and unstack events.
|
| readonly static integer stackIncrement
| - The change in the amount of stacks from the
| previous amount to the current amount.
| - Only meaningful in stack and unstack events.
|
| readonly static TimedBuffData lastData
| readonly static TimedBuffData curData
| - The timed buff data associated with the Buff,
| which holds additional parameters.
| ---------------
| | Parameters: |
| ---------------
| integer data
| - Custom data associated with the TimedBuffData.
| readonly integer stackCount
| - The number of times the TimedBuffData object
| has triggered the stackInterval event.
| readonly Buff buff
| - A relay pointer referring to the current
| Buff.
| ----------------------------------------------------
| - Only meaningful in the stack, unstack, interval
| and stack interval events.
| ================================================================
| }
|
| module CustomBuffHandler {
| interface static method onBuffAdd()
| interface static method onBuffStack()
| interface static method onBuffUnstack()
| interface static method onBuffMonitor()
| interface static method onBuffTick()
| interface static method onBuffStackTick()
| interface static method onBuffRemove()
| - Optional methods to implement in your class.
|
| static method DEBUFF_ABIL_ID() -> integer
| - The base ability ID for the included CustomBuff object.
|
| static method DEBUFF_BUFF_ID() -> integer
| - The buff ID the included CustomBuff object.
|
| stub static method STATIC_INTERVAL() -> real
| - The interval to be used by onBuffTick().
| - Default of 1.0
|
| stub static method STACK_INTERVAL() -> real
| - The interval to be used by onBuffStackTick().
| - Default of 1.0
|
| static method applyBuff(unit whichUnit, real dur) -> Buff
| - Applies a Buff instance based on the included CustomBuff object.
|
| static method has(unit whichUnit) -> boolean
| - Checks if the unit has a Buff based on the CustomBuff object.
| }
|
---------------------------------------------------------------
*/
native UnitAlive takes unit id returns boolean
globals
private integer codeflag = 0
private constant integer SYSTEM_FLAG = 1
endglobals
// ===================================
// A class that handles
// the bundle of ability IDs
// along with their respective
// buff IDs
// ====================================
struct CustomBuff extends array
implement Alloc
// Response mask enums
readonly static constant integer MASK_INTERVAL = 1
readonly static constant integer MASK_REMOVAL = 2
readonly static constant integer MASK_ADD = 4
readonly static constant integer MASK_MONITOR = 8
readonly static constant integer MASK_STACK = 16
readonly static constant integer MASK_UNSTACK = 32
readonly static constant integer MASK_STACK_INTERVAL = 64
// Class members
private static Table abilMap = 0
// Primary info
readonly integer abilID
readonly integer buffID
// Buff response mask
readonly integer respMask
// Tick callback info
private EventResponder intervalResp
private EventResponder stackIntervalResp
readonly real interval
readonly real stackInterval
// Add callback info
private EventResponder addResp
// Remove callback info
private EventResponder removeResp
// Monitor callback info
private EventResponder monitorResp
// Stack callback info
private EventResponder stackResp
private EventResponder unstackResp
method setIntervalListener takes real interval, code callback returns nothing
set this.respMask = BlzBitOr(this.respMask, MASK_INTERVAL)
set this.interval = interval
call this.intervalResp.change(callback)
endmethod
method setStackIntervalListener takes real interval, code callback returns nothing
set this.respMask = BlzBitOr(this.respMask, MASK_STACK_INTERVAL)
set this.stackInterval = interval
call this.stackIntervalResp.change(callback)
endmethod
method setAddListener takes code callback returns nothing
set this.respMask = BlzBitOr(this.respMask, MASK_ADD)
call this.addResp.change(callback)
endmethod
method setRemoveListener takes code callback returns nothing
set this.respMask = BlzBitOr(this.respMask, MASK_REMOVAL)
call this.removeResp.change(callback)
endmethod
method setMonitorListener takes code callback returns nothing
set this.respMask = BlzBitOr(this.respMask, MASK_MONITOR)
call this.monitorResp.change(callback)
endmethod
method setStackListener takes code callback returns nothing
set this.respMask = BlzBitOr(this.respMask, MASK_STACK)
call this.stackResp.change(callback)
endmethod
method setUnstackListener takes code callback returns nothing
set this.respMask = BlzBitOr(this.respMask, MASK_UNSTACK)
call this.unstackResp.change(callback)
endmethod
// ====================================================
// Technically private methods, since they're only called by
// the CustomBuffHandler class below.
method runIntervalListener takes nothing returns nothing
if (BlzBitAnd(codeflag, SYSTEM_FLAG) == 0) then
return
endif
call this.intervalResp.run()
endmethod
method runAddListener takes nothing returns nothing
if (BlzBitAnd(codeflag, SYSTEM_FLAG) == 0) then
return
endif
call this.addResp.run()
endmethod
method runRemoveListener takes nothing returns nothing
if (BlzBitAnd(codeflag, SYSTEM_FLAG) == 0) then
return
endif
call this.removeResp.run()
endmethod
method runMonitorListener takes nothing returns nothing
if (BlzBitAnd(codeflag, SYSTEM_FLAG) == 0) then
return
endif
call this.monitorResp.run()
endmethod
method runStackListener takes nothing returns nothing
if (BlzBitAnd(codeflag, SYSTEM_FLAG) == 0) then
return
endif
call this.stackResp.run()
endmethod
method runStackIntervalListener takes nothing returns nothing
if (BlzBitAnd(codeflag, SYSTEM_FLAG) == 0) then
return
endif
call this.stackIntervalResp.run()
endmethod
method runUnstackListener takes nothing returns nothing
if (BlzBitAnd(codeflag, SYSTEM_FLAG) == 0) then
return
endif
call this.unstackResp.run()
endmethod
method isMaskFlagged takes integer mask returns boolean
return BlzBitAnd(this.respMask, mask) != 0
endmethod
static method register takes integer abilID, integer buffID returns thistype
local thistype this = abilMap[abilID]
if (this == 0) then
set this = thistype.allocate()
set abilMap[abilID] = this
set this.abilID = abilID
set this.buffID = buffID
set this.stackIntervalResp = EventResponder.create(null)
set this.intervalResp = EventResponder.create(null)
set this.addResp = EventResponder.create(null)
set this.removeResp = EventResponder.create(null)
set this.monitorResp = EventResponder.create(null)
set this.stackResp = EventResponder.create(null)
set this.unstackResp = EventResponder.create(null)
endif
return this
endmethod
// ====================================================
private static method init takes nothing returns nothing
set abilMap = Table.create()
endmethod
implement Init
endstruct
//! runtextmacro DEFINE_LIST("private", "BuffList", "integer")
//! runtextmacro DEFINE_LIST("private", "UnitList", "unit")
private struct BuffData extends array
// Handles unit to CustomBuff maps
readonly Table unitHashMap
readonly BuffList buffList
integer monitorPtr
static method operator [] takes unit whichUnit returns thistype
return thistype(GetUnitId(whichUnit))
endmethod
static method register takes unit whichUnit returns thistype
local thistype this = GetUnitId(whichUnit)
if (this.buffList != 0) then
return this
endif
set this.buffList = BuffList.create()
set this.unitHashMap = Table.create()
return this
endmethod
method unregister takes nothing returns nothing
call this.unitHashMap.destroy()
call this.buffList.destroy()
set this.monitorPtr = 0
set this.buffList = 0
set this.unitHashMap = 0
endmethod
endstruct
struct TimedBuffData extends array
implement Alloc
readonly timer stackedTimer
readonly timer timer
readonly Buff buff
integer data
integer stackCount
IntegerListItem ptr
method destroy takes nothing returns nothing
call ReleaseTimer(this.stackedTimer)
call ReleaseTimer(this.timer)
set this.timer = null
set this.buff = 0
set this.data = 0
set this.ptr = 0
call this.deallocate()
endmethod
static method create takes Buff source, integer data returns thistype
local thistype this = thistype.allocate()
set this.buff = source
set this.data = data
set this.timer = NewTimerEx(this)
set this.stackedTimer = NewTimerEx(this)
set this.ptr = 0
return this
endmethod
endstruct
struct Buff extends array
implement Alloc
private static code onInstanceTimer = null
private static code onTimedRemove = null
private static code onStackInterval = null
private static UnitList monitorGroup = 0
private static EventResponder monitorResp = 0
readonly static Buff current = 0
readonly static CustomBuff currentType = 0
readonly static integer prevStackCount = 0
readonly static integer stackIncrement = 0
readonly static TimedBuffData lastData = 0
private IntegerList timedBuffList
readonly CustomBuff cbuff
readonly unit unit
readonly BuffListItem buffListPtr
readonly integer stack
integer data
private timer staticTimer
// ====================================================
// Convenience operators
// ====================================================
static method operator curData takes nothing returns TimedBuffData
return lastData
endmethod
// ====================================================
// Code flag manipulators
// ====================================================
private static method setCodeFlag takes integer mask returns nothing
set codeflag = BlzBitOr(codeflag, mask)
endmethod
private static method unsetCodeFlag takes integer mask returns nothing
set codeflag = BlzBitAnd(codeflag, -mask - 1)
endmethod
private static method isCodeFlagSet takes integer mask returns boolean
return BlzBitAnd(codeflag, mask) != 0
endmethod
// ====================================================
// Monitor and Release methods
// ====================================================
private static method monitorUnit takes unit whichUnit returns nothing
local BuffData temp = BuffData[whichUnit]
if (temp.monitorPtr != 0) then
return
endif
set temp.monitorPtr = monitorGroup.push(whichUnit).last
if (monitorGroup.size() == 1) then
call GTimer[UPDATE_TICK].requestCallback(monitorResp)
endif
endmethod
private static method releaseUnit takes unit whichUnit returns nothing
local BuffData temp = BuffData[whichUnit]
if (temp.monitorPtr == 0) then
return
endif
call monitorGroup.erase(temp.monitorPtr)
call temp.unregister()
if (monitorGroup.empty()) then
call GTimer[UPDATE_TICK].releaseCallback(monitorResp)
endif
endmethod
// ====================================================
// Accessible API
// ====================================================
private static method invokeCallback takes Buff this, CustomBuff whichBuff, integer callbackType returns nothing
local Buff prev = current
local CustomBuff prevType = currentType
local integer prevCFlag = codeflag
set current = this
set currentType = whichBuff
// switch (callbackType) {case: ...}
call Buff.setCodeFlag(SYSTEM_FLAG)
if (callbackType == CustomBuff.MASK_INTERVAL) then
call whichBuff.runIntervalListener()
elseif (callbackType == CustomBuff.MASK_STACK_INTERVAL) then
call whichBuff.runStackIntervalListener()
elseif (callbackType == CustomBuff.MASK_ADD) then
call whichBuff.runAddListener()
elseif (callbackType == CustomBuff.MASK_MONITOR) then
call whichBuff.runMonitorListener()
elseif (callbackType == CustomBuff.MASK_REMOVAL) then
call whichBuff.runRemoveListener()
elseif (callbackType == CustomBuff.MASK_STACK) then
call whichBuff.runStackListener()
elseif (callbackType == CustomBuff.MASK_UNSTACK) then
call whichBuff.runUnstackListener()
endif
set codeflag = prevCFlag
set currentType = prevType
set current = prev
endmethod
static method has takes unit whichUnit, CustomBuff whichBuff returns boolean
return (BuffData[whichUnit].unitHashMap != 0) and /*
*/ (BuffData[whichUnit].unitHashMap.has(whichBuff))
endmethod
static method applyTimed takes unit whichUnit, CustomBuff whichBuff, real dur returns Buff
local BuffData temp = BuffData.register(whichUnit)
local Buff this = temp.unitHashMap[whichBuff]
local TimedBuffData prevData = lastData
local integer stackQt = prevStackCount
if (this == 0) then
set this = Buff.allocate()
set this.cbuff = whichBuff
set this.unit = whichUnit
set this.buffListPtr = temp.buffList.push(this).last
set this.stack = 0
set this.timedBuffList = IntegerList.create()
if (whichBuff.isMaskFlagged(CustomBuff.MASK_ADD)) then
call Buff.invokeCallback(this, whichBuff, CustomBuff.MASK_ADD)
endif
if (whichBuff.isMaskFlagged(CustomBuff.MASK_INTERVAL)) then
set this.staticTimer = NewTimerEx(this)
call TimerStart(this.staticTimer, whichBuff.interval, true, Buff.onInstanceTimer)
endif
call UnitAddAbility(whichUnit, whichBuff.abilID)
call UnitMakeAbilityPermanent(whichUnit, true, whichBuff.abilID)
call Buff.monitorUnit(whichUnit)
set temp.unitHashMap[whichBuff] = this
endif
set lastData = 0
set stackIncrement = 1
set prevStackCount = this.stack
set this.stack = this.stack + 1
// If duration is <= 0.0, it is considered permanent.
if (dur > 0.0) then
set lastData = TimedBuffData.create(this, 0)
set lastData.ptr = this.timedBuffList.push(lastData).last
if (whichBuff.isMaskFlagged(CustomBuff.MASK_STACK_INTERVAL)) then
call TimerStart(lastData.stackedTimer, whichBuff.stackInterval, true, Buff.onStackInterval)
endif
call TimerStart(lastData.timer, dur, false, Buff.onTimedRemove)
endif
if (whichBuff.isMaskFlagged(CustomBuff.MASK_STACK)) then
call Buff.invokeCallback(this, whichBuff, CustomBuff.MASK_STACK)
endif
set prevStackCount = stackQt
set lastData = prevData
return this
endmethod
static method apply takes unit whichUnit, CustomBuff whichBuff returns Buff
return Buff.applyTimed(whichUnit, whichBuff, -1.0)
endmethod
private method removeEx takes integer stacks returns nothing
local BuffData temp = 0
local CustomBuff lastBuff = 0
local integer stackQt = 0
local TimedBuffData prevData = 0
// Check if object is already destroyed.
if (this.cbuff == 0) then
return
endif
set stackQt = prevStackCount
set stackIncrement = -stacks
set prevStackCount = this.stack
set this.stack = IMaxBJ(this.stack - stacks, 0)
if (this.cbuff.isMaskFlagged(CustomBuff.MASK_UNSTACK)) then
call Buff.invokeCallback(this, this.cbuff, CustomBuff.MASK_UNSTACK)
endif
set prevStackCount = stackQt
if (this.stack > 0) then
return
endif
// ======================================
// Proceed with destruction
// ======================================
set temp = BuffData[this.unit]
set lastBuff = this.cbuff
set this.cbuff = 0
call temp.buffList.erase(this.buffListPtr)
call temp.unitHashMap.remove(lastBuff)
// ======================================
// Clean up all associated timed
// instances.
// ======================================
set prevData = lastData
loop
exitwhen this.timedBuffList.empty()
set lastData = this.timedBuffList.last.data
call this.timedBuffList.erase(lastData.ptr)
call lastData.destroy()
endloop
set lastData = prevData
call this.timedBuffList.destroy()
if (this.staticTimer != null) then
call ReleaseTimer(this.staticTimer)
endif
if (lastBuff.isMaskFlagged(CustomBuff.MASK_REMOVAL)) then
call UnitRemoveAbility(this.unit, lastBuff.buffID)
call UnitRemoveAbility(this.unit, lastBuff.abilID)
call Buff.invokeCallback(this, lastBuff, CustomBuff.MASK_REMOVAL)
endif
set this.staticTimer = null
set this.unit = null
set this.stack = 0
set this.data = 0
set this.buffListPtr = 0
set this.timedBuffList = 0
call this.deallocate()
endmethod
method remove takes nothing returns nothing
call this.removeEx(1)
endmethod
// ====================================================
private static unit tempUnit = null
private static method onMonitorBuffs takes nothing returns nothing
local UnitListItem i = monitorGroup.first
local BuffData temp = 0
local BuffListItem iter = 0
local boolean isDead = false
loop
exitwhen (i == 0)
set tempUnit = i.data
set i = i.next
// ===================================
set isDead = (not UnitAlive(tempUnit))
set temp = BuffData[tempUnit]
set iter = temp.buffList.first
loop
exitwhen (iter == 0)
set current = Buff(iter.data)
set iter = iter.next
// ===================================
if (isDead) then
// Dispel buff.
call current.removeEx(current.stack)
elseif (current.cbuff.isMaskFlagged(CustomBuff.MASK_MONITOR)) then
// Monitor the situation.
call Buff.invokeCallback(current, current.cbuff, CustomBuff.MASK_MONITOR)
endif
endloop
// ===================================
// Remove this unit from the list if empty.
if (temp.buffList.empty()) then
call thistype.releaseUnit(tempUnit)
endif
endloop
endmethod
private static method onUnitLeave takes nothing returns nothing
local unit prevUnit = GetIndexedUnit()
local BuffData temp = BuffData[prevUnit]
local BuffListItem iter = 0
if (temp.monitorPtr == 0) then
set prevUnit = null
return
endif
set iter = temp.buffList.first
loop
exitwhen (iter == 0)
set current = Buff(iter.data)
set iter = iter.next
// ===================================
// Dispel buff.
call current.removeEx(current.stack)
endloop
// ===================================
// Remove this unit from the list if empty.
if (temp.buffList.empty()) then
call Buff.releaseUnit(tempUnit)
else
// Hopefully, this doesn't happen.
// Otherwise, it's nice to include this
// when it does occur.
call BJDebugMsg("CBuff {EVENT_UNIT_LEAVE} >> |cffff4040Error!|r - Buff list isn't empty!")
endif
set prevUnit = null
endmethod
private static method onInstanceExpire takes nothing returns nothing
local Buff this = GetTimerData(GetExpiredTimer())
call Buff.invokeCallback(this, this.cbuff, CustomBuff.MASK_INTERVAL)
endmethod
private static method onTimedExpire takes nothing returns nothing
local TimedBuffData data = GetTimerData(GetExpiredTimer())
local TimedBuffData prevData = lastData
local Buff this = data.buff
set lastData = data
call this.timedBuffList.erase(data.ptr)
call data.destroy()
call this.removeEx(1)
set lastData = prevData
endmethod
private static method onStackIntervalExpire takes nothing returns nothing
local TimedBuffData data = GetTimerData(GetExpiredTimer())
local TimedBuffData prevData = lastData
local Buff this = data.buff
set data.stackCount = data.stackCount + 1
set lastData = data
call Buff.invokeCallback(this, this.cbuff, CustomBuff.MASK_INTERVAL)
set lastData = prevData
endmethod
// ====================================================
private static method init takes nothing returns nothing
set monitorGroup = UnitList.create()
set monitorResp = GTimer.register(UPDATE_TICK, function Buff.onMonitorBuffs)
set onInstanceTimer = function Buff.onInstanceExpire
set onTimedRemove = function Buff.onTimedExpire
set onStackInterval = function Buff.onStackIntervalExpire
call OnUnitDeindex(function Buff.onUnitLeave)
endmethod
implement Init
endstruct
module CustomBuffHandler
private static CustomBuff base = 0
static if (not thistype.DEBUFF_ABIL_ID.exists) then
static method DEBUFF_ABIL_ID takes nothing returns integer
return 0
endmethod
endif
static if (not thistype.DEBUFF_BUFF_ID.exists) then
static method DEBUFF_BUFF_ID takes nothing returns integer
return 0
endmethod
endif
static if (not thistype.STATIC_INTERVAL.exists) then
static method STATIC_INTERVAL takes nothing returns real
return 1.0
endmethod
endif
static if (not thistype.STACK_INTERVAL.exists) then
static method STACK_INTERVAL takes nothing returns real
return 1.0
endmethod
endif
static method applyBuff takes unit whichUnit, real dur returns Buff
return Buff.applyTimed(whichUnit, base, dur)
endmethod
static method has takes unit whichUnit returns boolean
return Buff.has(whichUnit, base)
endmethod
private static method onInit takes nothing returns nothing
set base = CustomBuff.register(DEBUFF_ABIL_ID(), DEBUFF_BUFF_ID())
static if thistype.onBuffAdd.exists then
call base.setAddListener(function thistype.onBuffAdd)
endif
static if thistype.onBuffStack.exists then
call base.setStackListener(function thistype.onBuffStack)
endif
static if thistype.onBuffUnstack.exists then
call base.setUnstackListener(function thistype.onBuffUnstack)
endif
static if thistype.onBuffMonitor.exists then
call base.setMonitorListener(function thistype.onBuffMonitor)
endif
static if thistype.onBuffTick.exists then
call base.setIntervalListener(STATIC_INTERVAL(), function thistype.onBuffTick)
endif
static if thistype.onBuffStackTick.exists then
call base.setStackIntervalListener(STACK_INTERVAL(), function thistype.onBuffStackTick)
endif
static if thistype.onBuffRemove.exists then
call base.setRemoveListener(function thistype.onBuffRemove)
endif
endmethod
endmodule
endlibrary
library Stun requires CBuff
struct Stun extends array
private static effect array fx
private static method EFFECT_MODEL takes nothing returns string
return "Abilities\\Spells\\Human\\Thunderclap\\ThunderclapTarget.mdl"
endmethod
private static method EFFECT_ATTACHMENT takes nothing returns string
return "overhead"
endmethod
private static method DEBUFF_ABIL_ID takes nothing returns integer
return '!STN'
endmethod
private static method DEBUFF_BUFF_ID takes nothing returns integer
return '^STN'
endmethod
private static method onBuffRemove takes nothing returns nothing
local integer id = GetUnitId(Buff.current.unit)
call BlzPauseUnitEx(Buff.current.unit, false)
call DestroyEffect(fx[id])
set fx[id] = null
endmethod
private static method onBuffAdd takes nothing returns nothing
set fx[GetUnitId(Buff.current.unit)] = AddSpecialEffectTarget(EFFECT_MODEL(), Buff.current.unit, EFFECT_ATTACHMENT())
call BlzPauseUnitEx(Buff.current.unit, true)
endmethod
implement CustomBuffHandler
endstruct
endlibrary
library CustomRaceCore requires /*
----------------------
*/ Init, /*
----------------------
----------------------
*/ optional Table /*
----------------------
- Bribe
- link: https://www.hiveworkshop.com/threads/snippet-new-table.188084/
---------------------------------------------------------------------------------------
|
*/
struct CustomRace
static if not LIBRARY_Table then
private static hashtable ht = InitHashtable()
private static integer gHeroKey = 0
private static integer gHallKey = 0
else
private static Table gHeroMap = 0
private static Table gHallMap = 0
endif
readonly static integer array globalHeroID
readonly static integer array globalHallID
readonly static integer array raceFactionCount
readonly static thistype array humanFactionObject
readonly static thistype array orcFactionObject
readonly static thistype array undeadFactionObject
readonly static thistype array nightelfFactionObject
readonly string name
readonly string racePic
readonly string desc
readonly string playlist
readonly race baseRace
private trigger setupTrig
private trigger setupTrigAI
static if LIBRARY_Table then
private Table hallTable
private Table hallMap
private Table heroTable
private Table heroMap
private Table unitTable
private Table unitMap
private Table strcTable
private Table strcMap
else
private integer hallKey
private integer hallMapKey
private integer heroKey
private integer heroMapKey
private integer unitKey
private integer unitMapKey
private integer strcKey
private integer strcMapKey
endif
static if not LIBRARY_Table then
private static method generateKey takes nothing returns integer
call SaveInteger(ht, 0, 0, LoadInteger(ht, 0, 0) + 1)
return LoadInteger(ht, 0, 0)
endmethod
endif
// Only 4 races are actually available to the player.
private static method isValidRace takes race whichRace returns boolean
return GetHandleId(whichRace) < 5
endmethod
private static method updateFactionCount takes integer index, thistype this returns nothing
set raceFactionCount[index] = raceFactionCount[index] + 1
if index == 1 then
set humanFactionObject[raceFactionCount[index]] = this
elseif index == 2 then
set orcFactionObject[raceFactionCount[index]] = this
elseif index == 3 then
set undeadFactionObject[raceFactionCount[index]] = this
elseif index == 4 then
set nightelfFactionObject[raceFactionCount[index]] = this
endif
endmethod
static method getRaceFactionCount takes race whichRace returns integer
return raceFactionCount[GetHandleId(whichRace)]
endmethod
static method getRaceFaction takes race whichRace, integer index returns thistype
local integer id = GetHandleId(whichRace)
if (not thistype.isValidRace(whichRace)) then
return thistype(-1)
endif
if id == 1 then
return humanFactionObject[index]
elseif id == 2 then
return orcFactionObject[index]
elseif id == 3 then
return undeadFactionObject[index]
endif
return nightelfFactionObject[index]
endmethod
// destroy method is reserved to prevent bugs from occurring related
// to the creation of factions at runtime.
private method destroy takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60.00, /*
*/ "CustomRace.destroy >> Faction objects cannot be destroyed.")
endmethod
static method create takes race whichRace, string factionName returns thistype
local thistype this = 0
local integer index = GetHandleId(whichRace)
if not thistype.isValidRace(whichRace) then
return this
endif
set this = thistype.allocate()
set this.name = factionName
set this.baseRace = whichRace
// Setting this to empty string ensures that the faction display frame
// will not show by default.
set this.racePic = ""
// Basic info about the race done, now create a Table instance
static if LIBRARY_Table then
set this.hallTable = Table.create()
set this.hallMap = Table.create()
set this.heroTable = Table.create()
set this.heroMap = Table.create()
set this.unitTable = Table.create()
set this.unitMap = Table.create()
set this.strcTable = Table.create()
set this.strcMap = Table.create()
else
set this.hallKey = generateKey()
set this.hallMapKey = generateKey()
set this.heroKey = generateKey()
set this.heroMapKey = generateKey()
set this.unitKey = generateKey()
set this.unitMapKey = generateKey()
set this.strcKey = generateKey()
set this.strcMapKey = generateKey()
endif
// Update raceFactionCount and factionObject
call thistype.updateFactionCount(index, this)
return this
endmethod
//! textmacro CRCore_InstantiateStrings takes NAME
call GetObjectName($NAME$)
call BlzGetAbilityExtendedTooltip($NAME$, 0)
call BlzGetAbilityIcon($NAME$)
//! endtextmacro
method addUnit takes integer unitID returns nothing
//! runtextmacro CRCore_InstantiateStrings("unitID")
static if LIBRARY_Table then
if this.unitMap.has(unitID) then
return
endif
set this.unitTable.integer[0] = this.unitTable.integer[0] + 1
set this.unitTable[this.unitTable[0]] = unitID
set this.unitMap[unitID] = this.unitTable[0]
else
if HaveSavedInteger(ht, this.unitMapKey, unitID) then
return
endif
call SaveInteger(ht, this.unitKey, 0, LoadInteger(ht, this.unitKey, 0) + 1)
call SaveInteger(ht, this.unitKey, LoadInteger(ht, this.unitKey, 0), unitID)
call SaveInteger(ht, this.unitMapKey, unitID, LoadInteger(ht, this.unitKey, 0))
endif
endmethod
method addStructure takes integer strcID returns nothing
//! runtextmacro CRCore_InstantiateStrings("strcID")
static if LIBRARY_Table then
if this.strcMap.has(strcID) then
return
endif
set this.strcTable.integer[0] = this.strcTable.integer[0] + 1
set this.strcTable[this.strcTable[0]] = strcID
set this.strcMap[strcID] = this.strcTable[0]
else
if HaveSavedInteger(ht, this.strcMapKey, strcID) then
return
endif
call SaveInteger(ht, this.strcKey, 0, LoadInteger(ht, this.strcKey, 0) + 1)
call SaveInteger(ht, this.strcKey, LoadInteger(ht, this.strcKey, 0), strcID)
call SaveInteger(ht, this.strcMapKey, strcID, LoadInteger(ht, this.strcKey, 0))
endif
endmethod
// It is the responsibility of the user to provide the
// correct parameter for the heroIDs
method addHero takes integer heroID returns nothing
//! runtextmacro CRCore_InstantiateStrings("heroID")
static if LIBRARY_Table then
if this.heroMap.has(heroID) then
return
endif
set this.heroTable.integer[0] = this.heroTable.integer[0] + 1
set this.heroTable[this.heroTable[0]] = heroID
set this.heroMap[heroID] = this.heroTable[0]
if gHeroMap.has(heroID) then
return
endif
set globalHeroID[0] = globalHeroID[0] + 1
set globalHeroID[globalHeroID[0]] = heroID
set gHeroMap[heroID] = globalHeroID[0]
else
if HaveSavedInteger(ht, this.heroMapKey, heroID) then
return
endif
call SaveInteger(ht, this.heroKey, 0, LoadInteger(ht, this.heroKey, 0) + 1)
call SaveInteger(ht, this.heroKey, LoadInteger(ht, this.heroKey, 0), heroID)
call SaveInteger(ht, this.heroMapKey, heroID, LoadInteger(ht, this.heroKey, 0))
if HaveSavedInteger(ht, gHeroKey, heroID) then
return
endif
set globalHeroID[0] = globalHeroID[0] + 1
set globalHeroID[globalHeroID[0]] = heroID
call SaveInteger(ht, gHeroKey, heroID, globalHeroID[0])
endif
endmethod
method addHall takes integer hallID returns nothing
//! runtextmacro CRCore_InstantiateStrings("hallID")
static if LIBRARY_Table then
if this.hallMap.has(hallID) then
return
endif
set this.hallTable.integer[0] = this.hallTable.integer[0] + 1
set this.hallTable[this.hallTable[0]] = hallID
set this.hallMap[hallID] = this.hallTable[0]
if gHallMap.has(hallID) then
return
endif
set globalHallID[0] = globalHallID[0] + 1
set globalHallID[globalHallID[0]] = hallID
set gHallMap[hallID] = globalHallID[0]
else
if HaveSavedInteger(ht, this.hallMapKey, hallID) then
return
endif
call SaveInteger(ht, this.hallKey, 0, LoadInteger(ht, this.hallKey, 0) + 1)
call SaveInteger(ht, this.hallKey, LoadInteger(ht, this.hallKey, 0), hallID)
call SaveInteger(ht, this.hallMapKey, hallID, LoadInteger(ht, this.hallKey, 0))
if HaveSavedInteger(ht, gHallKey, hallID) then
return
endif
set globalHallID[0] = globalHallID[0] + 1
set globalHallID[globalHallID[0]] = hallID
call SaveInteger(ht, gHallKey, hallID, globalHallID[0])
endif
endmethod
//! textmacro CRCore_DEF_GETTER takes NAME, SUBNAME
method get$NAME$ takes integer index returns integer
static if LIBRARY_Table then
return this.$SUBNAME$Table[index]
else
return LoadInteger(ht, this.$SUBNAME$Key, index)
endif
endmethod
method get$NAME$MaxIndex takes nothing returns integer
static if LIBRARY_Table then
return this.$SUBNAME$Table.integer[0]
else
return LoadInteger(ht, this.$SUBNAME$Key, 0)
endif
endmethod
//! endtextmacro
//! runtextmacro CRCore_DEF_GETTER("Hero", "hero")
//! runtextmacro CRCore_DEF_GETTER("Hall", "hall")
//! runtextmacro CRCore_DEF_GETTER("Unit", "unit")
//! runtextmacro CRCore_DEF_GETTER("Structure", "strc")
method getRandomHero takes nothing returns integer
return this.getHero(GetRandomInt(1, this.getHeroMaxIndex()))
endmethod
static method getGlobalHeroMaxIndex takes nothing returns integer
return globalHeroID[0]
endmethod
static method getGlobalHallMaxIndex takes nothing returns integer
return globalHallID[0]
endmethod
static method getGlobalHero takes integer index returns integer
return globalHeroID[index]
endmethod
static method getGlobalHall takes integer index returns integer
return globalHallID[index]
endmethod
static method isGlobalHero takes integer heroID returns boolean
static if LIBRARY_Table then
return gHeroMap.has(heroID)
else
return HaveSavedInteger(ht, gHeroKey, heroID)
endif
endmethod
static method addGlobalHero takes integer heroID returns nothing
//! runtextmacro CRCore_InstantiateStrings("heroID")
static if LIBRARY_Table then
if gHeroMap.has(heroID) then
return
endif
set globalHeroID[0] = globalHeroID[0] + 1
set globalHeroID[globalHeroID[0]] = heroID
set gHeroMap[heroID] = globalHeroID[0]
else
if HaveSavedInteger(ht, gHeroKey, heroID) then
return
endif
set globalHeroID[0] = globalHeroID[0] + 1
set globalHeroID[globalHeroID[0]] = heroID
call SaveInteger(ht, gHeroKey, heroID, globalHeroID[0])
endif
endmethod
static method addGlobalHall takes integer hallID returns nothing
//! runtextmacro CRCore_InstantiateStrings("hallID")
static if LIBRARY_Table then
if gHeroMap.has(hallID) then
return
endif
set globalHeroID[0] = globalHeroID[0] + 1
set globalHeroID[globalHeroID[0]] = hallID
set gHeroMap[hallID] = globalHeroID[0]
else
if HaveSavedInteger(ht, gHeroKey, hallID) then
return
endif
set globalHeroID[0] = globalHeroID[0] + 1
set globalHeroID[globalHeroID[0]] = hallID
call SaveInteger(ht, gHeroKey, hallID, globalHeroID[0])
endif
endmethod
static method isKeyStructure takes integer hallID returns boolean
static if LIBRARY_Table then
return gHallMap.has(hallID)
else
return HaveSavedInteger(ht, gHallKey, hallID)
endif
endmethod
method defSetup takes code setupfunc returns nothing
if this.setupTrig != null then
call DestroyTrigger(this.setupTrig)
endif
set this.setupTrig = CreateTrigger()
call TriggerAddCondition(this.setupTrig, Condition(setupfunc))
endmethod
method defAISetup takes code setupfunc returns nothing
if this.setupTrigAI != null then
call DestroyTrigger(this.setupTrigAI)
endif
set this.setupTrigAI = CreateTrigger()
call TriggerAddCondition(this.setupTrigAI, Condition(setupfunc))
endmethod
method defRacePic takes string racePic returns nothing
set this.racePic = racePic
endmethod
method defDescription takes string desc returns nothing
set this.desc = desc
endmethod
method defName takes string name returns nothing
set this.name = name
endmethod
method defPlaylist takes string plist returns nothing
set this.playlist = plist
endmethod
method execSetup takes nothing returns nothing
call TriggerEvaluate(this.setupTrig)
endmethod
method execSetupAI takes nothing returns nothing
call TriggerEvaluate(this.setupTrigAI)
endmethod
private static method init takes nothing returns nothing
set raceFactionCount[GetHandleId(RACE_HUMAN)] = 0
set raceFactionCount[GetHandleId(RACE_ORC)] = 0
set raceFactionCount[GetHandleId(RACE_UNDEAD)] = 0
set raceFactionCount[GetHandleId(RACE_NIGHTELF)] = 0
static if LIBRARY_Table then
set gHeroMap = Table.create()
set gHallMap = Table.create()
else
set gHeroKey = generateKey()
set gHallKey = generateKey()
endif
endmethod
implement Init
endstruct
endlibrary
library CustomRaceUI requires /*
----------------------
*/ CustomRaceCore, /*
----------------------
----------------------
*/ Init, /*
----------------------
------------------------------
*/ optional FrameLoader /*
------------------------------
---------------------------------------------------------------------------
|
| CustomRaceUI
|
|---------------------------------------------------------------------------
|
| - function MainFrameInitiallyVisible() -> boolean
| - determines whether the main frame is visible at the start
| of the game. Must not be touched!
|
|---------------------------------------------------------------------------
|
| Configuration Section:
|
|---------------------------------------------------------------------------
|
| GetMainFrameCenterX() -> real
| GetMainFrameCenterY() -> real
| - Determines the position of the center of the main frame.
|
| GetTechtreeTooltipOffsetX() -> real
| GetTechtreeTooltipOffsetY() -> real
| - Determines the position of the leftmost lower edge of the
| tooltip frame.
|
| GetTechtreeChunkCount() -> integer
| - Determines the number of techtree chunks to generate.
| - An example of a techtree chunk may consist of units
| existing as part of the techtree of a certain faction.
|
| GetTechtreeChunkIconColumnMax() -> integer
| - Returns the maximum amount of icons per column
| within a given chunk.
| - o
| o --> 2
| GetTechtreeChunkIconRowMax() -> integer
| - Returns the maximum amount of icons per row
| within a given chunk.
|
| - o o o o o o --> 6
|
| InitChunkNames()
| - While not considered a traditionally configurable
| function, this function provides one with the
| means to edit the labels of each techtree chunk,
| albeit indirectly.
|
| GetMaxDisplayChoices() -> integer
| - The amount of choices for factions that are displayed at any given time.
| - NOTE: The actual maximum number of factions is practically unlimited
| and should not be confused with the value here.
| GetChoiceSizeOffset() -> real
| - The difference between the total height of the choices and the height
| of their container frame.
|
| GetBarModel() -> string
| - The model art for the "bar" frame (which is actually a backdrop frame)
| (behind the scenes.)
| GetBarTransparency() -> real
| - The transparency of the "bar" frame. Accepts values from 0.0 to 1.0.
| - A transparency of 1 will render the frame invisible. A transparency
| of 0 will render the frame completely opaque.
|
|---------------------------------------------------------------------------
|
| CustomRaceInterface method guide:
|
|---------------------------------------------------------------------------
|
| Although not really meant for public usage, the public methods
| "available" to the user are written to make certain interactions
| with some of the elements in the main frame as direct and easy
| as possible. In that regard, expect performance to be sacrificed
| a bit.
|
| If opting for speed, one can directly access most of the elements
| and make changes from there. Performance may improve, but the
| maintainability of the code might be negatively affected.
|
| All of the following methods are static:
|
|---------------------------------------------------------------------------
|
| Getters:
| method getTechtreeIcon(int techChunk, int row, int col) -> framehandle
| - returns the techtree icon at the specified location
| and chunk.
|
| method getTechtreeIconRaw(int index) -> framehandle
| - returns the techtree icon at the specified index.
| Be wary, as this is not protected from out of bounds
| results.
|
| method getChoiceButton(int index) -> framehandle
| - returns one of the choice buttons (found within the
| choice container frame at the lower left portion.)
|
| method getTechtreeArrow(int techChunk, bool isUp) -> framehandle
| - returns one of the two techtree arrows associated
| with the requested chunk.
|
| method getTechtreeArrowID(framehandle arrow) -> int
| - returns the index of the arrow handle.
|
| method getChoiceArrow(bool isUp) -> framehandle
| - returns one of the two choice arrows bound to the
| slider frame.
|
| method getChoiceArrowID(framehandle arrow) -> int
| - returns the index of the arrow handle.
|
| method getSliderValue() -> int
| - self-explanatory
|
| method getChunkFromIndex(int id) -> int
| - Retrieves the chunk containing the techtree
| icon id.
|
| Setters:
| method setTooltipName(string name)
| - sets the text entry for the name portion of the
| tooltip frame to the specified parameter. Empty
| string defaults into "Unit Name Missing!"
|
| method setTooltipDesc(string name)
| - similar to setTooltipName, this sets the entry
| of the description portion of the tooltip frame
| to the specified parameter. Defaults into "Tooltip
| Missing!"
|
| method setDescription(string desc)
| - Assigns the contents to the textarea frame. Used
| for giving a faction some background info or
| description, etc.
|
| method setChoiceName(int index, string name)
| - Sets the name of the selected choice button to
| the specified name. Defaults to "Factionless"
|
| method setTechtreeIconDisplay(int techChunk, int row, int col, string display)
| - Sets the background of the specified techtree
| icon to the model pointed by the display path.
|
| method setTechtreeIconDisplayEx(framehandle icon, string display)
| - A more direct version of the above function.
| Useful when the user is already using the
| appropriate icon directly.
|
| method setTechtreeIconDisplayByID(int contextID, string display)
| - An index-based version of the above function.
|
| method setBarProgress(real value)
| - Sets the width of the visual bar to a specified
| ratio relative to its parent frame that is equal
| to the specified value.
|
| method setMainAlpha(real ratio)
| - Modifies the alpha coloring of the main frame.
|
| method setFactionDisplay(string iconPath)
| - Updates the screen texture at the top left section
| of the main frame. Automatically handles visibility.
| - Setting the display to an empty string hides it
| while setting the display to any other value shows
| it.
|
| method setFactionName(string name)
| - Sets the contents of the text frame above the screen
| texture to the specified name. Defaults to "Faction
| Name".
| - It is to be used in conjunction with the selection
| of the current frame.
|
| method setMainPos(x, y)
| - Moves the center of the main frame to that specified
| position.
|
| method setSliderValue(real value)
| - Changes the position value of the slider button.
|
| method setSliderMaxValue(int max)
| - Sets the slider's maximum value to the specified
| amount. Cannot go lower than 1.
| - Has the added effect of automatically updating
| the slider value.
|
| Visibility:
| method isMainVisible() -> bool
| method isTooltipVisible() -> bool
| method isSliderVisible() -> bool
| method isChoiceButtonVisible(int index) -> bool
| method isTechtreeChunkVisible(int techChunk) -> bool
| method isChoiceArrowVisible(int isUp) -> bool
| method isTechtreeArrowVisible(int techChunk, bool isUp) -> bool
| method isFactionNameVisible() -> bool
| method isTechtreeIconVisible(int contextId) -> bool
| - Returns the visibility state of the following frames in order:
| - Main frame
| - Tooltip
| - Slider adjacent to container frame
| - Choice button
| - Techtree chunk contained inside techtree container frame
| - Arrow buttons adjacent to the techtree chunk frames.
| - Techtree arrows adjacent to the slider.
| - Faction Name
| - Techtree icon
|
| method setMainVisible(bool flag)
| method setTooltipVisible(bool flag)
| method setSliderVisible(bool flag)
| method setChoiceButtonVisible(int index, bool flag)
| method setTechtreeChunkVisible(int techChunk, bool flag)
| method setChoiceArrowVisible(int isUp, bool flag)
| method setTechtreeArrowVisible(int techChunk, bool isUp, bool flag)
| method setFactionNameVisible(bool flag)
| method setTechtreeIconVisible(int contextId, bool flag)
| - Modifies the visibility state of the following frames in order:
| - Main frame
| - Tooltip
| - Slider adjacent to container frame
| - Choice button
| - Techtree chunk contained inside techtree container frame
| - Arrow buttons adjacent to the techtree chunk frames.
| - Techtree arrows adjacent to the slider.
| - Faction Name
| - Techtree icon
|
|---------------------------------------------------------------------------
|
| Aside from the methods publicly available, the following members are
| readonly for the user's convenience (should they choose to update it):
|
| struct CustomRaceInterface
| framehandle main
| framehandle iconFrame
| framehandle descArea
| framehandle factFrame
| framehandle confirmFrame
| framehandle choiceFrame
| framehandle techFrame
| framehandle slider
| framehandle bar
| framehandle barParent
| framehandle techTooltip
| framehandle techTooltipName
| framehandle techTooltipDesc
| framehandle array techtreeIcons
| framehandle array techtreeChunk
| framehandle array techtreeArrow
| framehandle array choiceArrow
| framehandle array choiceButton
|
---------------------------------------------------------------------------
*/
globals
private constant boolean IN_DEBUG_MODE = true
endglobals
private constant function DebugFrameModel takes nothing returns string
return "ReplaceableTextures\\CommandButtons\\BTNGhoul.tga"
endfunction
private constant function GetTOCPath takes nothing returns string
return "war3mapImported\\CustomRaceTOC.toc"
endfunction
private constant function GetUpArrowButtonModel takes nothing returns string
return "UI\\Widgets\\Glues\\SinglePlayerSkirmish-ScrollBarUpButton.blp"
endfunction
private constant function GetDownArrowButtonModel takes nothing returns string
return "UI\\Widgets\\Glues\\SinglePlayerSkirmish-ScrollBarDownButton.blp"
endfunction
private constant function MainFrameInitiallyVisible takes nothing returns boolean
return false
endfunction
private constant function GetTechtreeChunkTextFrameWidth takes nothing returns real
return 0.024
endfunction
private constant function GetTechtreeChunkTextFrameHeight takes nothing returns real
return 0.018
endfunction
private constant function GetTechtreeChunkHolderWidthOffset takes nothing returns real
return 0.004
endfunction
private constant function GetTechtreeChunkHolderHeightOffset takes nothing returns real
return 0.004
endfunction
private constant function GetTechtreeArrowMaxWidth takes nothing returns real
return 0.032
endfunction
// ==================================================== //
// CONFIGURATION SECTION //
// ==================================================== //
globals
public string array techName
constant integer TECHTREE_CHUNK_UNIT = 1
constant integer TECHTREE_CHUNK_BUILDING = 2
constant integer TECHTREE_CHUNK_HEROES = 3
constant integer TECHTREE_CHUNK_UPGRADES = 4
endglobals
public constant function GetMainFrameCenterX takes nothing returns real
return 0.342
endfunction
public constant function GetMainFrameCenterY takes nothing returns real
return 0.338
endfunction
private constant function GetTechtreeTooltipOffsetX takes nothing returns real
return 0.016
endfunction
private constant function GetTechtreeTooltipOffsetY takes nothing returns real
return -0.06
endfunction
public constant function GetMaxDisplayChoices takes nothing returns integer
return 3
endfunction
private constant function GetChoiceSizeOffset takes nothing returns real
return 0.003
endfunction
public constant function GetTechtreeChunkCount takes nothing returns integer
return 2
endfunction
public constant function GetTechtreeIconColumnMax takes nothing returns integer
return 4
endfunction
public constant function GetTechtreeIconRowMax takes nothing returns integer
return 2
endfunction
private constant function GetBarModel takes nothing returns string
return "ReplaceableTextures\\Teamcolor\\Teamcolor01.tga"
endfunction
private constant function GetBarTransparency takes nothing returns real
return 0.55
endfunction
private function InitChunkNames takes nothing returns nothing
set techName[TECHTREE_CHUNK_UNIT] = "Units:"
set techName[TECHTREE_CHUNK_BUILDING] = "Buildings:"
set techName[TECHTREE_CHUNK_HEROES] = "Heroes:"
set techName[TECHTREE_CHUNK_UPGRADES] = "Upgrades:"
endfunction
// ==================================================== //
// END CONFIGURATION SECTION //
// ==================================================== //
// Too lazy to use a hashtable here, so this hashing function will do.
// Since there are only about 3-4 (8+ is pushing it) buttons in total,
// the chance of a collision in index entry might as well be 0. (there
// is a chance, but it is statistically improbable.)
private function GetFrameIndex takes framehandle whichButton returns integer
return ModuloInteger(GetHandleId(whichButton) - 0x100000, 0x8000)
endfunction
static if IN_DEBUG_MODE then
private struct Debugger extends array
private static integer warningRaised = 0
private static string array warning
static method prepWarning takes string source, string msg returns nothing
set warningRaised = warningRaised + 1
set warning[warningRaised] = "\n (" + I2S(warningRaised) + /*
*/ ") In " + source + ": (" + msg + ")."
endmethod
static method raiseWarning takes string source returns nothing
local string msg = source + ": Warning Raised!"
local integer i = 1
if warningRaised < 1 then
return
endif
loop
exitwhen i > warningRaised
set msg = msg + warning[i]
set i = i + 1
endloop
set warningRaised = 0
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.0, 0.0, 60.0, msg)
endmethod
static method warningReady takes nothing returns boolean
return warningRaised > 0
endmethod
endstruct
endif
struct CustomRaceInterface extends array
readonly static framehandle main = null
readonly static framehandle iconFrame = null
readonly static framehandle descArea = null
readonly static framehandle factFrame = null
readonly static framehandle confirmFrame = null
readonly static framehandle choiceFrame = null
readonly static framehandle techFrame = null
readonly static framehandle slider = null
readonly static framehandle bar = null
readonly static framehandle barParent = null
readonly static framehandle techTooltip = null
readonly static framehandle techTooltipName = null
readonly static framehandle techTooltipDesc = null
readonly static framehandle array techtreeIcons
readonly static framehandle array techtreeChunk
readonly static framehandle array techtreeArrow
readonly static framehandle array choiceArrow
readonly static framehandle array choiceButton
readonly static integer array choiceButtonId
readonly static integer array techtreeIconContextId
readonly static integer iconsPerChunk = 0
readonly static integer sliderMinValue = 0
readonly static integer sliderMaxValue = 1
readonly static string iconTexture = ""
private static method getBoundedRealValue takes real a, real max, real min returns real
local real temp = 0.0
if max < min then
set temp = max
set max = min
set min = temp
endif
if a > max then
static if IN_DEBUG_MODE then
call Debugger.prepWarning("getBoundedRealValue", R2S(a) + " is greater than the maximum value " + R2S(max))
endif
set a = max
endif
if a < min then
static if IN_DEBUG_MODE then
call Debugger.prepWarning("getBoundedRealValue", R2S(a) + " is less than the minimum value " + R2S(min))
endif
set a = min
endif
return a
endmethod
private static method getBoundedIntValue takes integer a, integer max, integer min returns integer
local integer temp = 0
if max < min then
set temp = max
set max = min
set min = temp
endif
if a > max then
static if IN_DEBUG_MODE then
call Debugger.prepWarning("getBoundedIntValue", I2S(a) + " is greater than the maximum value " + I2S(max))
endif
set a = max
endif
if a < min then
static if IN_DEBUG_MODE then
call Debugger.prepWarning("getBoundedIntValue", I2S(a) + " is less than the minimum value " + I2S(min))
endif
set a = min
endif
return a
endmethod
private static method chunkInfo2Index takes integer techChunk, integer row, integer col returns integer
set techChunk = getBoundedIntValue(techChunk, 1, GetTechtreeChunkCount())
static if IN_DEBUG_MODE then
call Debugger.raiseWarning("chunkInfo2Index (techChunk)")
endif
set row = getBoundedIntValue(row, 1, GetTechtreeIconRowMax())
static if IN_DEBUG_MODE then
call Debugger.raiseWarning("chunkInfo2Index (row)")
endif
set col = getBoundedIntValue(col, 1, GetTechtreeIconColumnMax())
static if IN_DEBUG_MODE then
call Debugger.raiseWarning("chunkInfo2Index (col)")
endif
return (techChunk-1)*GetTechtreeIconRowMax()*GetTechtreeIconColumnMax() + /*
*/ (row-1)*GetTechtreeIconColumnMax() + col
endmethod
// ============================================================= //
// External Struct API //
// ============================================================= //
// ================== //
// Getter API //
// ================== //
static method getTechtreeIcon takes integer techChunk, integer row, integer col returns framehandle
return techtreeIcons[chunkInfo2Index(techChunk, row, col)]
endmethod
static method getTechtreeIconRaw takes integer index returns framehandle
return techtreeIcons[index]
endmethod
static method getChoiceButton takes integer index returns framehandle
return choiceButton[index]
endmethod
static method getTechtreeArrow takes integer techChunk, boolean isUp returns framehandle
set techChunk = getBoundedIntValue(techChunk, 1, GetMaxDisplayChoices())
if isUp then
return techtreeArrow[2*(techChunk-1) + 1]
endif
return techtreeArrow[2*(techChunk)]
endmethod
static method getTechtreeArrowID takes framehandle arrow returns integer
local integer i = 1
local integer j = GetTechtreeChunkCount()*2
loop
exitwhen i > j
if techtreeArrow[i] == arrow then
return i
endif
set i = i + 1
endloop
return 0
endmethod
static method getChoiceArrow takes boolean isUp returns framehandle
if isUp then
return choiceArrow[1]
endif
return choiceArrow[2]
endmethod
static method getChoiceArrowID takes framehandle arrow returns integer
if arrow == choiceArrow[1] then
return 1
elseif arrow == choiceArrow[2] then
return 2
endif
return 0
endmethod
static method getChoiceButtonID takes framehandle choice returns integer
return choiceButtonId[GetFrameIndex(choice)]
endmethod
static method getTechtreeIconID takes framehandle icon returns integer
return techtreeIconContextId[GetFrameIndex(icon)]
endmethod
static method getSliderValue takes nothing returns integer
return R2I(BlzFrameGetValue(slider) + 0.01)
endmethod
static method getChunkFromIndex takes integer id returns integer
return ((id - 1) / iconsPerChunk) + 1
endmethod
// ================== //
// Setter API //
// ================== //
static method setTooltipName takes string name returns nothing
if name == "" then
set name = "Unit Name Missing!"
endif
call BlzFrameSetText(techTooltipName, name)
endmethod
static method setTooltipDesc takes string desc returns nothing
if desc == "" then
set desc = "Tooltip Missing!"
endif
call BlzFrameSetText(techTooltipDesc, desc)
endmethod
static method setDescription takes string content returns nothing
call BlzFrameSetText(descArea, content)
endmethod
static method setChoiceName takes integer index, string name returns nothing
set index = getBoundedIntValue(index, 1, GetMaxDisplayChoices())
if name == "" then
set name = "Factionless"
endif
call BlzFrameSetText(choiceButton[index], name)
endmethod
static method setTechtreeIconDisplay takes integer techChunk, integer row, integer col, string display returns nothing
local integer index = chunkInfo2Index(techChunk, row, col)
local framehandle icon = BlzGetFrameByName("CustomRaceFactionTechtreeIconActiveBackdrop", index)
local framehandle pIcon = BlzGetFrameByName("CustomRaceFactionTechtreeIconBackdrop", index)
call BlzFrameSetTexture(icon, display, 0, true)
call BlzFrameSetTexture(pIcon, display, 0, true)
set pIcon = null
set icon = null
endmethod
static method setTechtreeIconDisplayEx takes framehandle techIcon, string display returns nothing
local integer index = techtreeIconContextId[GetFrameIndex(techIcon)]
local framehandle icon
local framehandle pIcon
if index == 0 then
return
endif
set icon = BlzGetFrameByName("CustomRaceFactionTechtreeIconActiveBackdrop", index)
set pIcon = BlzGetFrameByName("CustomRaceFactionTechtreeIconBackdrop", index)
call BlzFrameSetTexture(icon, display, 0, true)
call BlzFrameSetTexture(pIcon, display, 0, true)
set pIcon = null
set icon = null
endmethod
static method setTechtreeIconDisplayByID takes integer contextID, string display returns nothing
local framehandle icon = BlzGetFrameByName("CustomRaceFactionTechtreeIconActiveBackdrop", contextID)
local framehandle pIcon = BlzGetFrameByName("CustomRaceFactionTechtreeIconBackdrop", contextID)
call BlzFrameSetTexture(icon, display, 0, true)
call BlzFrameSetTexture(pIcon, display, 0, true)
set pIcon = null
set icon = null
endmethod
// Values range from 0.0 - 1.0 with 1.0 filling up the entire bar
// and 0.0 being completely empty.
static method setBarProgress takes real amount returns nothing
set amount = getBoundedRealValue(amount, 1.0, 0.0)
call BlzFrameSetSize(bar, BlzFrameGetWidth(barParent)*(amount), BlzFrameGetHeight(barParent))
endmethod
// Values range from 0.0 - 1.0 with 1.0 being completely visible
// and 0.0 being completely invisible
static method setMainAlpha takes real ratio returns nothing
set ratio = getBoundedRealValue(ratio, 1.0, 0.0)
call BlzFrameSetAlpha(main, R2I(255.0*ratio))
if iconTexture == "" and BlzFrameIsVisible(iconFrame) then
call BlzFrameSetVisible(iconFrame, false)
endif
call BlzFrameSetAlpha(bar, R2I(255.0*ratio*(1.0 - GetBarTransparency())))
endmethod
// Displays the representative faction image to the top-left
// of the main frame. Adding an empty string automatically
// hides the frame.
static method setFactionDisplay takes string imagePath returns nothing
set iconTexture = imagePath
if imagePath == "" and BlzFrameIsVisible(iconFrame) then
call BlzFrameSetVisible(iconFrame, false)
elseif (imagePath != "") and (not BlzFrameIsVisible(iconFrame)) then
call BlzFrameSetVisible(iconFrame, true)
endif
call BlzFrameSetTexture(iconFrame, imagePath, 0, true)
endmethod
static method setFactionName takes string name returns nothing
if name == "" then
set name = "Faction Name"
endif
call BlzFrameSetText(factFrame, name)
endmethod
static method setMainPos takes real x, real y returns nothing
call BlzFrameSetAbsPoint(main, FRAMEPOINT_CENTER, x, y)
endmethod
static method setSliderValue takes integer value returns nothing
call BlzFrameSetValue(slider, value)
endmethod
static method setSliderMaxValue takes integer value returns nothing
local real preValue = BlzFrameGetValue(slider)
local real preMax = sliderMaxValue
set value = IMaxBJ(value, 1)
set sliderMaxValue = value
call BlzFrameSetMinMaxValue(slider, 0.0, value)
call BlzFrameSetValue(slider, preValue + value - preMax)
endmethod
// ============================== //
// Boolean State Check API //
// ============================== //
static method isMainVisible takes nothing returns boolean
return BlzFrameIsVisible(main)
endmethod
static method isTooltipVisible takes nothing returns boolean
return BlzFrameIsVisible(techTooltip)
endmethod
static method isSliderVisible takes nothing returns boolean
return BlzFrameIsVisible(slider)
endmethod
static method isChoiceButtonVisible takes integer index returns boolean
set index = getBoundedIntValue(index, 1, GetMaxDisplayChoices())
return BlzFrameIsVisible(choiceButton[index])
endmethod
static method isTechtreeChunkVisible takes integer techChunk returns boolean
set techChunk = getBoundedIntValue(techChunk, 1, GetTechtreeChunkCount())
return BlzFrameIsVisible(techtreeChunk[techChunk])
endmethod
static method isChoiceArrowVisible takes boolean isUp returns boolean
if isUp then
return BlzFrameIsVisible(choiceArrow[1])
endif
return BlzFrameIsVisible(choiceArrow[2])
endmethod
static method isTechtreeArrowVisible takes integer techChunk, boolean isUp returns boolean
local integer index = 0
set techChunk = getBoundedIntValue(techChunk, 1, GetTechtreeChunkCount())
set index = (techChunk)*2
if isUp then
set index = index - 1
endif
return BlzFrameIsVisible(techtreeArrow[index])
endmethod
static method isFactionNameVisible takes nothing returns boolean
return BlzFrameIsVisible(factFrame)
endmethod
static method isTechtreeIconVisible takes integer contextID returns boolean
return BlzFrameIsVisible(techtreeIcons[contextID])
endmethod
// ============================== //
// Regular API //
// ============================== //
static method setMainVisible takes boolean flag returns nothing
call BlzFrameSetVisible(main, flag)
endmethod
static method setTooltipVisible takes boolean flag returns nothing
call BlzFrameSetVisible(techTooltip, flag)
endmethod
static method setSliderVisible takes boolean flag returns nothing
call BlzFrameSetVisible(slider, flag)
endmethod
static method setChoiceButtonVisible takes integer index, boolean flag returns nothing
set index = getBoundedIntValue(index, 1, GetMaxDisplayChoices())
call BlzFrameSetVisible(choiceButton[index], flag)
endmethod
static method setTechtreeChunkVisible takes integer techChunk, boolean flag returns nothing
set techChunk = getBoundedIntValue(techChunk, 1, GetTechtreeChunkCount())
call BlzFrameSetVisible(techtreeChunk[techChunk], flag)
endmethod
static method setChoiceArrowVisible takes boolean isUp, boolean flag returns nothing
if isUp then
call BlzFrameSetVisible(choiceArrow[1], flag)
endif
call BlzFrameSetVisible(choiceArrow[2], flag)
endmethod
static method setTechtreeArrowVisible takes integer techChunk, boolean isUp, boolean flag returns nothing
local integer index = 0
set techChunk = getBoundedIntValue(techChunk, 1, GetTechtreeChunkCount())
set index = (techChunk)*2
if isUp then
set index = index - 1
endif
call BlzFrameSetVisible(techtreeArrow[index], flag)
endmethod
static method setFactionNameVisible takes boolean flag returns nothing
call BlzFrameSetVisible(factFrame, flag)
endmethod
static method setTechtreeIconVisible takes integer contextID, boolean flag returns nothing
call BlzFrameSetVisible(techtreeIcons[contextID], flag)
endmethod
// ============================================================= //
// End External Struct API //
// ============================================================= //
private static method initMainFrame takes framehandle world returns nothing
// Assign variables
set main = BlzCreateFrame("CustomRaceMainFrame", world, 0, 0)
set iconFrame = BlzGetFrameByName("CustomRaceFactionDisplayIcon", 0)
set descArea = BlzGetFrameByName("CustomRaceFactionDescArea", 0)
set factFrame = BlzGetFrameByName("CustomRaceFactionName", 0)
set confirmFrame = BlzGetFrameByName("CustomRaceFactionConfirmButton", 0)
set choiceFrame = BlzGetFrameByName("CustomRaceFactionChoiceMain", 0)
set techFrame = BlzGetFrameByName("CustomRaceFactionTechtreeBackdrop", 0)
set slider = BlzGetFrameByName("CustomRaceFactionChoiceScrollbar", 0)
set bar = BlzGetFrameByName("CustomRaceFactionUpdateBar", 0)
set barParent = BlzFrameGetParent(bar)
set choiceArrow[1] = BlzGetFrameByName("CustomRaceFactionChoiceScrollbarIncButton", 0)
set choiceArrow[2] = BlzGetFrameByName("CustomRaceFactionChoiceScrollbarDecButton", 0)
set iconsPerChunk = GetTechtreeIconRowMax()*GetTechtreeIconColumnMax()
// Prepare actual frame for use.
call BlzFrameSetAbsPoint(main, FRAMEPOINT_CENTER, GetMainFrameCenterX(), GetMainFrameCenterY())
call BlzFrameSetTexture(bar, GetBarModel(), 0, true)
call BlzFrameSetAlpha(bar, R2I(255.0*(1.0 - GetBarTransparency())))
if not MainFrameInitiallyVisible() then
call BlzFrameSetVisible(main, false)
endif
endmethod
private static method initChildFrames takes nothing returns nothing
local integer i = 1
local integer j = 0
local integer k = 0
local integer row = GetTechtreeIconRowMax()
local integer col = GetTechtreeIconColumnMax()
local integer id = 0
local real width = BlzFrameGetWidth(choiceFrame)
local real size = (BlzFrameGetHeight(choiceFrame) - GetChoiceSizeOffset()) / R2I(GetMaxDisplayChoices())
local real dwidth = 0.0
local framehandle tempFrame
local framehandle oldTempFrame
// Create the choice buttons.
loop
exitwhen i > GetMaxDisplayChoices()
set choiceButton[i] = BlzCreateFrame("CustomRaceFactionChoiceButton", choiceFrame, /*
*/ 0, i)
set id = GetFrameIndex(choiceButton[i])
set choiceButtonId[id] = i
call BlzFrameSetPoint(choiceButton[i], FRAMEPOINT_TOP, choiceFrame, FRAMEPOINT_TOP, 0, /*
*/ -(GetChoiceSizeOffset() + (i-1)*size))
call BlzFrameSetSize(choiceButton[i], width, size)
set i = i + 1
endloop
// Create the tooltip frame.
set techTooltip = BlzCreateFrame("CustomRaceTechtreeTooltip", main, 0, 0)
set techTooltipName = BlzGetFrameByName("CustomRaceTechtreeTooltipName", 0)
set techTooltipDesc = BlzGetFrameByName("CustomRaceTechtreeTooltipNameExtended", 0)
call BlzFrameSetPoint(techTooltip, FRAMEPOINT_BOTTOMLEFT, techFrame, FRAMEPOINT_TOPRIGHT, /*
*/ GetTechtreeTooltipOffsetX(), GetTechtreeTooltipOffsetY())
// Create the techtree chunks and icons
set j = 1
loop
exitwhen j > GetTechtreeChunkCount()
set techtreeChunk[j] = BlzCreateFrame("CustomRaceTechtreeChunk", techFrame, 0, j)
call BlzFrameSetSize(techtreeChunk[j], BlzFrameGetWidth(techFrame), /*
*/ BlzFrameGetHeight(techFrame) / I2R(GetTechtreeChunkCount()))
if j == 1 then
call BlzFrameSetPoint(techtreeChunk[j], FRAMEPOINT_TOP, techFrame, /*
*/ FRAMEPOINT_TOP, 0.0, 0.0)
else
call BlzFrameSetPoint(techtreeChunk[j], FRAMEPOINT_TOP, techtreeChunk[j - 1], /*
*/ FRAMEPOINT_BOTTOM, 0.0, 0.0)
endif
set tempFrame = BlzGetFrameByName("CustomRaceTechtreeChunkTitle", j)
call BlzFrameSetText(tempFrame, techName[j])
call BlzFrameSetSize(tempFrame, BlzFrameGetWidth(techFrame), /*
*/ GetTechtreeChunkTextFrameHeight())
call BlzFrameSetPoint(tempFrame, FRAMEPOINT_TOP, techtreeChunk[j], /*
*/ FRAMEPOINT_TOP, 0.0, 0.0)
set oldTempFrame = tempFrame
set tempFrame = BlzGetFrameByName("CustomRaceTechtreeChunkHolder", j)
call BlzFrameSetSize(tempFrame, BlzFrameGetWidth(techFrame) - /*
*/ GetTechtreeChunkTextFrameWidth() , /*
*/ BlzFrameGetHeight(techtreeChunk[j]) - /*
*/ GetTechtreeChunkTextFrameHeight())
call BlzFrameSetPoint(tempFrame, FRAMEPOINT_TOPRIGHT, oldTempFrame, /*
*/ FRAMEPOINT_BOTTOMRIGHT, 0.0, 0.0)
set width = (BlzFrameGetWidth(tempFrame) - 2*GetTechtreeChunkHolderWidthOffset()) / I2R(col)
set size = (BlzFrameGetHeight(tempFrame) - 2*GetTechtreeChunkHolderHeightOffset()) / I2R(row)
set k = 1
loop
exitwhen k > row
set i = 1
loop
exitwhen i > col
set id = (j-1)*(col*row) + (k-1)*col + i
set techtreeIcons[id] = BlzCreateFrame("CustomRaceFactionTechtreeIcon", /*
*/ tempFrame, 0, id)
// DO NOT DELETE THESE LINES! This ensures that the handle id
// counter is in sync across all players.
call BlzGetFrameByName("CustomRaceFactionTechtreeIconActiveBackdrop", id)
call BlzGetFrameByName("CustomRaceFactionTechtreeIconBackdrop", id)
// Again, too lazy to use hashtables here.
set techtreeIconContextId[GetFrameIndex(techtreeIcons[id])] = id
call BlzFrameSetSize(techtreeIcons[id], width, size)
if i == 1 then
if k == 1 then
// Reposition the first icon above
call BlzFrameSetPoint(techtreeIcons[id], FRAMEPOINT_TOPLEFT, /*
*/ tempFrame, FRAMEPOINT_TOPLEFT, /*
*/ GetTechtreeChunkHolderWidthOffset(), /*
*/ -GetTechtreeChunkHolderHeightOffset())
else
// First icon already defined. Just move
// this icon below that.
call BlzFrameSetPoint(techtreeIcons[id], FRAMEPOINT_TOPLEFT, /*
*/ techtreeIcons[id - col], FRAMEPOINT_BOTTOMLEFT, /*
*/ 0.0, 0.0)
endif
else
call BlzFrameSetPoint(techtreeIcons[id], FRAMEPOINT_LEFT, /*
*/ techtreeIcons[id - 1], FRAMEPOINT_RIGHT, /*
*/ 0.0, 0.0)
endif
set i = i + 1
endloop
set k = k + 1
endloop
set dwidth = BlzFrameGetWidth(techFrame) - BlzFrameGetWidth(tempFrame)
set size = BlzFrameGetHeight(tempFrame) / 2.0
set dwidth = RMinBJ(dwidth - GetTechtreeChunkHolderWidthOffset() / 2.0, /*
*/ GetTechtreeArrowMaxWidth())
set size = size - GetTechtreeChunkHolderHeightOffset() / 2.0
// Creating the slide arrows
set id = (j-1)*2 + 1
set techtreeArrow[id] = BlzCreateFrame("CustomRaceButton", techtreeChunk[j], 0, id)
call BlzFrameSetSize(techtreeArrow[id], dwidth, size)
call BlzFrameSetPoint(techtreeArrow[id], FRAMEPOINT_TOPRIGHT, tempFrame, FRAMEPOINT_TOPLEFT, /*
*/ GetTechtreeChunkHolderWidthOffset(), /*
*/ -GetTechtreeChunkHolderHeightOffset())
call BlzFrameSetTexture(BlzGetFrameByName("CustomRaceButtonBG", id), /*
*/ GetUpArrowButtonModel(), 0, true)
call BlzFrameSetTexture(BlzGetFrameByName("CustomRaceButtonPushedBG", id), /*
*/ GetUpArrowButtonModel(), 0, true)
call BlzFrameSetTexture(BlzGetFrameByName("CustomRaceButtonDBG", id), /*
*/ GetUpArrowButtonModel(), 0, true)
call BlzFrameSetTexture(BlzGetFrameByName("CustomRaceButtonPushedDBG", id), /*
*/ GetUpArrowButtonModel(), 0, true)
set id = id + 1
set techtreeArrow[id] = BlzCreateFrame("CustomRaceButton", techtreeChunk[j], 0, id)
call BlzFrameSetSize(techtreeArrow[id], dwidth, size)
call BlzFrameSetPoint(techtreeArrow[id], FRAMEPOINT_BOTTOMRIGHT, tempFrame, FRAMEPOINT_BOTTOMLEFT, /*
*/ GetTechtreeChunkHolderWidthOffset(), /*
*/ GetTechtreeChunkHolderHeightOffset())
call BlzFrameSetTexture(BlzGetFrameByName("CustomRaceButtonBG", id), /*
*/ GetDownArrowButtonModel(), 0, true)
call BlzFrameSetTexture(BlzGetFrameByName("CustomRaceButtonPushedBG", id), /*
*/ GetDownArrowButtonModel(), 0, true)
call BlzFrameSetTexture(BlzGetFrameByName("CustomRaceButtonDBG", id), /*
*/ GetDownArrowButtonModel(), 0, true)
call BlzFrameSetTexture(BlzGetFrameByName("CustomRaceButtonPushedDBG", id), /*
*/ GetDownArrowButtonModel(), 0, true)
set j = j + 1
endloop
set tempFrame = null
set oldTempFrame = null
endmethod
private static method init takes nothing returns nothing
local framehandle world = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0)
if not BlzLoadTOCFile(GetTOCPath()) then
static if IN_DEBUG_MODE then
call Debugger.prepWarning("thistype.init", "Unable to load toc path. Aborting! \n (" + GetTOCPath() + ")")
call Debugger.raiseWarning("thistype")
endif
return
endif
call InitChunkNames()
call thistype.initMainFrame(world)
call thistype.initChildFrames()
static if LIBRARY_FrameLoader then
call FrameLoaderAdd(function thistype.init)
endif
endmethod
implement Init
endstruct
endlibrary
library CustomRacePSelection requires /*
----------------------
*/ CustomRaceCore, /*
----------------------
----------------------
*/ CustomRaceUI, /*
----------------------
----------------------
*/ Init, /*
----------------------
---------------------------------------------------------------------------------------
|
| This library is intended to handle the selection of
| players who can select certain factions in addition
| to the regular races.
|
|---------------------------------------------------------------------------------------
|
| As an additional note, this library dictates whether
| Computer Units get to use custom factions or not via
| ComputerPlayersUseCustomFaction()
|
---------------------------------------------------------------------------------------
*/
private constant function ComputerPlayersUseCustomFaction takes nothing returns boolean
return false
endfunction
struct CustomRacePSelection extends array
readonly integer raceIndex
readonly static boolean isSinglePlayer = false
private static integer userPlayerCount = 0
integer faction
integer baseChoice
integer focusFaction
integer focusFactionStack
static integer array baseTechID
integer techtree
integer focusTechtree
integer focusTechtreeStack
integer focusTechID
readonly static integer choicedPlayerSize = 0
readonly static integer unchoicedPlayerSize = 0
readonly static player array choicedPlayers
readonly static player array unchoicedPlayers
private static integer array choicedPlayerMap
private static integer array unchoicedPlayerMap
static method operator [] takes integer index returns thistype
return thistype(index + 1)
endmethod
method getBaseTechID takes integer index returns integer
return baseTechID[(integer(this)-1)*CustomRaceUI_GetTechtreeChunkCount() + index]
endmethod
method setBaseTechID takes integer index, integer value returns nothing
set baseTechID[(integer(this)-1)*CustomRaceUI_GetTechtreeChunkCount() + index] = value
endmethod
//! textmacro_once CRPSelect_ADD_REMOVE takes NAME, SUBNAME
static method add$NAME$Player takes player p returns boolean
local integer id = GetPlayerId(p) + 1
if $SUBNAME$PlayerMap[id] != 0 then
return false
endif
set $SUBNAME$PlayerSize = $SUBNAME$PlayerSize + 1
set $SUBNAME$Players[$SUBNAME$PlayerSize] = p
set $SUBNAME$PlayerMap[id] = 1
return true
endmethod
static method remove$NAME$Player takes player p returns boolean
local integer id = GetPlayerId(p) + 1
local integer i = 1
if $SUBNAME$PlayerMap[id] == 0 then
return false
endif
loop
// The second condition is unnecessary, but
// I want to make sure it really stops at
// that point. If it does, I have a bug to fix.
exitwhen ($SUBNAME$Players[i] == p) or (i > $SUBNAME$PlayerSize)
set i = i + 1
endloop
// Note: The distinction between id and i
// id refers to the player's ID + 1
// i refers to the position of the player in the array in question.
set $SUBNAME$Players[i] = $SUBNAME$Players[$SUBNAME$PlayerSize]
set $SUBNAME$Players[$SUBNAME$PlayerSize] = null
set $SUBNAME$PlayerSize = $SUBNAME$PlayerSize - 1
set $SUBNAME$PlayerMap[id] = 0
return true
endmethod
static method has$NAME$Player takes player p returns boolean
return $SUBNAME$PlayerMap[GetPlayerId(p) + 1] != 0
endmethod
//! endtextmacro
//! runtextmacro CRPSelect_ADD_REMOVE("Choiced", "choiced")
//! runtextmacro CRPSelect_ADD_REMOVE("Unchoiced", "unchoiced")
static method init takes nothing returns nothing
local integer i = 0
local player p = null
local race r = null
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS - 4
set p = Player(i)
set r = GetPlayerRace(p)
set thistype[i].raceIndex = GetHandleId(r)
// For string synchronization purposes.
call GetPlayerName(p)
if (GetPlayerController(p) == MAP_CONTROL_USER) and /*
*/ (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) then
set userPlayerCount = userPlayerCount + 1
if (CustomRace.getRaceFactionCount(r) > 1) then
call thistype.addChoicedPlayer(p)
else
set thistype[i].faction = 1
call thistype.addUnchoicedPlayer(p)
endif
elseif (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) then
static if ComputerPlayersUseCustomFaction() then
set thistype[i].faction = GetRandomInt(1, CustomRace.getRaceFactionCount(r))
else
set thistype[i].faction = 1
endif
call thistype.addUnchoicedPlayer(p)
endif
set i = i + 1
endloop
set isSinglePlayer = (userPlayerCount == 1)
endmethod
endstruct
struct CRPSelection extends array
static method [] takes player p returns CustomRacePSelection
return CustomRacePSelection[GetPlayerId(p)]
endmethod
endstruct
endlibrary
library CustomRaceMatchConditions requires /*
----------------------
*/ CustomRaceCore, /*
----------------------
------------------------------
*/ CustomRacePSelection, /*
------------------------------
----------------------
*/ Init, /*
----------------------
*/
native UnitAlive takes unit id returns boolean
globals
private force array allyTeam
private force array enemyTeam
private integer array allyCount
private integer array enemyCount
private boolean array userOnStart
private boolean array activeOnStart
private boolean array controlShared
private real crippleTime = bj_MELEE_CRIPPLE_TIMEOUT
endglobals
struct CustomRaceForce extends array
readonly static force activePlayers = CreateForce()
endstruct
// ============================================================================= //
public function IsPlayerActive takes player whichPlayer returns boolean
return (GetPlayerSlotState(whichPlayer) == PLAYER_SLOT_STATE_PLAYING) and /*
*/ (not IsPlayerObserver(whichPlayer))
endfunction
private function WasPlayerActive takes player whichPlayer returns boolean
return activeOnStart[GetPlayerId(whichPlayer)]
endfunction
private function WasPlayerUser takes player whichPlayer returns boolean
return WasPlayerActive(whichPlayer) and /*
*/ userOnStart[GetPlayerId(whichPlayer)]
endfunction
private function IsPlayerOpponent takes integer id, integer opID returns boolean
local player thePlayer = Player(id)
local player theOpponent = Player(opID)
// The player himself is not an opponent.
// Players that aren't playing aren't opponents.
// Neither are players that are already defeated.
if (id == opID) or /*
*/ (GetPlayerSlotState(theOpponent) != PLAYER_SLOT_STATE_PLAYING) or /*
*/ (bj_meleeDefeated[opID]) then
return false
endif
// Allied players with allied victory set are not opponents.
if (GetPlayerAlliance(thePlayer, theOpponent, ALLIANCE_PASSIVE)) and /*
*/ (GetPlayerAlliance(theOpponent, thePlayer, ALLIANCE_PASSIVE)) and /*
*/ (GetPlayerState(thePlayer, PLAYER_STATE_ALLIED_VICTORY) == 1) and /*
*/ (GetPlayerState(theOpponent, PLAYER_STATE_ALLIED_VICTORY) == 1) then
return false
endif
return true
endfunction
// ============================================================================= //
// ============================================================================= //
private function UnitSurrender takes nothing returns nothing
call SetUnitOwner(GetEnumUnit(), Player(bj_PLAYER_NEUTRAL_VICTIM), false)
endfunction
private function PlayerSurrender takes player whichPlayer returns nothing
local group playerUnits = CreateGroup()
call CachePlayerHeroData(whichPlayer)
call GroupEnumUnitsOfPlayer(playerUnits, whichPlayer, null)
call ForGroup(playerUnits, function UnitSurrender)
call DestroyGroup(playerUnits)
set playerUnits = null
endfunction
private function TeamSurrenderEnum takes nothing returns nothing
call PlayerSurrender(GetEnumPlayer())
endfunction
private function TeamSurrender takes player whichPlayer returns nothing
local integer playerIndex = GetPlayerId(whichPlayer) + 1
call ForForce(allyTeam[playerIndex], function TeamSurrenderEnum)
endfunction
// ============================================================================= //
// ============================================================================= //
globals
private player shareCheckPlayer = null
endglobals
private function TeamGainControl takes nothing returns nothing
local player enumPlayer = GetEnumPlayer()
if (PlayersAreCoAllied(shareCheckPlayer, enumPlayer)) and /*
*/ (shareCheckPlayer != enumPlayer) then
call SetPlayerAlliance(shareCheckPlayer, enumPlayer, ALLIANCE_SHARED_VISION, true)
call SetPlayerAlliance(shareCheckPlayer, enumPlayer, ALLIANCE_SHARED_CONTROL, true)
call SetPlayerAlliance(enumPlayer, shareCheckPlayer, ALLIANCE_SHARED_CONTROL, true)
call SetPlayerAlliance(shareCheckPlayer, enumPlayer, ALLIANCE_SHARED_ADVANCED_CONTROL, true)
endif
endfunction
private function TeamShare takes player whichPlayer returns nothing
local integer playerIndex = GetPlayerId(whichPlayer) + 1
local CustomRace curFaction = 0
local player indexPlayer
set curFaction = CustomRace.getRaceFaction(GetPlayerRace(whichPlayer), /*
*/ CRPSelection[whichPlayer].faction)
set controlShared[playerIndex - 1] = true
set shareCheckPlayer = whichPlayer
call ForForce(allyTeam[playerIndex], function TeamGainControl)
call SetPlayerController(whichPlayer, MAP_CONTROL_COMPUTER)
call curFaction.execSetupAI()
endfunction
// ============================================================================= //
// ============================================================================= //
globals
private integer allyStructures = 0
private integer allyKeyStructures = 0
private integer allyCountEnum = 0
private player checkPlayer = null
private constant group allyStructGroup = CreateGroup()
endglobals
private function AllyCountEnum takes nothing returns nothing
local player enumPlayer = GetEnumPlayer()
local integer playerIndex = GetPlayerId(enumPlayer)
if (not bj_meleeDefeated[playerIndex]) and /*
*/ (checkPlayer != enumPlayer) then
set allyCountEnum = allyCountEnum + 1
endif
endfunction
private function GetAllyCount takes player whichPlayer returns integer
set allyCountEnum = 0
set checkPlayer = whichPlayer
call ForForce(allyTeam[GetPlayerId(whichPlayer) + 1], function AllyCountEnum)
return allyCountEnum
endfunction
private function GetAllyKeyStructureCountEnum takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and /*
*/ CustomRace.isKeyStructure(GetUnitTypeId(GetFilterUnit()))
endfunction
private function OnEnumAllyStructureCount takes nothing returns nothing
local player enumPlayer = GetEnumPlayer()
call GroupEnumUnitsOfPlayer(allyStructGroup, enumPlayer, /*
*/ Filter(function GetAllyKeyStructureCountEnum))
set allyStructures = allyStructures + GetPlayerStructureCount(enumPlayer, true)
set allyKeyStructures = allyKeyStructures + BlzGroupGetSize(allyStructGroup)
endfunction
private function EnumAllyStructureCount takes player whichPlayer returns nothing
local integer playerIndex = GetPlayerId(whichPlayer) + 1
set allyStructures = 0
set allyKeyStructures = 0
call ForForce(allyTeam[playerIndex], function OnEnumAllyStructureCount)
endfunction
private function PlayerIsCrippled takes player whichPlayer returns boolean
call EnumAllyStructureCount(whichPlayer)
// Dead teams are not considered to be crippled.
return (allyStructures > 0) and (allyKeyStructures <= 0)
endfunction
private function GetAllyStructureCount takes player whichPlayer returns integer
call EnumAllyStructureCount(whichPlayer)
return allyStructures
endfunction
private function GetAllyKeyStructureCount takes player whichPlayer returns integer
call EnumAllyStructureCount(whichPlayer)
return allyKeyStructures
endfunction
// ============================================================================= //
// ============================================================================= //
globals
private player defeatCheckPlayer = null
private player defeatCurrPlayer = null
endglobals
// This removes the locally defeated player from the list
// of enemy players.
private function OnDefeatRemove takes nothing returns nothing
local player enumPlayer = GetEnumPlayer()
local integer index = GetPlayerId(enumPlayer) + 1
call ForceRemovePlayer(enemyTeam[index], defeatCheckPlayer)
call ForceRemovePlayer(CustomRaceForce.activePlayers, defeatCheckPlayer)
call CustomRacePSelection.removeUnchoicedPlayer(enumPlayer)
set enemyCount[index] = enemyCount[index] - 1
endfunction
private function DefeatRemove takes player whichPlayer returns nothing
local integer index = GetPlayerId(whichPlayer)
local player prevPlayer = defeatCheckPlayer
set defeatCheckPlayer = whichPlayer
call ForForce(enemyTeam[index + 1], function OnDefeatRemove)
call ForceClear(enemyTeam[index + 1])
call DestroyForce(enemyTeam[index + 1])
set enemyCount[index + 1] = 0
set defeatCheckPlayer = prevPlayer
endfunction
private function DoLeave takes player whichPlayer returns nothing
local player prevPlayer = defeatCurrPlayer
if (GetIntegerGameState(GAME_STATE_DISCONNECTED) != 0) then
call GameOverDialogBJ(whichPlayer, true )
call DefeatRemove(whichPlayer)
else
set bj_meleeDefeated[GetPlayerId(whichPlayer)] = true
call DefeatRemove(whichPlayer)
set defeatCurrPlayer = whichPlayer
call RemovePlayerPreserveUnitsBJ(whichPlayer, PLAYER_GAME_RESULT_DEFEAT, true)
set defeatCurrPlayer = prevPlayer
endif
endfunction
private function DoDefeat takes player whichPlayer returns nothing
local integer index = GetPlayerId(whichPlayer)
local player prevPlayer = defeatCurrPlayer
set bj_meleeDefeated[index] = true
call DefeatRemove(whichPlayer)
set defeatCurrPlayer = whichPlayer
call RemovePlayerPreserveUnitsBJ(whichPlayer, PLAYER_GAME_RESULT_DEFEAT, false)
set defeatCurrPlayer = prevPlayer
endfunction
private function DoDefeatEnum takes nothing returns nothing
local player thePlayer = GetEnumPlayer()
// needs to happen before ownership change
call TeamSurrender(thePlayer)
call DoDefeat(thePlayer)
endfunction
private function DoVictoryEnum takes nothing returns nothing
// Diagnose problems with VictoryEnum
call MeleeDoVictoryEnum()
endfunction
// ============================================================================= //
// ============================================================================= //
globals
private constant force toExposeTo = CreateForce()
endglobals
private function ExposePlayer takes player whichPlayer, boolean expose returns nothing
local integer playerIndex = GetPlayerId(whichPlayer) + 1
local player indexPlayer
call CripplePlayer(whichPlayer, toExposeTo, false)
set bj_playerIsExposed[playerIndex - 1] = expose
call CripplePlayer(whichPlayer, enemyTeam[playerIndex], expose)
endfunction
private function ExposeAllPlayers takes nothing returns nothing
local integer i = 1
loop
exitwhen i > CustomRacePSelection.unchoicedPlayerSize
call ExposePlayer(CustomRacePSelection.unchoicedPlayers[i], false)
set i = i + 1
endloop
endfunction
private function RevealTimerTimeout takes nothing returns nothing
local timer expiredTimer = GetExpiredTimer()
local integer playerIndex = 0
local player exposedPlayer
// Determine which player's timer expired.
set playerIndex = 0
loop
exitwhen (bj_crippledTimer[playerIndex] == expiredTimer)
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
if (playerIndex == bj_MAX_PLAYERS) then
return
endif
set exposedPlayer = Player(playerIndex)
if (GetLocalPlayer() == exposedPlayer) then
// Hide the timer window for this player.
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)
endif
// Display a text message to all players, explaining the exposure.
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, MeleeGetCrippledRevealedMessage(exposedPlayer))
// Expose the player.
call ExposePlayer(exposedPlayer, true)
endfunction
// ============================================================================= //
// ============================================================================= //
globals
private force tempForce
private boolean loserVictorCheckRecursive = false
endglobals
private function CheckForVictors takes nothing returns force
local integer playerIndex
local boolean gameOver = true
set tempForce = CreateForce()
// Check to see if any players have opponents remaining.
set playerIndex = 0
loop
if (not bj_meleeDefeated[playerIndex]) then
// Determine whether or not this player has any remaining opponents.
if enemyCount[playerIndex + 1] > 0 then
call ForceClear(tempForce)
return tempForce
endif
// Keep track of each opponentless player so that we can give
// them a victory later.
call ForceAddPlayer(tempForce, Player(playerIndex))
endif
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
// Set the game over global flag
set bj_meleeGameOver = gameOver
return tempForce
endfunction
private function CheckForLosersAndVictors takes nothing returns nothing
local integer playerIndex
local integer structureCount = 0
local player indexPlayer
local force defeatedPlayers = CreateForce()
local force victoriousPlayers
local boolean gameOver = false
local boolean prevCheck = loserVictorCheckRecursive
// If the game is already over, do nothing
if (bj_meleeGameOver) then
call DestroyForce(defeatedPlayers)
set defeatedPlayers = null
return
endif
// If the game was disconnected then it is over, in this case we
// don't want to report results for anyone as they will most likely
// conflict with the actual game results
if (GetIntegerGameState(GAME_STATE_DISCONNECTED) != 0) then
set bj_meleeGameOver = true
call DestroyForce(defeatedPlayers)
set defeatedPlayers = null
return
endif
// Check each player to see if he or she has been defeated yet.
set playerIndex = 0
loop
set indexPlayer = Player(playerIndex)
if (not bj_meleeDefeated[playerIndex] and not bj_meleeVictoried[playerIndex]) then
set structureCount = GetAllyStructureCount(indexPlayer)
if (GetAllyStructureCount(indexPlayer) <= 0) then
// Keep track of each defeated player so that we can give
// them a defeat later.
call ForceAddPlayer(defeatedPlayers, Player(playerIndex))
// Set their defeated flag now so MeleeCheckForVictors
// can detect victors.
set bj_meleeDefeated[playerIndex] = true
endif
endif
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
// Now that the defeated flags are set, check if there are any victors
set victoriousPlayers = CheckForVictors()
// Defeat all defeated players
call ForForce(defeatedPlayers, function DoDefeatEnum)
// Recheck victory conditions here
if loserVictorCheckRecursive then
call ForForce(victoriousPlayers, function DoVictoryEnum)
call DestroyForce(victoriousPlayers)
call DestroyForce(defeatedPlayers)
set victoriousPlayers = null
set defeatedPlayers = null
return
endif
set loserVictorCheckRecursive = true
call CheckForLosersAndVictors()
set loserVictorCheckRecursive = prevCheck
// Give victory to all victorious players
// If the game is over we should remove all observers
if (bj_meleeGameOver) then
call MeleeRemoveObservers()
endif
call DestroyForce(victoriousPlayers)
call DestroyForce(defeatedPlayers)
set defeatedPlayers = null
set victoriousPlayers = null
endfunction
private function CheckForCrippledPlayers takes nothing returns nothing
local integer playerIndex
local player indexPlayer
local force crippledPlayers = CreateForce()
local boolean isNowCrippled
local race indexRace
// The "finish soon" exposure of all players overrides any "crippled" exposure
if bj_finishSoonAllExposed then
return
endif
// Check each player to see if he or she has been crippled or uncrippled.
set playerIndex = 0
loop
set indexPlayer = Player(playerIndex)
set isNowCrippled = PlayerIsCrippled(indexPlayer)
if (not bj_playerIsCrippled[playerIndex] and isNowCrippled) then
// Player became crippled; start their cripple timer.
set bj_playerIsCrippled[playerIndex] = true
call TimerStart(bj_crippledTimer[playerIndex], crippleTime, false, function RevealTimerTimeout)
if (GetLocalPlayer() == indexPlayer) then
// Use only local code (no net traffic) within this block to avoid desyncs.
// Show the timer window.
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], true)
// Display a warning message.
endif
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, MeleeGetCrippledWarningMessage(indexPlayer))
elseif (bj_playerIsCrippled[playerIndex] and not isNowCrippled) then
// Player became uncrippled; stop their cripple timer.
set bj_playerIsCrippled[playerIndex] = false
call PauseTimer(bj_crippledTimer[playerIndex])
if (GetLocalPlayer() == indexPlayer) then
// Use only local code (no net traffic) within this block to avoid desyncs.
// Hide the timer window for this player.
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)
endif
// Display a confirmation message if the player's team is still alive.
if (GetAllyStructureCount(indexPlayer) > 0) then
if (bj_playerIsExposed[playerIndex]) then
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNREVEALED"))
else
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNCRIPPLED"))
endif
endif
// If the player granted shared vision, deny that vision now.
call ExposePlayer(indexPlayer, false)
endif
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
endfunction
// ============================================================================= //
// ============================================================================= //
private function CheckAddedUnit takes unit whichUnit returns nothing
local player owner = GetOwningPlayer(whichUnit)
// If the player was crippled, this unit may have uncrippled him/her.
if (bj_playerIsCrippled[GetPlayerId(owner)]) then
call CheckForCrippledPlayers()
endif
endfunction
private function CheckLostUnit takes unit whichUnit returns nothing
local player owner = GetOwningPlayer(whichUnit)
local integer count = GetPlayerStructureCount(owner, true)
// We only need to check for mortality if this was the last building.
if (GetPlayerStructureCount(owner, true) <= 0) then
call CheckForLosersAndVictors()
endif
// Check if the lost unit has crippled or uncrippled the player.
// (A team with 0 units is dead, and thus considered uncrippled.)
call CheckForCrippledPlayers()
endfunction
// ============================================================================= //
// ============================================================================= //
private function OnObserverLeave takes nothing returns nothing
local player thePlayer = GetTriggerPlayer()
call RemovePlayerPreserveUnitsBJ(thePlayer, PLAYER_GAME_RESULT_NEUTRAL, false)
endfunction
public function OnAllianceChange takes nothing returns nothing
local player indexPlayer = GetTriggerPlayer()
local player otherPlayer
local integer index = GetPlayerId(indexPlayer)
local integer otherIndex = 0
loop
exitwhen otherIndex >= bj_MAX_PLAYERS
set otherPlayer = Player(otherIndex)
loop
exitwhen (not WasPlayerActive(otherPlayer)) or (index == otherIndex)
if (BlzForceHasPlayer(allyTeam[index + 1], otherPlayer)) and /*
*/ (not PlayersAreCoAllied(indexPlayer, otherPlayer)) then
call ForceRemovePlayer(allyTeam[index + 1], otherPlayer)
call ForceRemovePlayer(allyTeam[otherIndex + 1], indexPlayer)
set allyCount[index + 1] = allyCount[index + 1] - 1
set allyCount[otherIndex + 1] = allyCount[otherIndex + 1] - 1
if (enemyCount[index + 1] > 0) and (enemyCount[otherIndex + 1] > 0) then
call ForceAddPlayer(enemyTeam[index + 1], otherPlayer)
call ForceAddPlayer(enemyTeam[otherIndex + 1], indexPlayer)
set enemyCount[index + 1] = enemyCount[index + 1] + 1
set enemyCount[otherIndex + 1] = enemyCount[otherIndex + 1] + 1
endif
if controlShared[index] then
call SetPlayerAlliance(indexPlayer, otherPlayer, ALLIANCE_SHARED_VISION, false)
call SetPlayerAlliance(indexPlayer, otherPlayer, ALLIANCE_SHARED_CONTROL, false)
call SetPlayerAlliance(otherPlayer, indexPlayer, ALLIANCE_SHARED_CONTROL, false)
call SetPlayerAlliance(indexPlayer, otherPlayer, ALLIANCE_SHARED_ADVANCED_CONTROL, false)
endif
elseif (BlzForceHasPlayer(enemyTeam[index + 1], otherPlayer)) and /*
*/ (PlayersAreCoAllied(indexPlayer, otherPlayer)) then
call ForceRemovePlayer(enemyTeam[index + 1], otherPlayer)
call ForceRemovePlayer(enemyTeam[otherIndex + 1], indexPlayer)
set enemyCount[index + 1] = enemyCount[index + 1] - 1
set enemyCount[otherIndex + 1] = enemyCount[otherIndex + 1] - 1
call ForceAddPlayer(allyTeam[index + 1], otherPlayer)
call ForceAddPlayer(allyTeam[otherIndex + 1], indexPlayer)
set allyCount[index + 1] = allyCount[index + 1] + 1
set allyCount[otherIndex + 1] = allyCount[otherIndex + 1] + 1
if controlShared[index] then
call SetPlayerAlliance(indexPlayer, otherPlayer, ALLIANCE_SHARED_VISION, true)
call SetPlayerAlliance(indexPlayer, otherPlayer, ALLIANCE_SHARED_CONTROL, true)
call SetPlayerAlliance(otherPlayer, indexPlayer, ALLIANCE_SHARED_CONTROL, true)
call SetPlayerAlliance(indexPlayer, otherPlayer, ALLIANCE_SHARED_ADVANCED_CONTROL, true)
endif
endif
exitwhen true
endloop
set otherIndex = otherIndex + 1
endloop
call CheckForLosersAndVictors()
call CheckForCrippledPlayers()
endfunction
private function OnPlayerLeave takes nothing returns nothing
local player thePlayer = GetTriggerPlayer()
call CachePlayerHeroData(thePlayer)
// This is the same as defeat except the player generates the message
// "player left the game" as opposed to "player was defeated".
if (GetAllyCount(thePlayer) > 0) then
// If at least one ally is still alive and kicking, share units with
// them and proceed with death.
call TeamShare(thePlayer)
call DoLeave(thePlayer)
else
// If no living allies remain, swap all units and buildings over to
// neutral_passive and proceed with death.
call TeamSurrender(thePlayer)
call DoLeave(thePlayer)
endif
call CheckForLosersAndVictors()
endfunction
private function OnPlayerDefeat takes nothing returns nothing
local player thePlayer = GetTriggerPlayer()
call CachePlayerHeroData(thePlayer)
// Change it slightly so that control is automatically
// ceded to the computer.
if (GetAllyCount(thePlayer) > 0) then
// If at least one ally is still alive and kicking, share units with
// them and proceed with death.
call TeamShare(thePlayer)
if (not bj_meleeDefeated[GetPlayerId(thePlayer)]) then
call DoDefeat(thePlayer)
endif
else
// If no living allies remain, swap all units and buildings over to
// neutral_passive and proceed with death.
call TeamSurrender(thePlayer)
if (not bj_meleeDefeated[GetPlayerId(thePlayer)]) then
call DoDefeat(thePlayer)
endif
endif
if defeatCurrPlayer == thePlayer then
return
endif
call CheckForLosersAndVictors()
endfunction
private function OnConstructStart takes nothing returns nothing
call CheckAddedUnit(GetConstructingStructure())
endfunction
private function OnStructureDeath takes nothing returns nothing
if IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) then
call CheckLostUnit(GetTriggerUnit())
endif
endfunction
private function OnConstructCancel takes nothing returns nothing
call CheckLostUnit(GetTriggerUnit())
endfunction
// ============================================================================= //
private function OnTournamentFinishRule takes integer multiplier returns nothing
local integer array playerScore
local integer array teamScore
local force array teamForce
local integer teamCount
local integer index
local player indexPlayer
local integer index2
local player indexPlayer2
local integer bestTeam
local integer bestScore
local boolean draw
// Compute individual player scores
set index = 0
loop
set indexPlayer = Player(index)
if WasPlayerUser(indexPlayer) then
set playerScore[index] = IMinBJ(GetTournamentScore(indexPlayer), 1)
else
set playerScore[index] = 0
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
// Compute team scores and team forces
set teamCount = 0
set index = 0
loop
if playerScore[index] != 0 then
set indexPlayer = Player(index)
set teamScore[teamCount] = 0
set teamForce[teamCount] = allyTeam[index + 1]
set index2 = index
loop
loop
exitwhen not IsPlayerInForce(Player(index2), teamForce[teamCount])
if playerScore[index2] != 0 then
set indexPlayer2 = Player(index2)
set teamScore[teamCount] = teamScore[teamCount] + playerScore[index2]
endif
exitwhen true
endloop
set index2 = index2 + 1
exitwhen index2 == bj_MAX_PLAYERS
endloop
set teamCount = teamCount + 1
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
// The game is now over
set bj_meleeGameOver = true
// There should always be at least one team, but continue to work if not
if teamCount != 0 then
// Find best team score
set bestTeam = -1
set bestScore = -1
set index = 0
loop
if teamScore[index] > bestScore then
set bestTeam = index
set bestScore = teamScore[index]
endif
set index = index + 1
exitwhen index == teamCount
endloop
// Check whether the best team's score is 'multiplier' times better than
// every other team. In the case of multiplier == 1 and exactly equal team
// scores, the first team (which was randomly chosen by the server) will win.
set draw = false
set index = 0
loop
if index != bestTeam then
if bestScore < (multiplier * teamScore[index]) then
set draw = true
endif
endif
set index = index + 1
exitwhen index == teamCount
endloop
if draw then
// Give draw to all players on all teams
set index = 0
loop
call ForForce(teamForce[index], function MeleeDoDrawEnum)
set index = index + 1
exitwhen index == teamCount
endloop
else
// Give defeat to all players on teams other than the best team
set index = 0
loop
if index != bestTeam then
call ForForce(teamForce[index], function DoDefeatEnum)
endif
set index = index + 1
exitwhen index == teamCount
endloop
// Give victory to all players on the best team
call ForForce(teamForce[bestTeam], function DoVictoryEnum)
endif
endif
endfunction
private function OnTournamentFinishSoon takes nothing returns nothing
// Note: We may get this trigger multiple times
local integer playerIndex
local player indexPlayer
local real timeRemaining = GetTournamentFinishSoonTimeRemaining()
if bj_finishSoonAllExposed then
return
endif
set bj_finishSoonAllExposed = true
// Reset all crippled players and their timers, and hide the local crippled timer dialog
set playerIndex = 0
loop
exitwhen playerIndex > CustomRacePSelection.unchoicedPlayerSize
set indexPlayer = CustomRacePSelection.unchoicedPlayers[playerIndex]
call ExposePlayer(indexPlayer, false)
/*
if bj_playerIsCrippled[playerIndex] then
// Uncripple the player
set bj_playerIsCrippled[playerIndex] = false
call PauseTimer(bj_crippledTimer[playerIndex])
if (GetLocalPlayer() == indexPlayer) then
// Use only local code (no net traffic) within this block to avoid desyncs.
// Hide the timer window.
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)
endif
endif
*/
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
// Expose all players
// call ExposeAllPlayers()
// Show the "finish soon" timer dialog and set the real time remaining
call TimerDialogDisplay(bj_finishSoonTimerDialog, true)
call TimerDialogSetRealTimeRemaining(bj_finishSoonTimerDialog, timeRemaining)
endfunction
private function OnTournamentFinishNow takes nothing returns nothing
local integer rule = GetTournamentFinishNowRule()
// If the game is already over, do nothing
if bj_meleeGameOver then
return
endif
if (rule == 1) then
// Finals games
call MeleeTournamentFinishNowRuleA(1)
else
// Preliminary games
call MeleeTournamentFinishNowRuleA(3)
endif
// Since the game is over we should remove all observers
call MeleeRemoveObservers()
endfunction
// ============================================================================= //
// ============================================================================= //
private function DefineTeamLineupEx takes integer index, integer otherIndex returns nothing
local integer id = index + 1
local integer otherID = otherIndex + 1
local player whichPlayer = Player(index)
local player otherPlayer
// One of the primary conditions for team lineup
// is that the player must be playing (obviously).
set activeOnStart[index] = IsPlayerActive(whichPlayer)
if not activeOnStart[index] then
return
endif
set userOnStart[index] = GetPlayerController(whichPlayer) == MAP_CONTROL_USER
call ForceAddPlayer(CustomRaceForce.activePlayers, whichPlayer)
if allyTeam[id] == null then
set allyTeam[id] = CreateForce()
set allyCount[id] = 1
call ForceAddPlayer(allyTeam[id], whichPlayer)
endif
if enemyTeam[id] == null then
set enemyTeam[id] = CreateForce()
set enemyCount[id] = 0
endif
loop
exitwhen otherIndex >= bj_MAX_PLAYERS
set otherPlayer = Player(otherIndex)
if IsPlayerActive(otherPlayer) then
// Instantiate the forces
if allyTeam[otherID] == null then
set allyTeam[otherID] = CreateForce()
set allyCount[otherID] = 1
call ForceAddPlayer(allyTeam[otherID], otherPlayer)
endif
if enemyTeam[otherID] == null then
set enemyTeam[otherID] = CreateForce()
set enemyCount[otherID] = 0
endif
if PlayersAreCoAllied(whichPlayer, otherPlayer) then
call ForceAddPlayer(allyTeam[id], otherPlayer)
call ForceAddPlayer(allyTeam[otherID], whichPlayer)
set allyCount[id] = allyCount[id] + 1
set allyCount[otherID] = allyCount[otherID] + 1
else
call ForceAddPlayer(enemyTeam[id], otherPlayer)
call ForceAddPlayer(enemyTeam[otherID], whichPlayer)
set enemyCount[id] = enemyCount[id] + 1
set enemyCount[otherID] = enemyCount[otherID] + 1
endif
endif
set otherIndex = otherIndex + 1
set otherID = otherID + 1
endloop
endfunction
private function DefineTeamLineup takes nothing returns nothing
local integer index = 0
local integer otherIndex = 0
loop
exitwhen index >= bj_MAX_PLAYERS
set otherIndex = index + 1
call DefineTeamLineupEx(index, otherIndex)
set index = index + 1
endloop
endfunction
private function DefineVictoryDefeatEx takes nothing returns nothing
local trigger constructCancelTrig = CreateTrigger()
local trigger deathTrig = CreateTrigger()
local trigger constructStartTrig = CreateTrigger()
local trigger defeatTrig = CreateTrigger()
local trigger leaveTrig = CreateTrigger()
local trigger allianceTrig = CreateTrigger()
local trigger obsLeaveTrig = CreateTrigger()
local trigger tournamentSoonTrig = CreateTrigger()
local trigger tournamentNowTrig = CreateTrigger()
local integer index
local player indexPlayer
call TriggerAddAction(constructCancelTrig, function OnConstructCancel)
call TriggerAddAction(deathTrig, function OnStructureDeath)
call TriggerAddAction(constructStartTrig, function OnConstructStart)
call TriggerAddAction(defeatTrig, function OnPlayerDefeat)
call TriggerAddAction(leaveTrig, function OnPlayerLeave)
call TriggerAddAction(allianceTrig, function OnAllianceChange)
call TriggerAddAction(obsLeaveTrig, function OnObserverLeave)
call TriggerAddAction(tournamentSoonTrig, function OnTournamentFinishSoon)
call TriggerAddAction(tournamentNowTrig, function OnTournamentFinishNow)
// Create a timer window for the "finish soon" timeout period, it has no timer
// because it is driven by real time (outside of the game state to avoid desyncs)
set bj_finishSoonTimerDialog = CreateTimerDialog(null)
// Set a trigger to fire when we receive a "finish soon" game event
call TriggerRegisterGameEvent(tournamentSoonTrig, EVENT_GAME_TOURNAMENT_FINISH_SOON)
// Set a trigger to fire when we receive a "finish now" game event
call TriggerRegisterGameEvent(tournamentNowTrig, EVENT_GAME_TOURNAMENT_FINISH_NOW)
// Set up each player's mortality code.
set index = 0
loop
set indexPlayer = Player(index)
// Make sure this player slot is playing.
if IsPlayerActive(indexPlayer) then
set bj_meleeDefeated[index] = false
set bj_meleeVictoried[index] = false
// Create a timer and timer window in case the player is crippled.
// Coder Notes: Better leave this section untouched.
set bj_playerIsCrippled[index] = false
set bj_playerIsExposed[index] = false
set bj_crippledTimer[index] = CreateTimer()
set bj_crippledTimerWindows[index] = CreateTimerDialog(bj_crippledTimer[index])
call TimerDialogSetTitle(bj_crippledTimerWindows[index], /*
*/ MeleeGetCrippledTimerMessage(indexPlayer))
// Set a trigger to fire whenever a building is cancelled for this player.
call TriggerRegisterPlayerUnitEvent(constructCancelTrig, indexPlayer, /*
*/ EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null)
// Set a trigger to fire whenever a unit dies for this player.
call TriggerRegisterPlayerUnitEvent(deathTrig, indexPlayer, /*
*/ EVENT_PLAYER_UNIT_DEATH, null)
// Set a trigger to fire whenever a unit begins construction for this player
call TriggerRegisterPlayerUnitEvent(constructStartTrig, indexPlayer, /*
*/ EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
// Set a trigger to fire whenever this player defeats-out
call TriggerRegisterPlayerEvent(defeatTrig, indexPlayer, EVENT_PLAYER_DEFEAT)
// Set a trigger to fire whenever this player leaves
call TriggerRegisterPlayerEvent(leaveTrig, indexPlayer, EVENT_PLAYER_LEAVE)
// Set a trigger to fire whenever this player changes his/her alliances.
call TriggerRegisterPlayerAllianceChange(allianceTrig, indexPlayer, ALLIANCE_PASSIVE)
call TriggerRegisterPlayerStateEvent(allianceTrig, indexPlayer, PLAYER_STATE_ALLIED_VICTORY, EQUAL, 1)
else
set bj_meleeDefeated[index] = true
set bj_meleeVictoried[index] = false
// Handle leave events for observers
if (IsPlayerObserver(indexPlayer)) then
// Set a trigger to fire whenever this player leaves
call TriggerRegisterPlayerEvent(obsLeaveTrig, indexPlayer, EVENT_PLAYER_LEAVE)
endif
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
endfunction
public function DefineVictoryDefeat takes nothing returns nothing
call DefineVictoryDefeatEx()
endfunction
// ============================================================================= //
private struct S extends array
private static method init takes nothing returns nothing
call DefineTeamLineup()
endmethod
implement Init
endstruct
endlibrary
library CustomRaceMatch requires /*
--------------------------
*/ CustomRaceCore, /*
--------------------------
--------------------------
*/ CustomRaceUI, /*
--------------------------
------------------------------
*/ CustomRacePSelection, /*
------------------------------
----------------------------------
*/ CustomRaceMatchConditions, /*
----------------------------------
------------------------------
*/ optional Init /*
------------------------------
*/
globals
private constant boolean FOGGED_START = false
private constant boolean USE_EXTRA_TICK = true
public constant boolean APPLY_TIMER_IN_SINGLE_PLAYER = false
private constant integer GAME_START_TICKS = 3
private constant integer EXTRA_TICK_FOR_START = 1
private constant real TICK_INTERVAL = 1.0
private constant real DISPLAY_LIFETIME = 0.80
private constant real DISPLAY_INTERVAL = 1.0 / 100.0
endglobals
// ============================================================================= //
private function ClearMusicPlaylist takes nothing returns nothing
// Observers can't play any faction playlist,
// so return at this point. Comment later
// if this causes desyncs.
if IsPlayerObserver(GetLocalPlayer()) then
return
endif
call ClearMapMusic()
call StopMusic(false)
endfunction
// ============================================================================= //
// ============================================================================= //
// In previous versions, visibility was actually affected.
// In modern versions, visibility is kept intact and only
// the time of day is affected.
// ============================================================================= //
private function StartingVisibility takes nothing returns nothing
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, bj_MELEE_STARTING_TOD)
call SuspendTimeOfDay(true)
static if FOGGED_START then
call FogMaskEnable(true)
call FogEnable(true)
endif
endfunction
// ============================================================================= //
// ============================================================================= //
private function StartingHeroLimit takes nothing returns nothing
local integer index = 0
local integer i = 1
local integer maxHeroIndex = CustomRace.getGlobalHeroMaxIndex()
local player whichPlayer
loop
exitwhen index > bj_MAX_PLAYERS
set whichPlayer = Player(index)
set i = 1
call SetPlayerTechMaxAllowed(whichPlayer, 'HERO', bj_MELEE_HERO_LIMIT)
loop
exitwhen i > maxHeroIndex
call SetPlayerTechMaxAllowed(whichPlayer, CustomRace.getGlobalHero(i), /*
*/ bj_MELEE_HERO_TYPE_LIMIT)
set i = i + 1
endloop
set index = index + 1
endloop
endfunction
// ============================================================================= //
// ============================================================================= //
private function GrantItem takes unit hero returns nothing
if IsUnitType(hero, UNIT_TYPE_HERO) then
call MeleeGrantItemsToHero(hero)
endif
endfunction
private function OnNeutralHeroHired takes nothing returns nothing
call GrantItem(GetSoldUnit())
endfunction
private function OnTrainedHeroFinish takes nothing returns nothing
call GrantItem(GetTrainedUnit())
endfunction
private function GrantHeroItems takes nothing returns nothing
local integer index = 0
local trigger trig = CreateTrigger()
local player whichPlayer = null
call TriggerAddAction(trig, function OnTrainedHeroFinish)
loop
exitwhen index > bj_MAX_PLAYER_SLOTS
// Initialize the twinked hero counts.
set bj_meleeTwinkedHeroes[index] = 0
set whichPlayer = Player(index)
// Register for an event whenever a hero is trained, so that we can give
// him/her their starting items. Exclude
if (index < bj_MAX_PLAYERS) and CustomRaceMatchConditions_IsPlayerActive(whichPlayer) then
call TriggerRegisterPlayerUnitEvent(trig, whichPlayer, /*
*/ EVENT_PLAYER_UNIT_TRAIN_FINISH, null)
endif
set index = index + 1
endloop
// Register for an event whenever a neutral hero is hired, so that we
// can give him/her their starting items.
set trig = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(trig, Player(PLAYER_NEUTRAL_PASSIVE), /*
*/ EVENT_PLAYER_UNIT_SELL, null)
call TriggerAddAction(trig, function OnNeutralHeroHired)
// Flag that we are giving starting items to heroes, so that the melee
// starting units code can create them as necessary.
set bj_meleeGrantHeroItems = true
endfunction
// ============================================================================= //
// ============================================================================= //
private function StartingResources takes nothing returns nothing
local integer index
local player whichPlayer
local version v
local integer startingGold = bj_MELEE_STARTING_GOLD_V1
local integer startingLumber = bj_MELEE_STARTING_LUMBER_V1
set v = VersionGet()
if (v == VERSION_REIGN_OF_CHAOS) then
set startingGold = bj_MELEE_STARTING_GOLD_V0
set startingLumber = bj_MELEE_STARTING_LUMBER_V0
endif
// Set each player's starting resources.
set index = 0
loop
set whichPlayer = Player(index)
if CustomRaceMatchConditions_IsPlayerActive(whichPlayer) then
call SetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_GOLD, startingGold)
call SetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_LUMBER, startingLumber)
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
endfunction
// ============================================================================= //
// ============================================================================= //
private function RemoveNearbyUnits takes real x, real y, real radius returns nothing
local integer i = 0
local integer owner = 0
local integer size = 0
local group nearbyUnits = CreateGroup()
local unit enumUnit
call GroupEnumUnitsInRange(nearbyUnits, x, y, radius, null)
set size = BlzGroupGetSize(nearbyUnits)
loop
exitwhen i >= size
set enumUnit = BlzGroupUnitAt(nearbyUnits, i)
set owner = GetPlayerId(GetOwningPlayer(enumUnit))
if (owner == PLAYER_NEUTRAL_AGGRESSIVE) or /*
*/ ((owner == PLAYER_NEUTRAL_PASSIVE) and /*
*/ (not IsUnitType(enumUnit, UNIT_TYPE_STRUCTURE))) then
// Remove any Neutral Hostile units or
// Neutral Passive units (not structures) from the area.
call RemoveUnit(enumUnit)
endif
set i = i + 1
endloop
call DestroyGroup(nearbyUnits)
set enumUnit = null
set nearbyUnits = null
endfunction
private function ClearExcessUnits takes nothing returns nothing
local integer index = 0
local real locX
local real locY
local player indexPlayer
loop
set indexPlayer = Player(index)
// If the player slot is being used, clear any nearby creeps.
if CustomRaceMatchConditions_IsPlayerActive(indexPlayer) then
set locX = GetStartLocationX(GetPlayerStartLocation(indexPlayer))
set locY = GetStartLocationY(GetPlayerStartLocation(indexPlayer))
call RemoveNearbyUnits(locX, locY, bj_MELEE_CLEAR_UNITS_RADIUS)
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
endfunction
// ============================================================================= //
// ============================================================================= //
private function DefineVictoryDefeat takes nothing returns nothing
// Unravelling this function will open a can of worms
// the likes which would not likely be appreciated.
// Leave it as it is, and make changes in a separate
// library specifically for this function.
call CustomRaceMatchConditions_DefineVictoryDefeat()
endfunction
// ============================================================================= //
// ============================================================================= //
private function OnStartCheckAlliance takes nothing returns nothing
local timer whichTimer = GetExpiredTimer()
call PauseTimer(whichTimer)
call DestroyTimer(whichTimer)
call CustomRaceMatchConditions_OnAllianceChange()
endfunction
public function TestVictoryDefeat takes nothing returns nothing
// Test for victory / defeat at startup, in case the user has already won / lost.
// Allow for a short time to pass first, so that the map can finish loading.
call TimerStart(CreateTimer(), 2.0, false, function OnStartCheckAlliance)
endfunction
// ============================================================================= //
// ============================================================================= //
globals
private integer tempStart = 0
private location tempStartLoc = null
private player tempStartPlayer = null
endglobals
public function OnStartGetPlayer takes nothing returns player
return tempStartPlayer
endfunction
public function OnStartGetLoc takes nothing returns location
return tempStartLoc
endfunction
private function StartingUnits takes nothing returns nothing
local integer index = 1
local CustomRacePSelection obj = 0
local CustomRace faction = 0
local player indexPlayer
local race pRace
call Preloader( "scripts\\SharedMelee.pld" )
loop
exitwhen index > CustomRacePSelection.unchoicedPlayerSize
set indexPlayer = CustomRacePSelection.unchoicedPlayers[index]
set tempStartPlayer = indexPlayer
set tempStart = GetPlayerStartLocation(indexPlayer)
set tempStartLoc = GetStartLocationLoc(tempStart)
set pRace = GetPlayerRace(indexPlayer)
set obj = CRPSelection[indexPlayer]
set faction = CustomRace.getRaceFaction(pRace, obj.faction)
call faction.execSetup()
if GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER then
call faction.execSetupAI()
endif
call RemoveLocation(tempStartLoc)
set index = index + 1
endloop
// Do NOT make these usable afterwards!
set tempStartPlayer = null
set tempStart = 0
set tempStartLoc = null
endfunction
// ============================================================================= //
// ============================================================================= //
private struct FrameInterpolation
private static constant real FRAME_SCALE = 10.0
private static constant real FRAME_ENDSCALE = 2.0
private static constant real START_X = 0.40
private static constant real END_X = 0.40
private static constant real START_Y = 0.45
private static constant real END_Y = 0.25
private static constant framepointtype POINT = FRAMEPOINT_CENTER
private static thistype array objectList
private static integer objectCurIndex = 0
private static timer interpolator = CreateTimer()
private string message
private integer maxTicks
private integer ticks
private framehandle frame
private static method alphaResponse takes real x returns real
set x = x - 0.5
return -16.0*(x*x*x*x) + 1.0
endmethod
private static method slideResponse takes real x returns real
set x = x - 0.5
return -4.0*(x*x*x) + 0.5
endmethod
private static method scaleResponse takes real x returns real
return -(x*x*x*x*x*x) + 1.0
endmethod
private method destroy takes nothing returns nothing
set this.ticks = 0
set this.maxTicks = 0
call BlzFrameSetVisible(this.frame, false)
call BlzDestroyFrame(this.frame)
call this.deallocate()
endmethod
private static method onUpdate takes nothing returns nothing
local integer i = 1
local thistype this = 0
local real ratio = 0.0
local real resp = 0.0
local real cx = 0.0
local real cy = 0.0
local real scale = 0.0
loop
exitwhen i > objectCurIndex
set this = objectList[i]
set this.ticks = this.ticks + 1
set ratio = I2R(this.ticks) / I2R(this.maxTicks)
call BlzFrameSetAlpha(this.frame, R2I(255.0*thistype.alphaResponse(ratio)))
set resp = slideResponse(ratio)
set cx = START_X*resp + END_X*(1-resp)
set cy = START_Y*resp + END_Y*(1-resp)
set resp = scaleResponse(ratio)
set scale = FRAME_SCALE*resp + FRAME_ENDSCALE*(1-resp)
call BlzFrameSetAbsPoint(this.frame, POINT, cx, cy)
call BlzFrameSetScale(this.frame, scale)
if this.ticks >= this.maxTicks then
set objectList[i] = objectList[objectCurIndex]
set objectCurIndex = objectCurIndex - 1
set i = i - 1
call this.destroy()
endif
set i = i + 1
endloop
if objectCurIndex < 1 then
call PauseTimer(interpolator)
endif
endmethod
private static method insert takes thistype this returns nothing
set objectCurIndex = objectCurIndex + 1
set objectList[objectCurIndex] = this
if objectCurIndex == 1 then
call TimerStart(interpolator, DISPLAY_INTERVAL, true, function thistype.onUpdate)
endif
endmethod
static method request takes string msg, real lifetime returns nothing
local thistype this = thistype.allocate()
set this.message = msg
set this.maxTicks = R2I(lifetime / DISPLAY_INTERVAL + 0.01)
set this.ticks = 0
set this.frame = BlzCreateFrameByType("TEXT", "CustomRaceMatchDisplayText", /*
*/ BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), /*
*/ "", integer(this))
call BlzFrameSetText(this.frame, message)
call BlzFrameSetScale(this.frame, FRAME_SCALE)
call BlzFrameSetAlpha(this.frame, 0)
call BlzFrameSetAbsPoint(this.frame, POINT, START_X, START_Y)
call thistype.insert(this)
endmethod
endstruct
private function DisplayToWorld takes string msg, real lifetime returns nothing
call FrameInterpolation.request(msg, lifetime)
endfunction
// ============================================================================= //
// ============================================================================= //
globals
private integer beginTick = 0
private integer extraTick = 0
private group tickGroup = null
private sound tempSound = null
endglobals
private function GenerateTickSound takes nothing returns sound
set tempSound = CreateSound( "Sound\\Interface\\BattleNetTick.wav", false, false, false, 10, 10, "" )
call SetSoundParamsFromLabel( tempSound, "ChatroomTimerTick" )
call SetSoundDuration( tempSound, 476 )
return tempSound
endfunction
private function GenerateHornSound takes nothing returns sound
set tempSound = CreateSound( "Sound\\Ambient\\DoodadEffects\\TheHornOfCenarius.wav", false, false, false, 10, 10, "DefaultEAXON" )
call SetSoundParamsFromLabel( tempSound, "HornOfCenariusSound" )
call SetSoundDuration( tempSound, 12120 )
return tempSound
endfunction
private function PlaySoundForPlayer takes sound snd, player p returns nothing
if GetLocalPlayer() != p then
call SetSoundVolume(snd, 0)
endif
call StartSound(snd)
call KillSoundWhenDone(snd)
endfunction
// ============================================================================= //
// ============================================================================= //
private function SetupPlaylist takes nothing returns nothing
local player whichPlayer = GetLocalPlayer()
local CustomRacePSelection obj = CRPSelection[whichPlayer]
local CustomRace faction = CustomRace.getRaceFaction(GetPlayerRace(whichPlayer), obj.faction)
if faction == 0 then
return
endif
call SetMapMusic(faction.playlist, true, 0)
call PlayMusic(faction.playlist)
endfunction
private function ResetVisuals takes nothing returns nothing
call EnableDragSelect(true, true)
call EnablePreSelect(true, true)
call EnableSelect(true, true)
call EnableUserControl(true)
call EnableUserUI(true)
call SuspendTimeOfDay(false)
endfunction
private function MatchTickDown takes nothing returns nothing
local integer i = 0
local integer size = 0
set beginTick = beginTick - 1
if beginTick > 0 then
call StartSound(GenerateTickSound())
call KillSoundWhenDone(tempSound)
call DisplayToWorld(I2S(beginTick), DISPLAY_LIFETIME)
return
endif
set extraTick = extraTick - 1
if extraTick > 0 then
return
endif
call StartSound(GenerateHornSound())
call KillSoundWhenDone(tempSound)
call DisplayToWorld("|cffff4040Start!|r", 1.20)
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
call TestVictoryDefeat()
call ResetVisuals()
call SetupPlaylist()
set size = BlzGroupGetSize(tickGroup)
loop
exitwhen i >= size
call PauseUnit(BlzGroupUnitAt(tickGroup, i), false)
set i = i + 1
endloop
endfunction
private function SetupVisuals takes nothing returns nothing
local real zdist = GetCameraField(CAMERA_FIELD_TARGET_DISTANCE)
local real ndist = zdist + 1250.0
local real dur = (GAME_START_TICKS)*TICK_INTERVAL
if IsPlayerInForce(GetLocalPlayer(), CustomRaceForce.activePlayers) then
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, ndist, 0.00)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, zdist, 0.00)
endif
call EnableDragSelect(false, false)
call EnablePreSelect(false, false)
call EnableSelect(false, false)
call EnableUserControl(false)
call EnableUserUI(false)
endfunction
private function BeginMatch takes nothing returns nothing
local rect world = GetWorldBounds()
local integer i = 0
local integer size = 0
set tickGroup = CreateGroup()
set beginTick = GAME_START_TICKS + 1
if USE_EXTRA_TICK then
set extraTick = EXTRA_TICK_FOR_START
endif
call TimerStart(CreateTimer(), TICK_INTERVAL, true, function MatchTickDown)
call SetupVisuals()
call GroupEnumUnitsInRect(tickGroup, world, null)
set size = BlzGroupGetSize(tickGroup)
loop
exitwhen i >= size
call PauseUnit(BlzGroupUnitAt(tickGroup, i), true)
set i = i + 1
endloop
endfunction
// ============================================================================= //
// ============================================================================= //
public function MeleeInitialization takes nothing returns nothing
call ClearMusicPlaylist()
call StartingVisibility()
call StartingHeroLimit()
call GrantHeroItems()
call StartingResources()
call ClearExcessUnits()
endfunction
public function MeleeInitializationFinish takes nothing returns nothing
call DefineVictoryDefeat()
call StartingUnits()
call BeginMatch()
endfunction
endlibrary
library CustomRaceObserverUI requires /*
--------------------------
*/ CustomRaceCore, /*
--------------------------
----------------------
*/ CustomRaceUI, /*
----------------------
------------------------------
*/ CustomRacePSelection, /*
------------------------------
--------------
*/ Init, /*
--------------
------------------------------
*/ optional FrameLoader /*
------------------------------
*/
// This only applies to players. Observers will always see the
// faction choice.
private constant function DisplayPlayerFactionChoice takes nothing returns boolean
return false
endfunction
private constant function GetObserverFrameHeight takes nothing returns real
return 0.28
endfunction
private constant function GetObserverFrameWidth takes nothing returns real
return 0.28
endfunction
private constant function GetObserverFrameCenterX takes nothing returns real
return 0.40
endfunction
private constant function GetObserverFrameCenterY takes nothing returns real
return 0.35
endfunction
private constant function GetContainerWidthOffset takes nothing returns real
return 0.06
endfunction
private constant function GetContainerHeightOffset takes nothing returns real
return 0.10
endfunction
private constant function GetContainerFramePoint takes nothing returns framepointtype
return FRAMEPOINT_BOTTOM
endfunction
private constant function GetContainerOffsetX takes nothing returns real
return 0.00
endfunction
private constant function GetContainerOffsetY takes nothing returns real
return 0.01
endfunction
private constant function GetPlayerTextGuideHeight takes nothing returns real
return 0.03
endfunction
private constant function GetPlayerTextOffsetX takes nothing returns real
return 0.00
endfunction
private constant function GetPlayerTextOffsetY takes nothing returns real
return -0.0075
endfunction
private constant function GetPlayerTextGuidePlayerNameOffsetX takes nothing returns real
return 0.04
endfunction
private constant function GetPlayerTextGuidePlayerSelectionOffsetX takes nothing returns real
return 0.08
endfunction
private constant function GetSliderWidth takes nothing returns real
return 0.012
endfunction
private constant function GetSliderOffsetX takes nothing returns real
return -0.006
endfunction
private constant function GetSliderOffsetY takes nothing returns real
return 0.0
endfunction
private constant function GetPlayerFrameCount takes nothing returns integer
return 8
endfunction
private constant function GetPlayerFrameWidthOffset takes nothing returns real
return 0.006
endfunction
private constant function GetPlayerFrameOffsetX takes nothing returns real
return 0.003
endfunction
private constant function GetPlayerFrameOffsetY takes nothing returns real
return 0.0
endfunction
private function GetFrameIndex takes framehandle whichFrame returns integer
return ModuloInteger(GetHandleId(whichFrame) - 0x1000000, 0x8000)
endfunction
struct CustomRaceObserverUI extends array
static integer maxValue = 1
readonly static integer array basePlayerIndex
readonly static framehandle main = null
readonly static framehandle playerContainer = null
readonly static framehandle playerTextGuide = null
readonly static framehandle playerPanelSlider = null
readonly static framehandle array playerTextParams
readonly static framehandle array playerFrame
readonly static framehandle array playerFrameBG
readonly static framehandle array playerFrameHighlight
readonly static framehandle array playerFrameName
readonly static framehandle array playerFrameFaction
private static integer array playerFrameID
private static method initMainFrames takes nothing returns nothing
set main = BlzCreateFrameByType("BACKDROP", "CustomRaceObserverMainFrame", /*
*/ BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), /*
*/ "EscMenuButtonBackdropTemplate", 0)
call BlzFrameSetSize(main, GetObserverFrameWidth(), GetObserverFrameHeight())
call BlzFrameSetAbsPoint(main, FRAMEPOINT_CENTER, GetObserverFrameCenterX(), /*
*/ GetObserverFrameCenterY())
set playerContainer = BlzCreateFrameByType("BACKDROP", "CustomRaceObserverContainer", /*
*/ main, "EscMenuControlBackdropTemplate", 0)
call BlzFrameSetSize(playerContainer, BlzFrameGetWidth(main) - GetContainerWidthOffset() / 2.0, /*
*/ BlzFrameGetWidth(main) - GetContainerHeightOffset())
call BlzFrameSetPoint(playerContainer, GetContainerFramePoint(), main, FRAMEPOINT_BOTTOM, /*
*/ GetContainerOffsetX(), GetContainerOffsetY())
set playerTextGuide = BlzCreateFrameByType("BACKDROP", "CustomRaceObserverTextGuide", /*
*/ main, "EscMenuControlBackdropTemplate", 0)
call BlzFrameSetSize(playerTextGuide, BlzFrameGetWidth(playerContainer), /*
*/ GetPlayerTextGuideHeight())
call BlzFrameSetPoint(playerTextGuide, FRAMEPOINT_BOTTOM, playerContainer, /*
*/ FRAMEPOINT_TOP, GetPlayerTextOffsetX(), GetPlayerTextOffsetY())
set playerPanelSlider = BlzCreateFrameByType("SLIDER", "CustomRaceObserverPlayerPanelSlider", /*
*/ playerContainer, "EscMenuSliderTemplate", 0)
call BlzFrameSetPoint(playerPanelSlider, FRAMEPOINT_LEFT, playerContainer, FRAMEPOINT_RIGHT, /*
*/ GetSliderOffsetX(), GetSliderOffsetY())
call BlzFrameSetSize(playerPanelSlider, GetSliderWidth(), BlzFrameGetHeight(playerContainer))
call BlzFrameSetMinMaxValue(playerPanelSlider, 0.0, 1.0)
call BlzFrameSetValue(playerPanelSlider, 1.0)
set maxValue = 1
//call BlzFrameSetVisible(playerPanelSlider, false)
endmethod
private static method initChildFrames takes nothing returns nothing
local integer i = 1
local real height = BlzFrameGetHeight(playerContainer) / I2R(GetPlayerFrameCount())
local real guideWidth = 0.0
// Create player text parameters
set playerTextParams[1] = BlzCreateFrameByType("TEXT", "CustomRaceObserverTextGuidePlayerName", /*
*/ playerTextGuide, "", 0)
set playerTextParams[2] = BlzCreateFrameByType("TEXT", "CustomRaceObserverTextGuidePlayerSelection", /*
*/ playerTextGuide, "", 0)
set guideWidth = GetPlayerTextGuidePlayerSelectionOffsetX()
call BlzFrameSetSize(playerTextParams[1], guideWidth, BlzFrameGetHeight(playerTextGuide))
set guideWidth = BlzFrameGetWidth(playerTextGuide) - /*
*/ GetPlayerTextGuidePlayerNameOffsetX() - /*
*/ GetPlayerTextGuidePlayerSelectionOffsetX()
call BlzFrameSetSize(playerTextParams[2], guideWidth, BlzFrameGetHeight(playerTextGuide))
call BlzFrameSetPoint(playerTextParams[1], FRAMEPOINT_LEFT, playerTextGuide, FRAMEPOINT_LEFT, /*
*/ GetPlayerTextGuidePlayerNameOffsetX(), 0.0)
call BlzFrameSetPoint(playerTextParams[2], FRAMEPOINT_LEFT, playerTextParams[1], FRAMEPOINT_LEFT, /*
*/ GetPlayerTextGuidePlayerSelectionOffsetX(), 0.0)
call BlzFrameSetText(playerTextParams[1], "|cffffcc00Player:|r")
call BlzFrameSetText(playerTextParams[2], "|cffffcc00Current Faction:|r")
call BlzFrameSetTextAlignment(playerTextParams[1], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_LEFT)
call BlzFrameSetTextAlignment(playerTextParams[2], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_LEFT)
set guideWidth = BlzFrameGetWidth(playerContainer) - GetPlayerFrameWidthOffset()
loop
exitwhen i > GetPlayerFrameCount()
set playerFrame[i] = BlzCreateFrameByType("BUTTON", "CustomRaceObserverPlayerMainPanel", /*
*/ playerContainer, "", i)
set playerFrameBG[i] = BlzCreateFrameByType("BACKDROP", "CustomRaceObserverPlayerMainPanelBG", /*
*/ playerFrame[i], "CustomRaceSimpleBackdropTemplate", /*
*/ i)
set playerFrameID[GetFrameIndex(playerFrame[i])] = i
call BlzFrameSetSize(playerFrame[i], guideWidth, height)
call BlzFrameSetAllPoints(playerFrameBG[i], playerFrame[i])
if i == 1 then
call BlzFrameSetPoint(playerFrame[i], FRAMEPOINT_TOPLEFT, playerContainer, /*
*/ FRAMEPOINT_TOPLEFT, GetPlayerFrameOffsetX(), /*
*/ GetPlayerFrameOffsetY())
else
call BlzFrameSetPoint(playerFrame[i], FRAMEPOINT_TOP, playerFrame[i - 1], /*
*/ FRAMEPOINT_BOTTOM, 0.0, 0.0)
endif
set playerFrameHighlight[i] = BlzCreateFrameByType("HIGHLIGHT", "CustomRaceObserverPlayerMainPanelHighlight", /*
*/ playerFrame[i], "EscMenuButtonMouseOverHighlightTemplate", /*
*/ i)
call BlzFrameSetAllPoints(playerFrameHighlight[i], playerFrame[i])
call BlzFrameSetVisible(playerFrameHighlight[i], false)
set playerFrameName[i] = BlzCreateFrameByType("TEXT", "CustomRaceObserverPlayerPanelPlayerName", /*
*/ playerFrameBG[i], "", i)
set playerFrameFaction[i] = BlzCreateFrameByType("TEXT", "CustomRaceObserverPlayerPanelFaction", /*
*/ playerFrameBG[i], "", i)
call BlzFrameSetSize(playerFrameName[i], BlzFrameGetWidth(playerTextParams[1]), /*
*/ BlzFrameGetHeight(playerTextParams[1]))
call BlzFrameSetSize(playerFrameFaction[i], BlzFrameGetWidth(playerTextParams[2]), /*
*/ BlzFrameGetHeight(playerTextParams[2]))
call BlzFrameSetPoint(playerFrameName[i], FRAMEPOINT_LEFT, playerFrameBG[i], FRAMEPOINT_LEFT, /*
*/ GetPlayerTextGuidePlayerNameOffsetX(), 0.0)
call BlzFrameSetPoint(playerFrameFaction[i], FRAMEPOINT_LEFT, playerFrameName[i], FRAMEPOINT_LEFT, /*
*/ GetPlayerTextGuidePlayerSelectionOffsetX(), 0.0)
call BlzFrameSetTextAlignment(playerFrameName[i], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_LEFT)
call BlzFrameSetTextAlignment(playerFrameFaction[i], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_LEFT)
set i = i + 1
endloop
endmethod
private static method onPlayerPanelEnter takes nothing returns nothing
local player trigPlayer = GetTriggerPlayer()
local integer id = GetFrameIndex(BlzGetTriggerFrame())
local integer i = playerFrameID[id]
if GetLocalPlayer() == trigPlayer then
call BlzFrameSetVisible(playerFrameHighlight[i], true)
endif
endmethod
private static method onPlayerPanelLeave takes nothing returns nothing
local player trigPlayer = GetTriggerPlayer()
local integer id = GetFrameIndex(BlzGetTriggerFrame())
local integer i = playerFrameID[id]
if GetLocalPlayer() == trigPlayer then
call BlzFrameSetVisible(playerFrameHighlight[i], false)
endif
endmethod
private static method onSliderValueChange takes nothing returns nothing
local player trigPlayer = GetTriggerPlayer()
local integer id = GetPlayerId(trigPlayer)
local integer value = R2I(BlzGetTriggerFrameValue() + 0.01)
if CustomRacePSelection.choicedPlayerSize <= GetPlayerFrameCount() then
set basePlayerIndex[id] = 0
return
endif
set basePlayerIndex[id] = CustomRacePSelection.choicedPlayerSize - /*
*/ (GetPlayerFrameCount() + value)
endmethod
private static method addPlayerFrameEvents takes nothing returns nothing
local trigger enterTrig = CreateTrigger()
local trigger leaveTrig = CreateTrigger()
local integer i = 1
loop
exitwhen i > GetPlayerFrameCount()
call BlzTriggerRegisterFrameEvent(enterTrig, playerFrame[i], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(leaveTrig, playerFrame[i], FRAMEEVENT_MOUSE_LEAVE)
set i = i + 1
endloop
call TriggerAddAction(enterTrig, function thistype.onPlayerPanelEnter)
call TriggerAddAction(leaveTrig, function thistype.onPlayerPanelLeave)
// Detect slider change events
set enterTrig = CreateTrigger()
endmethod
private static method init takes nothing returns nothing
call thistype.initMainFrames()
call thistype.initChildFrames()
call thistype.addPlayerFrameEvents()
// Hide the frame upon at its' release state.
call BlzFrameSetVisible(main, false)
static if LIBRARY_FrameLoader then
call FrameLoaderAdd(function thistype.init)
endif
endmethod
implement Init
endstruct
private function CanPlayerSeeUI takes player whichPlayer returns boolean
return CustomRacePSelection.hasUnchoicedPlayer(whichPlayer) or /*
*/ IsPlayerObserver(whichPlayer)
endfunction
public function RenderFrame takes nothing returns nothing
local string factionText = "No Faction Selected"
local integer i = 1
local integer id = GetPlayerId(GetLocalPlayer())
local integer oldBase = CustomRaceObserverUI.maxValue
local real preValue = BlzFrameGetValue(CustomRaceObserverUI.playerPanelSlider)
local CustomRacePSelection obj = 0
local CustomRace faction = 0
local player whichPlayer
// This is guaranteed to be a synchronous action
if CustomRacePSelection.choicedPlayerSize > GetPlayerFrameCount() then
set CustomRaceObserverUI.maxValue = R2I(CustomRacePSelection.choicedPlayerSize - GetPlayerFrameCount() /*
*/ + 0.01)
else
set CustomRaceObserverUI.maxValue = 1
endif
if CustomRaceObserverUI.maxValue != oldBase then
call BlzFrameSetMinMaxValue(CustomRaceObserverUI.playerPanelSlider, 0, /*
*/ CustomRaceObserverUI.maxValue)
call BlzFrameSetValue(CustomRaceObserverUI.playerPanelSlider, preValue + /*
*/ R2I(oldBase - CustomRaceObserverUI.maxValue + 0.01))
endif
loop
exitwhen (i > GetPlayerFrameCount())
if (CustomRaceObserverUI.basePlayerIndex[id] + i > CustomRacePSelection.choicedPlayerSize) then
// Do not display anymore
call BlzFrameSetVisible(CustomRaceObserverUI.playerFrame[i], false)
else
set whichPlayer = CustomRacePSelection.choicedPlayers[CustomRaceObserverUI.basePlayerIndex[id] + i]
set obj = CRPSelection[whichPlayer]
if obj.faction != 0 then
set faction = CustomRace.getRaceFaction(GetPlayerRace(whichPlayer), obj.baseChoice + obj.faction)
set factionText = faction.name
endif
call BlzFrameSetVisible(CustomRaceObserverUI.playerFrame[i], true)
call BlzFrameSetText(CustomRaceObserverUI.playerFrameName[i], /*
*/ GetPlayerName(whichPlayer))
call BlzFrameSetText(CustomRaceObserverUI.playerFrameFaction[i], /*
*/ factionText)
if (not DisplayPlayerFactionChoice()) and /*
*/ (CustomRacePSelection.hasUnchoicedPlayer(GetLocalPlayer())) then
call BlzFrameSetVisible(CustomRaceObserverUI.playerFrameFaction[i], false)
else
call BlzFrameSetVisible(CustomRaceObserverUI.playerFrameFaction[i], true)
endif
endif
set i = i + 1
endloop
call BlzFrameSetVisible(CustomRaceObserverUI.main, CanPlayerSeeUI(GetLocalPlayer()))
endfunction
public function UnrenderFrame takes nothing returns nothing
call BlzFrameSetVisible(CustomRaceObserverUI.main, false)
endfunction
endlibrary
library CustomRaceUserUI requires /*
--------------------------
*/ CustomRaceCore, /*
--------------------------
--------------------------
*/ CustomRaceUI, /*
--------------------------
------------------------------
*/ CustomRacePSelection, /*
------------------------------
----------------------------------
*/ CustomRaceMatchConditions, /*
----------------------------------
----------------------------------
*/ CustomRaceMatch, /*
----------------------------------
----------------------------------
*/ CustomRaceObserverUI, /*
----------------------------------
----------------------
*/ Init, /*
----------------------
------------------------------
*/ optional FrameLoader /*
------------------------------
*/
globals
private constant real INTERVAL = 1.0 / 64.0
private constant real WAIT_DURATION = 30.0
private constant real EASE_IN = 1.75 // 0.5
private constant real EASE_OUT = 0.75 // 0.5
private constant real FINALIZE_DELAY = 0.50 // 0.5
private integer WAIT_MAX_TICKS = R2I(WAIT_DURATION / INTERVAL + 0.01)
private integer EASE_IN_TICKS = R2I(EASE_IN / INTERVAL + 0.01)
private integer EASE_OUT_TICKS = R2I(EASE_OUT / INTERVAL + 0.01)
private constant integer MODE_BAR_UPDATE = 1
private constant integer MODE_EASE_IN = 2
private constant integer MODE_EASE_OUT = 4
private constant real FRAME_START_DELAY = 0.25
private constant real DECAY_RATE = 8.0
private constant real OSCILLATE_RATE = 2.0
private constant real DELAY = 0.15
private constant real E = 2.7182818
endglobals
private constant function GetDefaultTechtreeIconTexture takes nothing returns string
return "UI\\Widgets\\EscMenu\\Human\\editbox-background.blp"
endfunction
// ============================================================================= //
// Originally, the ease in and out functions were going to have different
// responses. However, I liked the final ease in response so much that
// I used it all throughout instead.
// ============================================================================= //
private struct DecayResponse extends array
private static constant integer MAX_STEPS = 1000
private static real STEP_SIZE = 1.0 / I2R(MAX_STEPS)
readonly static real array value
private static method init takes nothing returns nothing
local integer i = 1
local real a = STEP_SIZE
local real mult = Pow(E, -a*DECAY_RATE)
local real curMult = mult
set value[0] = 0.0
loop
exitwhen i > MAX_STEPS
set value[i] = 1 - curMult*Cos(2*bj_PI*OSCILLATE_RATE*a)
set curMult = curMult*mult
set i = i + 1
set a = a + STEP_SIZE
endloop
set value[MAX_STEPS] = 1.0
endmethod
static method getValue takes real x returns real
local integer index
local real modulo
if x <= 0.0 then
return 0.0
elseif x >= 1.0 then
return 1.0
endif
set index = R2I(x / STEP_SIZE)
set modulo = ModuloReal(x, STEP_SIZE) / STEP_SIZE
return value[index]*(1 - modulo) + value[index + 1]*(modulo)
endmethod
implement Init
endstruct
private function StepResponse takes real x returns real
if x > 0 then
return 1.0
endif
return 0.0
endfunction
private function BarUpdateResponse takes real x returns real
return 1.0 - x
endfunction
private function EaseInResponse takes real x returns real
set x = x - 0.25
return DecayResponse.getValue(x)
endfunction
private function EaseOutResponse takes real x returns real
return (x*(x*(x*(x*(0.20*x-0.55)+0.47)-0.1))) / 0.02
endfunction
private function EaseInPosResponse takes real x returns real
return EaseInResponse(x)
endfunction
private function EaseOutPosResponse takes real x returns real
return EaseOutResponse(x)
endfunction
private constant function GetMainFrameStartCenterX takes nothing returns real
return CustomRaceUI_GetMainFrameCenterX()
endfunction
private constant function GetMainFrameStartCenterY takes nothing returns real
return 0.30
endfunction
private struct FrameInterpolation extends array
readonly static timer timer = CreateTimer()
private static constant integer MAX_POWER_INDEX = 3
private static integer instanceCount = 0
private static integer array powersOf2
private static integer array tickMap
private static thistype array activeInstances
static integer array currentTicks
static integer array maxTicks
readonly integer mode
readonly integer activeTicks
private static method getPowerIndex takes integer mode returns integer
if mode == MODE_EASE_OUT then
return 3
elseif mode == MODE_EASE_IN then
return 2
elseif mode == MODE_BAR_UPDATE then
return 1
endif
return 0
endmethod
private method setTick takes integer mode, integer newval returns nothing
set mode = thistype.getPowerIndex(mode)
set currentTicks[MAX_POWER_INDEX*integer(this) + mode] = newval
endmethod
private method getTick takes integer mode returns integer
set mode = thistype.getPowerIndex(mode)
return currentTicks[MAX_POWER_INDEX*integer(this) + mode]
endmethod
private method getMaxTick takes integer mode returns integer
set mode = thistype.getPowerIndex(mode)
return maxTicks[MAX_POWER_INDEX*integer(this) + mode]
endmethod
private static method onUpdate takes nothing returns nothing
local integer i = 1
local integer alpha = 0
local thistype this
local real ratio
local real posRatio
local real cx
local real cy
local player whichPlayer
loop
exitwhen i > instanceCount
set this = activeInstances[i]
set whichPlayer = Player(integer(this))
if BlzBitAnd(this.mode, MODE_BAR_UPDATE) != 0 then
call this.setTick(MODE_BAR_UPDATE, this.getTick(MODE_BAR_UPDATE) + 1)
set ratio = I2R(this.getTick(MODE_BAR_UPDATE)) / /*
*/ I2R(this.getMaxTick(MODE_BAR_UPDATE))
set ratio = BarUpdateResponse(ratio)
if this.getTick(MODE_BAR_UPDATE) >= this.getMaxTick(MODE_BAR_UPDATE) then
set this.mode = this.mode - MODE_BAR_UPDATE
endif
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setBarProgress(ratio)
endif
endif
if BlzBitAnd(this.mode, MODE_EASE_IN) != 0 then
if (GetLocalPlayer() == whichPlayer) and /*
*/ (not CustomRaceInterface.isMainVisible()) then
call CustomRaceInterface.setMainVisible(true)
endif
call this.setTick(MODE_EASE_IN, this.getTick(MODE_EASE_IN) + 1)
set ratio = I2R(this.getTick(MODE_EASE_IN)) / /*
*/ I2R(this.getMaxTick(MODE_EASE_IN))
set posRatio = EaseInPosResponse(ratio)
set ratio = EaseInResponse(ratio)
set cx = GetMainFrameStartCenterX()*(1 - posRatio) + /*
*/ CustomRaceUI_GetMainFrameCenterX()*posRatio
set cy = GetMainFrameStartCenterY()*(1 - posRatio) + /*
*/ CustomRaceUI_GetMainFrameCenterY()*posRatio
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setMainAlpha(ratio)
call CustomRaceInterface.setMainPos(cx, cy)
endif
if this.getTick(MODE_EASE_IN) >= this.getMaxTick(MODE_EASE_IN) then
set this.mode = this.mode - MODE_EASE_IN
endif
elseif BlzBitAnd(this.mode, MODE_EASE_OUT) != 0 then
call this.setTick(MODE_EASE_OUT, this.getTick(MODE_EASE_OUT) + 1)
set ratio = I2R(this.getTick(MODE_EASE_OUT)) / /*
*/ I2R(this.getMaxTick(MODE_EASE_OUT))
set posRatio = EaseOutPosResponse(ratio)
set ratio = 1.0 - RMinBJ(EaseOutResponse(ratio), 1.0)
set cx = CustomRaceUI_GetMainFrameCenterX()*(1 - posRatio) + /*
*/ GetMainFrameStartCenterX()*posRatio
set cy = CustomRaceUI_GetMainFrameCenterY()*(1 - posRatio) + /*
*/ GetMainFrameStartCenterY()*posRatio
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setMainAlpha(ratio)
call CustomRaceInterface.setMainPos(cx, cy)
endif
if this.getTick(MODE_EASE_OUT) >= this.getMaxTick(MODE_EASE_OUT) then
set this.mode = this.mode - MODE_EASE_OUT
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setMainVisible(false)
call CustomRaceInterface.setMainAlpha(1.0)
endif
endif
endif
// Remove this instance from the instance list
if this.mode == 0 then
set activeInstances[i] = activeInstances[instanceCount]
set instanceCount = instanceCount - 1
set i = i - 1
endif
set i = i + 1
endloop
if instanceCount <= 0 then
call PauseTimer(timer)
endif
endmethod
private static method startTimer takes nothing returns nothing
if instanceCount == 1 then
call TimerStart(timer, INTERVAL, true, function thistype.onUpdate)
endif
endmethod
private method setCurrentTicks takes integer mode returns nothing
set mode = thistype.getPowerIndex(mode)
set currentTicks[MAX_POWER_INDEX*integer(this) + mode] = 0
set maxTicks[MAX_POWER_INDEX*integer(this) + mode] = tickMap[mode]
endmethod
method isTransitioning takes integer mode returns boolean
return BlzBitAnd(this.mode, mode) != 0
endmethod
method stop takes integer mode returns boolean
if not this.isTransitioning(mode) then
return false
endif
call this.setTick(mode, this.getTick(mode) - 1)
set this.mode = this.mode - mode
return true
endmethod
method request takes integer mode returns boolean
if BlzBitAnd(this.mode, mode) != 0 then
return false
endif
if this.mode == 0 then
set instanceCount = instanceCount + 1
set activeInstances[instanceCount] = this
call thistype.startTimer()
endif
set this.mode = this.mode + mode
call this.setCurrentTicks(mode)
return true
endmethod
static method [] takes player whichPlayer returns thistype
return thistype(GetPlayerId(whichPlayer))
endmethod
private static method init takes nothing returns nothing
set powersOf2[1] = 1
set powersOf2[2] = 2
set powersOf2[3] = 4
set tickMap[1] = WAIT_MAX_TICKS
set tickMap[2] = EASE_IN_TICKS
set tickMap[3] = EASE_OUT_TICKS
endmethod
implement Init
endstruct
// ========================================================================== //
// UI Drawing API //
// ========================================================================== //
private function GetObjectIdDescription takes integer objectID returns string
return BlzGetAbilityExtendedTooltip(objectID, 0)
endfunction
private function GetObjectIdIcon takes integer objectID returns string
return BlzGetAbilityIcon(objectID)
endfunction
private function GetObjectIdFromChunk takes integer i, integer j, integer baseTechID, CustomRace faction /*
*/ returns integer
if (i == 1) then
return faction.getUnit(baseTechID + j)
elseif (i == 2) then
return faction.getStructure(baseTechID + j)
elseif (i == 3) then
return faction.getHero(baseTechID + j)
endif
return 0
endfunction
private function GetTechtreeArrowMaxValue takes integer i, CustomRace faction returns integer
if (i == 1) then
return faction.getUnitMaxIndex()
elseif (i == 2) then
return faction.getStructureMaxIndex()
elseif (i == 3) then
return faction.getHeroMaxIndex()
endif
return 0
endfunction
private function CheckTechtreeChunkForDraw takes integer i, CustomRace faction returns boolean
return GetTechtreeArrowMaxValue(i, faction) > 0
endfunction
private function CheckTechtreeIconForDraw takes integer i, integer j, CustomRace faction, /*
*/ CustomRacePSelection obj returns boolean
// Draw units?
local integer max = GetTechtreeArrowMaxValue(i, faction)
return obj.getBaseTechID(i) + j <= max
endfunction
private function DrawTechtreeIcon takes player whichPlayer, integer i, integer j, integer baseTechID, /*
*/ CustomRace faction, CustomRacePSelection obj returns nothing
local integer objectID = GetObjectIdFromChunk(i, j, baseTechID, faction)
local integer baseIndex = (i-1)*CustomRaceInterface.iconsPerChunk
local string desc = ""
/*
if (i == 1) then
set objectID = faction.getUnit(baseTechID + j)
elseif (i == 2) then
set objectID = faction.getStructure(baseTechID + j)
endif
*/
set desc = GetObjectIdIcon(objectID)
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setTechtreeIconVisible(baseIndex + j, true)
call CustomRaceInterface.setTechtreeIconDisplayByID(baseIndex + j, desc)
endif
endfunction
private function DrawUIFromPlayerData takes player whichPlayer, CustomRacePSelection obj returns nothing
local integer i = 1
local integer j = 0
local integer objectID = 0
local integer baseIndex = 0
local race pRace = GetPlayerRace(whichPlayer)
local CustomRace faction = 0
// Draw the choice buttons first
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setChoiceArrowVisible(true, obj.baseChoice != 0)
call CustomRaceInterface.setSliderVisible(CustomRace.getRaceFactionCount(pRace) > CustomRaceUI_GetMaxDisplayChoices())
call BlzFrameSetEnable(CustomRaceInterface.confirmFrame, obj.faction != 0)
endif
loop
exitwhen i > CustomRaceUI_GetMaxDisplayChoices()
if obj.baseChoice + i > CustomRace.getRaceFactionCount(pRace) then
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setChoiceName(i, "")
call CustomRaceInterface.setChoiceButtonVisible(i, false)
endif
else
if (GetLocalPlayer() == whichPlayer) then
call CustomRaceInterface.setChoiceArrowVisible(false, /*
*/ obj.baseChoice + i < CustomRace.getRaceFactionCount(pRace))
endif
set faction = CustomRace.getRaceFaction(pRace, obj.baseChoice + i)
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setChoiceName(i, faction.name)
call CustomRaceInterface.setChoiceButtonVisible(i, true)
endif
endif
set i = i + 1
endloop
// If a faction was selected, show the name, display
// and race description, and continue with techtree
// visuals. Otherwise, hide techtree visuals.
if obj.focusFaction != 0 then
set faction = CustomRace.getRaceFaction(pRace, obj.baseChoice + obj.focusFaction)
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setFactionNameVisible(true)
call CustomRaceInterface.setFactionName(faction.name)
call CustomRaceInterface.setDescription(faction.desc)
call CustomRaceInterface.setFactionDisplay(faction.racePic)
endif
else
set faction = 0
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setFactionNameVisible(false)
call CustomRaceInterface.setFactionName("")
call CustomRaceInterface.setDescription("")
call CustomRaceInterface.setFactionDisplay("")
endif
endif
// Since no faction in particular was selected, terminate
// the drawing process here by hiding the techtree chunks.
if (faction == 0) then
set i = 1
loop
exitwhen i > CustomRaceUI_GetTechtreeChunkCount()
call obj.setBaseTechID(i, 0)
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setTechtreeChunkVisible(i, false)
endif
set i = i + 1
endloop
set obj.focusTechtree = 0
set obj.focusTechID = 0
set obj.techtree = 0
set obj.focusTechtreeStack = 0
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setTooltipVisible(false)
endif
return
endif
// Draw the techtree chunks if data exists
set i = 1
loop
exitwhen i > CustomRaceUI_GetTechtreeChunkCount()
loop
if CheckTechtreeChunkForDraw(i, faction) then
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setTechtreeChunkVisible(i, true)
endif
else
/*
if obj.focusTechtree == i then
set obj.focusTechtree = 0
set obj.focusTechID = 0
set obj.techtree = 0
set obj.focusTechtreeStack = 0
call obj.setBaseTechID(i, 0)
endif
*/
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setTechtreeChunkVisible(i, false)
endif
exitwhen true
endif
set j = 1
set baseIndex = (i-1)*CustomRaceInterface.iconsPerChunk
// Draw the choice buttons first
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setTechtreeArrowVisible(i, true, obj.getBaseTechID(i) != 0)
endif
loop
exitwhen j > CustomRaceInterface.iconsPerChunk
if CheckTechtreeIconForDraw(i, j, faction, obj) then
call DrawTechtreeIcon(whichPlayer, i, j, obj.getBaseTechID(i), faction, obj)
if (GetLocalPlayer() == whichPlayer) then
call CustomRaceInterface.setTechtreeArrowVisible(i, false, /*
*/ (obj.getBaseTechID(i) + j) < /*
*/ (GetTechtreeArrowMaxValue(i, faction)))
endif
else
if (GetLocalPlayer() == whichPlayer) then
call CustomRaceInterface.setTechtreeIconVisible(baseIndex + j, false)
endif
endif
set j = j + 1
endloop
exitwhen true
endloop
set i = i + 1
endloop
// If a techtree icon was selected, show the name, display
// and race description.
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setTooltipVisible(obj.focusTechID != 0)
call CustomRaceInterface.setTooltipName("")
call CustomRaceInterface.setTooltipDesc("")
endif
if (obj.focusTechID != 0) then
set objectID = GetObjectIdFromChunk(obj.focusTechtree, obj.focusTechID, /*
*/ obj.getBaseTechID(obj.focusTechtree), faction)
if GetLocalPlayer() == whichPlayer then
call CustomRaceInterface.setTooltipName(GetObjectName(objectID))
call CustomRaceInterface.setTooltipDesc(GetObjectIdDescription(objectID))
endif
endif
endfunction
// ========================================================================== //
// Sound Generating Functions //
// ========================================================================== //
globals
private sound tempSound = null
endglobals
private function GenerateClunkDownSound takes nothing returns sound
set tempSound = CreateSound("Sound\\Interface\\LeftAndRightGlueScreenPopDown.wav", false, false, false, 10, 10, "DefaultEAXON")
call SetSoundParamsFromLabel(tempSound, "BothGlueScreenPopDown")
call SetSoundDuration(tempSound, 2246)
call SetSoundVolume(tempSound, 127)
return tempSound
endfunction
private function GenerateClunkUpSound takes nothing returns sound
set tempSound = CreateSound("Sound\\Interface\\LeftAndRightGlueScreenPopUp.wav", false, false, false, 10, 10, "DefaultEAXON")
call SetSoundParamsFromLabel(tempSound, "BothGlueScreenPopUp")
call SetSoundDuration(tempSound, 1953)
call SetSoundVolume(tempSound, 127)
return tempSound
endfunction
private function GenerateClickSound takes nothing returns sound
set tempSound = CreateSound("Sound\\Interface\\MouseClick1.wav", false, false, false, 10, 10, "")
call SetSoundParamsFromLabel(tempSound, "InterfaceClick")
call SetSoundDuration(tempSound, 239)
return tempSound
endfunction
private function GenerateWarningSound takes nothing returns sound
set tempSound = CreateSound("Sound\\Interface\\CreepAggroWhat1.wav", false, false, false, 10, 10, "DefaultEAXON")
call SetSoundParamsFromLabel(tempSound, "CreepAggro")
call SetSoundDuration(tempSound, 1236)
return tempSound
endfunction
private function PlaySoundForPlayer takes sound snd, player whichPlayer returns nothing
if GetLocalPlayer() == whichPlayer then
call SetSoundVolume(snd, 127)
else
call SetSoundVolume(snd, 0)
endif
call StartSound(snd)
call KillSoundWhenDone(snd)
endfunction
// ========================================================================== //
// ========================================================================== //
globals
private code onDefaultFinalize = null
private timer finalizer = null
endglobals
private function IsFrameUseable takes player whichPlayer returns boolean
return (not FrameInterpolation[whichPlayer].isTransitioning(MODE_EASE_IN)) and /*
*/ (not FrameInterpolation[whichPlayer].isTransitioning(MODE_EASE_OUT))
endfunction
private function HideCustomFrame takes player whichPlayer returns nothing
if FrameInterpolation[whichPlayer].isTransitioning(MODE_EASE_IN) then
return
endif
call PlaySoundForPlayer(GenerateClunkUpSound(), whichPlayer)
call FrameInterpolation[whichPlayer].request(MODE_EASE_OUT)
endfunction
private function ShowCustomFrame takes player whichPlayer returns nothing
if FrameInterpolation[whichPlayer].isTransitioning(MODE_EASE_OUT) then
return
endif
call PlaySoundForPlayer(GenerateClunkDownSound(), whichPlayer)
call FrameInterpolation[whichPlayer].request(MODE_EASE_IN)
endfunction
// ========================================================================== //
// ========================================================================== //
private function HideCustomFrameEnum takes nothing returns nothing
call HideCustomFrame(GetEnumPlayer())
endfunction
private function HideCustomFrameAll takes nothing returns nothing
call ForForce(CustomRaceForce.activePlayers, function HideCustomFrameEnum)
endfunction
private function ShowCustomFrameEnum takes nothing returns nothing
if CustomRacePSelection.hasChoicedPlayer(GetEnumPlayer()) then
call ShowCustomFrame(GetEnumPlayer())
endif
endfunction
private function ShowCustomFrameAll takes nothing returns nothing
call ForForce(CustomRaceForce.activePlayers, function ShowCustomFrameEnum)
endfunction
// ========================================================================== //
// ========================================================================== //
private function OnBarTimerStart takes nothing returns nothing
local integer i = 1
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
// Apply timer only when in a multiplayer setting
// or when the following flag is true.
if (not CustomRaceMatch_APPLY_TIMER_IN_SINGLE_PLAYER) and /*
*/ (CustomRacePSelection.isSinglePlayer) then
return
endif
loop
exitwhen i > CustomRacePSelection.choicedPlayerSize
call FrameInterpolation[CustomRacePSelection.choicedPlayers[i]].request(MODE_BAR_UPDATE)
set i = i + 1
endloop
set finalizer = CreateTimer()
call TimerStart(finalizer, WAIT_DURATION, false, onDefaultFinalize)
endfunction
private function OnPrepUI takes nothing returns nothing
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
call TimerStart(CreateTimer(), EASE_IN, false, function OnBarTimerStart)
call ShowCustomFrameAll()
endfunction
private function PrepUI takes nothing returns nothing
local integer i = 1
loop
exitwhen i > CustomRaceUI_GetTechtreeChunkCount()
call CustomRaceInterface.setTechtreeChunkVisible(i, false)
set i = i + 1
endloop
set i = 1
loop
exitwhen i > CustomRaceUI_GetMaxDisplayChoices()
call CustomRaceInterface.setChoiceButtonVisible(i, false)
set i = i + 1
endloop
call CustomRaceInterface.setSliderVisible(false)
call CustomRaceInterface.setFactionNameVisible(false)
call CustomRaceInterface.setFactionDisplay("")
call CustomRaceInterface.setBarProgress(1.00)
call CustomRaceInterface.setTooltipVisible(false)
// No players with a choice in their faction..
if CustomRacePSelection.choicedPlayerSize < 1 then
return
endif
call TimerStart(CreateTimer(), FRAME_START_DELAY, false, function OnPrepUI)
endfunction
// ========================================================================== //
// ========================================================================== //
private struct UIFrameEvents extends array
readonly static trigger abandonTrig = CreateTrigger()
// ========================================================================== //
// Finalization Events //
// ========================================================================== //
static method onFinalizeEnd takes nothing returns nothing
call DisableTrigger(thistype.abandonTrig)
call DestroyTrigger(thistype.abandonTrig)
call CustomRaceMatch_MeleeInitializationFinish()
endmethod
static method finalize takes nothing returns nothing
call TimerStart(CreateTimer(), FINALIZE_DELAY + EASE_OUT, false, /*
*/ function thistype.onFinalizeEnd)
endmethod
static method onFinalize takes nothing returns nothing
local player indexPlayer
local CustomRacePSelection obj
local integer i
loop
exitwhen CustomRacePSelection.choicedPlayerSize < 1
set indexPlayer = CustomRacePSelection.choicedPlayers[CustomRacePSelection.choicedPlayerSize]
set obj = CRPSelection[indexPlayer]
set obj.focusFaction = 0
set obj.faction = 0
set obj.focusFactionStack = 0
set obj.techtree = 0
set obj.focusTechtree = 0
set obj.focusTechID = 0
set obj.focusTechtreeStack = 0
set i = 1
loop
exitwhen i > CustomRaceUI_GetTechtreeChunkCount()
call obj.setBaseTechID(i, 0)
set i = i + 1
endloop
call DrawUIFromPlayerData(indexPlayer, obj)
set obj.faction = 1
call CustomRacePSelection.removeChoicedPlayer(indexPlayer)
call CustomRacePSelection.addUnchoicedPlayer(indexPlayer)
call HideCustomFrame(indexPlayer)
endloop
call thistype.finalize()
endmethod
static method onExpireFinalize takes nothing returns nothing
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
call thistype.onFinalize()
endmethod
// ========================================================================== //
// Enter and Leave Events //
// ========================================================================== //
static method onChoiceButtonEnter takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local integer i = CustomRaceInterface.getChoiceButtonID(zabutton)
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
if obj.focusFactionStack < 1 then
set obj.focusFaction = i
endif
set obj.focusFactionStack = obj.focusFactionStack + 1
if not IsFrameUseable(trigPlayer) then
return
endif
call DrawUIFromPlayerData(trigPlayer, obj)
endmethod
static method onChoiceButtonLeave takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local integer i = CustomRaceInterface.getChoiceButtonID(zabutton)
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
// obj.focusFactionStack can only be equal to 2
// if a choice button was already selected.
if obj.focusFactionStack < 2 then
set obj.focusFaction = 0
endif
set obj.focusFactionStack = obj.focusFactionStack - 1
if not IsFrameUseable(trigPlayer) then
return
endif
call DrawUIFromPlayerData(trigPlayer, obj)
endmethod
static method onTechtreeIconEnter takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local integer i = CustomRaceInterface.getTechtreeIconID(zabutton)
local integer curChunk = CustomRaceInterface.getChunkFromIndex(i)
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
if obj.focusTechtreeStack < 1 then
set obj.focusTechtree = curChunk
set obj.focusTechID = ModuloInteger(i, CustomRaceInterface.iconsPerChunk)
if obj.focusTechID == 0 then
set obj.focusTechID = CustomRaceInterface.iconsPerChunk
endif
endif
set obj.focusTechtreeStack = obj.focusTechtreeStack + 1
if not IsFrameUseable(trigPlayer) then
return
endif
call DrawUIFromPlayerData(trigPlayer, obj)
endmethod
static method onTechtreeIconLeave takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local integer i = CustomRaceInterface.getTechtreeIconID(zabutton)
local integer curChunk = CustomRaceInterface.getChunkFromIndex(i)
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
if obj.focusTechtreeStack < 2 then
set obj.focusTechtree = 0
set obj.focusTechID = 0
endif
set obj.focusTechtreeStack = obj.focusTechtreeStack - 1
if not IsFrameUseable(trigPlayer) then
return
endif
call DrawUIFromPlayerData(trigPlayer, obj)
endmethod
// ========================================================================== //
// Click Events //
// ========================================================================== //
static method onChoiceButtonClick takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local integer i = CustomRaceInterface.getChoiceButtonID(zabutton)
local integer j = 0
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
// obj.focusFactionStack can only be equal to 2
// if a choice button was already selected.
if obj.faction == 0 then
set obj.focusFactionStack = obj.focusFactionStack + 1
set obj.focusFaction = i
set obj.faction = obj.focusFaction
else
set obj.focusFactionStack = obj.focusFactionStack - 1
if GetLocalPlayer() == trigPlayer then
call BlzFrameSetEnable(CustomRaceInterface.getChoiceButton(obj.focusFaction), false)
call BlzFrameSetEnable(CustomRaceInterface.getChoiceButton(obj.focusFaction), true)
endif
if i != obj.focusFaction then
set obj.focusFactionStack = obj.focusFactionStack + 1
set obj.focusFaction = i
set obj.faction = i
// Update techtree tooltip and icons as well
set obj.focusTechtree = 0
set obj.focusTechID = 0
set obj.techtree = 0
set obj.focusTechtreeStack = 0
set j = 1
loop
exitwhen j > CustomRaceUI_GetTechtreeChunkCount()
call obj.setBaseTechID(j, 0)
set j = j + 1
endloop
else
set obj.faction = 0
endif
endif
if not IsFrameUseable(trigPlayer) then
return
endif
call DrawUIFromPlayerData(trigPlayer, obj)
call CustomRaceObserverUI_RenderFrame()
endmethod
static method onTechtreeIconClick takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local integer i = CustomRaceInterface.getTechtreeIconID(zabutton)
local integer index = 0
local integer curChunk = CustomRaceInterface.getChunkFromIndex(i)
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
if obj.techtree == 0 then
set obj.focusTechtreeStack = obj.focusTechtreeStack + 1
set obj.focusTechtree = curChunk
set obj.focusTechID = ModuloInteger(i, CustomRaceInterface.iconsPerChunk)
if obj.focusTechID == 0 then
set obj.focusTechID = CustomRaceInterface.iconsPerChunk
endif
set obj.techtree = obj.focusTechID
else
set obj.focusTechtreeStack = obj.focusTechtreeStack - 1
set index = (obj.focusFaction-1)
set index = index*CustomRaceInterface.iconsPerChunk + obj.focusTechID
if GetLocalPlayer() == trigPlayer then
call BlzFrameSetEnable(CustomRaceInterface.getTechtreeIconRaw(index), false)
call BlzFrameSetEnable(CustomRaceInterface.getTechtreeIconRaw(index), true)
endif
set i = ModuloInteger(i, CustomRaceInterface.iconsPerChunk)
if i == 0 then
set i = CustomRaceInterface.iconsPerChunk
endif
if i != obj.focusTechID then
set obj.focusTechtreeStack = obj.focusTechtreeStack + 1
set obj.focusTechtree = curChunk
set obj.focusTechID = i
set obj.techtree = obj.focusTechID
else
set obj.techtree = 0
endif
endif
if not IsFrameUseable(trigPlayer) then
return
endif
call DrawUIFromPlayerData(trigPlayer, obj)
endmethod
static method onChoiceArrowClick takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local integer i = CustomRaceInterface.getChoiceArrowID(zabutton)
local player trigPlayer = GetTriggerPlayer()
local integer incr = 1
call PlaySoundForPlayer(GenerateClickSound(), trigPlayer)
if BlzBitAnd(i, 1) == 0 then
set incr = -1
endif
if GetLocalPlayer() == trigPlayer then
call CustomRaceInterface.setSliderValue(CustomRaceInterface.getSliderValue() + incr)
endif
endmethod
static method onTechtreeArrowClick takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local integer i = CustomRaceInterface.getTechtreeArrowID(zabutton)
local integer techChunk = ((i - 1) / CustomRaceUI_GetTechtreeChunkCount()) + 1
local integer inc = CustomRaceUI_GetTechtreeIconColumnMax()
local integer newBase = 0
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
// If the direction is up, decrease the base choice instead.
if BlzBitAnd(i, 1) != 0 then
set inc = -inc
endif
set newBase = IMaxBJ(obj.getBaseTechID(techChunk) + inc, 0)
if newBase == obj.getBaseTechID(techChunk) then
return
endif
call obj.setBaseTechID(techChunk, newBase)
if obj.techtree != 0 then
set obj.techtree = obj.techtree - inc
// The techtree icon previously highlighted is out of bounds
if (obj.techtree <= 0) or (obj.techtree > CustomRaceInterface.iconsPerChunk) then
set obj.techtree = 0
set obj.focusTechID = 0
set obj.focusTechtree = 0
set obj.focusTechtreeStack = 0
else
set obj.focusTechID = obj.techtree
endif
endif
if not IsFrameUseable(trigPlayer) then
return
endif
call DrawUIFromPlayerData(trigPlayer, obj)
endmethod
static method onConfirmButtonClick takes nothing returns nothing
local framehandle zabutton = BlzGetTriggerFrame()
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
local integer finalChoice = 0
local integer i = 0
// obj.focusFactionStack can only be equal to 2
// if a choice button was already selected.
if obj.faction == 0 then
call PlaySoundForPlayer(GenerateWarningSound(), trigPlayer)
return
endif
if not IsFrameUseable(trigPlayer) then
return
endif
set finalChoice = obj.faction + obj.baseChoice
set obj.focusFaction = 0
set obj.faction = 0
set obj.focusFactionStack = 0
set obj.techtree = 0
set obj.focusTechtree = 0
set obj.focusTechID = 0
set obj.focusTechtreeStack = 0
loop
exitwhen i > CustomRaceUI_GetTechtreeChunkCount()
call obj.setBaseTechID(i, 0)
set i = i + 1
endloop
call DrawUIFromPlayerData(trigPlayer, obj)
set obj.faction = finalChoice
call CustomRacePSelection.removeChoicedPlayer(trigPlayer)
call CustomRacePSelection.addUnchoicedPlayer(trigPlayer)
call FrameInterpolation[trigPlayer].stop(MODE_BAR_UPDATE)
call HideCustomFrame(trigPlayer)
call CustomRaceObserverUI_RenderFrame()
if CustomRacePSelection.choicedPlayerSize < 1 then
call PauseTimer(finalizer)
call DestroyTimer(finalizer)
call thistype.finalize()
call CustomRaceObserverUI_UnrenderFrame()
endif
endmethod
// ========================================================================== //
// Scroll Events //
// ========================================================================== //
static method onSliderValueChange takes nothing returns nothing
local integer value = R2I(BlzGetTriggerFrameValue() + 0.01)
local integer maxBase = 0
local integer newBase = 0
local integer oldBase = 0
local player trigPlayer = GetTriggerPlayer()
local CustomRacePSelection obj = CRPSelection[trigPlayer]
set maxBase = IMaxBJ(CustomRace.getRaceFactionCount(GetPlayerRace(trigPlayer)) - CustomRaceUI_GetMaxDisplayChoices(), /*
*/ 0)
set newBase = maxBase - value
if obj.baseChoice == newBase then
return
endif
set oldBase = obj.baseChoice
set obj.baseChoice = newBase
if GetLocalPlayer() == trigPlayer then
call BlzFrameSetFocus(CustomRaceInterface.getChoiceButton(obj.faction), false)
endif
if obj.faction != 0 then
set obj.focusFaction = obj.focusFaction + oldBase - obj.baseChoice
set obj.faction = obj.focusFaction
// If the faction in focus is out of the list of choices displayed,
// clear out.
if (obj.focusFaction <= 0) or (obj.focusFaction > CustomRaceUI_GetMaxDisplayChoices()) then
set obj.focusFaction = 0
set obj.faction = 0
set obj.focusFactionStack = 0
set obj.focusTechtree = 0
set obj.focusTechID = 0
set obj.techtree = 0
set obj.focusTechtreeStack = 0
endif
endif
if not IsFrameUseable(trigPlayer) then
return
endif
call DrawUIFromPlayerData(trigPlayer, obj)
endmethod
static method onSliderDetectWheel takes nothing returns nothing
local real value = BlzGetTriggerFrameValue()
if value > 0 then
call CustomRaceInterface.setSliderValue(CustomRaceInterface.getSliderValue() + 1)
else
call CustomRaceInterface.setSliderValue(CustomRaceInterface.getSliderValue() - 1)
endif
endmethod
// ========================================================================== //
// Abandon Event //
// ========================================================================== //
static method onPlayerAbandon takes nothing returns nothing
local player trigPlayer = GetTriggerPlayer()
call FrameInterpolation[trigPlayer].stop(MODE_BAR_UPDATE)
call CustomRacePSelection.removeChoicedPlayer(trigPlayer)
call CustomRacePSelection.removeUnchoicedPlayer(trigPlayer)
if CustomRacePSelection.choicedPlayerSize < 1 then
call PauseTimer(finalizer)
call DestroyTimer(finalizer)
call thistype.finalize()
call CustomRaceObserverUI_UnrenderFrame()
else
call CustomRaceObserverUI_RenderFrame()
endif
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.0, 0.0, 20.0, /*
*/ GetPlayerName(trigPlayer) + " has abandoned the game.")
endmethod
private static method init takes nothing returns nothing
set onDefaultFinalize = function thistype.onExpireFinalize
endmethod
implement Init
endstruct
private function PrepHoverEvents takes nothing returns nothing
local trigger enterTrig = CreateTrigger()
local trigger leaveTrig = CreateTrigger()
local integer i = 1
local integer j = 1
// Add events to the choice buttons
loop
exitwhen i > CustomRaceUI_GetMaxDisplayChoices()
call BlzTriggerRegisterFrameEvent(enterTrig, CustomRaceInterface.getChoiceButton(i), /*
*/FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(leaveTrig, CustomRaceInterface.getChoiceButton(i), /*
*/FRAMEEVENT_MOUSE_LEAVE)
set i = i + 1
endloop
call TriggerAddAction(enterTrig, function UIFrameEvents.onChoiceButtonEnter)
call TriggerAddAction(leaveTrig, function UIFrameEvents.onChoiceButtonLeave)
// Add events to the techtree buttons
set enterTrig = CreateTrigger()
set leaveTrig = CreateTrigger()
set i = 1
set j = CustomRaceUI_GetTechtreeChunkCount()*CustomRaceUI_GetTechtreeIconColumnMax()
set j = j*CustomRaceUI_GetTechtreeIconRowMax()
loop
exitwhen i > j
call BlzTriggerRegisterFrameEvent(enterTrig, CustomRaceInterface.getTechtreeIconRaw(i), /*
*/FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(leaveTrig, CustomRaceInterface.getTechtreeIconRaw(i), /*
*/FRAMEEVENT_MOUSE_LEAVE)
set i = i + 1
endloop
call TriggerAddAction(enterTrig, function UIFrameEvents.onTechtreeIconEnter)
call TriggerAddAction(leaveTrig, function UIFrameEvents.onTechtreeIconLeave)
endfunction
private function PrepClickEvents takes nothing returns nothing
local trigger clickTrig = CreateTrigger()
local integer i = 1
local integer j = 1
// Add events to the choice buttons
loop
exitwhen i > CustomRaceUI_GetMaxDisplayChoices()
call BlzTriggerRegisterFrameEvent(clickTrig, CustomRaceInterface.getChoiceButton(i), /*
*/FRAMEEVENT_CONTROL_CLICK)
set i = i + 1
endloop
call TriggerAddAction(clickTrig, function UIFrameEvents.onChoiceButtonClick)
// Add events to the techtree buttons
set clickTrig = CreateTrigger()
set i = 1
set j = CustomRaceUI_GetTechtreeChunkCount()*CustomRaceUI_GetTechtreeIconColumnMax()
set j = j*CustomRaceUI_GetTechtreeIconRowMax()
loop
exitwhen i > j
call BlzTriggerRegisterFrameEvent(clickTrig, CustomRaceInterface.getTechtreeIconRaw(i), /*
*/FRAMEEVENT_CONTROL_CLICK)
set i = i + 1
endloop
call TriggerAddAction(clickTrig, function UIFrameEvents.onTechtreeIconClick)
// Add events to the choice arrows
set clickTrig = CreateTrigger()
call BlzTriggerRegisterFrameEvent(clickTrig, CustomRaceInterface.getChoiceArrow(true), /*
*/ FRAMEEVENT_CONTROL_CLICK)
call BlzTriggerRegisterFrameEvent(clickTrig, CustomRaceInterface.getChoiceArrow(false), /*
*/ FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(clickTrig, function UIFrameEvents.onChoiceArrowClick)
// Add events to the techtree arrows
set clickTrig = CreateTrigger()
set i = 1
set j = CustomRaceUI_GetTechtreeChunkCount()
loop
exitwhen i > j
call BlzTriggerRegisterFrameEvent(clickTrig, CustomRaceInterface.getTechtreeArrow(i, true), /*
*/FRAMEEVENT_CONTROL_CLICK)
call BlzTriggerRegisterFrameEvent(clickTrig, CustomRaceInterface.getTechtreeArrow(i, false), /*
*/FRAMEEVENT_CONTROL_CLICK)
set i = i + 1
endloop
call TriggerAddAction(clickTrig, function UIFrameEvents.onTechtreeArrowClick)
// Add the final event to the confirm button.
set clickTrig = CreateTrigger()
call BlzTriggerRegisterFrameEvent(clickTrig, CustomRaceInterface.confirmFrame, /*
*/FRAMEEVENT_CONTROL_CLICK)
call TriggerAddAction(clickTrig, function UIFrameEvents.onConfirmButtonClick)
endfunction
private function PrepScrollEvents takes nothing returns nothing
local trigger trig = CreateTrigger()
call BlzTriggerRegisterFrameEvent(trig, CustomRaceInterface.slider, /*
*/FRAMEEVENT_SLIDER_VALUE_CHANGED)
call TriggerAddAction(trig, function UIFrameEvents.onSliderValueChange)
set trig = CreateTrigger()
call BlzTriggerRegisterFrameEvent(trig, CustomRaceInterface.slider, /*
*/FRAMEEVENT_MOUSE_WHEEL)
call TriggerAddAction(trig, function UIFrameEvents.onSliderDetectWheel)
endfunction
private function PrepAbandonEvent takes nothing returns nothing
local integer i = 1
loop
exitwhen i > CustomRacePSelection.choicedPlayerSize
call TriggerRegisterPlayerEvent(UIFrameEvents.abandonTrig, CustomRacePSelection.choicedPlayers[i], /*
*/ EVENT_PLAYER_LEAVE)
set i = i + 1
endloop
set i = 1
loop
exitwhen i > CustomRacePSelection.unchoicedPlayerSize
call TriggerRegisterPlayerEvent(UIFrameEvents.abandonTrig, CustomRacePSelection.unchoicedPlayers[i], /*
*/ EVENT_PLAYER_LEAVE)
set i = i + 1
endloop
call TriggerAddAction(UIFrameEvents.abandonTrig, function UIFrameEvents.onPlayerAbandon)
endfunction
private function PrepEvents takes nothing returns nothing
call PrepHoverEvents()
call PrepClickEvents()
call PrepScrollEvents()
call PrepAbandonEvent()
endfunction
// ========================================================================== //
// ========================================================================== //
private function FillWithPlayerData takes player whichPlayer returns nothing
local CustomRacePSelection obj = CRPSelection[whichPlayer]
local integer maxSteps = CustomRace.getRaceFactionCount(GetPlayerRace(whichPlayer))
set maxSteps = IMaxBJ(maxSteps - CustomRaceUI_GetMaxDisplayChoices(), 0)
if (maxSteps != 0) and (GetLocalPlayer() == whichPlayer) then
call CustomRaceInterface.setSliderMaxValue(maxSteps)
endif
call DrawUIFromPlayerData(whichPlayer, obj)
endfunction
private function PopulateUI takes nothing returns nothing
local integer i = 1
loop
exitwhen i > CustomRacePSelection.choicedPlayerSize
call FillWithPlayerData(CustomRacePSelection.choicedPlayers[i])
set i = i + 1
endloop
// All players don't have a faction choice, proceed with game
// as normal
if CustomRacePSelection.choicedPlayerSize < 1 then
call UIFrameEvents.onFinalizeEnd()
else
call CustomRaceObserverUI_RenderFrame()
endif
endfunction
// ========================================================================== //
public function Initialization takes nothing returns nothing
call CustomRacePSelection.init()
call CustomRaceMatch_MeleeInitialization()
call PrepUI()
call PopulateUI()
call PrepEvents()
endfunction
endlibrary
library CustomRaceDefault requires /*
----------------------
*/ CustomRaceCore, /*
----------------------
--------------------------
*/ CustomRaceMatch, /*
--------------------------
----------------------
*/ Init, /*
----------------------
*/
globals
private CustomRace array defaultRace
endglobals
//! textmacro CRDefault_DEF_SETUP takes NAME, SUBNAME
private function On$NAME$Setup takes nothing returns nothing
call MeleeStartingUnits$NAME$(CustomRaceMatch_OnStartGetPlayer(), /*
*/ CustomRaceMatch_OnStartGetLoc(), true, true, true)
endfunction
private function On$NAME$SetupAI takes nothing returns nothing
call PickMeleeAI(CustomRaceMatch_OnStartGetPlayer(), "$SUBNAME$.ai", null, null)
endfunction
//! endtextmacro
//! runtextmacro CRDefault_DEF_SETUP("Human", "human")
//! runtextmacro CRDefault_DEF_SETUP("Orc", "orc")
//! runtextmacro CRDefault_DEF_SETUP("NightElf", "elf")
private function OnUndeadSetup takes nothing returns nothing
call MeleeStartingUnitsUndead(CustomRaceMatch_OnStartGetPlayer(), /*
*/ CustomRaceMatch_OnStartGetLoc(), true, true, true)
endfunction
private function OnUndeadSetupAI takes nothing returns nothing
call PickMeleeAI(CustomRaceMatch_OnStartGetPlayer(), "undead.ai", null, null)
call RecycleGuardPosition(bj_ghoul[GetPlayerId(CustomRaceMatch_OnStartGetPlayer())])
endfunction
private struct S extends array
private static method init takes nothing returns nothing
set defaultRace[1] = CustomRace.create(RACE_HUMAN, "Alliance")
call defaultRace[1].defDescription("A coalition of Humans, Dwarves and Elves bound " + /*
*/ "together through the forging of bonds as old as " + /*
*/ "their kingdoms themselves.")
call defaultRace[1].defRacePic("war3mapImported\\humanseal.blp")
call defaultRace[1].addUnit('hpea')
call defaultRace[1].addUnit('hfoo')
call defaultRace[1].addUnit('hrif')
call defaultRace[1].addUnit('hkni')
call defaultRace[1].addUnit('hmtm')
call defaultRace[1].addUnit('hgyr')
call defaultRace[1].addUnit('hmpr')
call defaultRace[1].addUnit('hsor')
call defaultRace[1].addUnit('hspt')
call defaultRace[1].addUnit('hgry')
call defaultRace[1].addUnit('hmtt')
call defaultRace[1].addHall('htow')
call defaultRace[1].addHall('hkee')
call defaultRace[1].addHall('hcas')
call defaultRace[1].addStructure('htow')
call defaultRace[1].addStructure('hkee')
call defaultRace[1].addStructure('hcas')
call defaultRace[1].addStructure('hhou')
call defaultRace[1].addStructure('hbar')
call defaultRace[1].addStructure('halt')
call defaultRace[1].addStructure('hlum')
call defaultRace[1].addStructure('hbla')
call defaultRace[1].addStructure('hwtw')
call defaultRace[1].addStructure('hvlt')
call defaultRace[1].addStructure('harm')
call defaultRace[1].addStructure('hars')
call defaultRace[1].addStructure('hgra')
call defaultRace[1].addHero('Hpal')
call defaultRace[1].addHero('Hamg')
call defaultRace[1].addHero('Hmkg')
call defaultRace[1].addHero('Hblm')
call defaultRace[1].defSetup(function OnHumanSetup)
call defaultRace[1].defAISetup(function OnHumanSetupAI)
call defaultRace[1].defPlaylist("Sound\\Music\\mp3Music\\HumanX1.mp3;Sound\\Music\\mp3Music\\Human3.mp3;Sound\\Music\\mp3Music\\Human2.mp3;Sound\\Music\\mp3Music\\Human1.mp3")
set defaultRace[2] = CustomRace.create(RACE_ORC, "Horde")
call defaultRace[2].defDescription("Once beings with shamanic roots, these creatures " + /*
*/ "became as bloodthirsty as the demons. Seeking " + /*
*/ "refuge from their destroyed homeworld, they seek " + /*
*/ "even now to establish a new home in Azeroth, even " + /*
*/ "at the price of eternal bloodshed.")
call defaultRace[2].defRacePic("war3mapImported\\orcseal.blp")
call defaultRace[2].addUnit('opeo')
call defaultRace[2].addUnit('ogru')
call defaultRace[2].addUnit('ohun')
call defaultRace[2].addUnit('ocat')
call defaultRace[2].addUnit('orai')
call defaultRace[2].addUnit('okod')
call defaultRace[2].addUnit('oshm')
call defaultRace[2].addUnit('odoc')
call defaultRace[2].addUnit('otau')
call defaultRace[2].addUnit('ospw')
call defaultRace[2].addUnit('owvy')
call defaultRace[2].addUnit('otbr')
call defaultRace[2].addHall('ogre')
call defaultRace[2].addHall('ostr')
call defaultRace[2].addHall('ofrt')
call defaultRace[2].addStructure('ogre')
call defaultRace[2].addStructure('ostr')
call defaultRace[2].addStructure('ofrt')
call defaultRace[2].addStructure('otrb')
call defaultRace[2].addStructure('obar')
call defaultRace[2].addStructure('oalt')
call defaultRace[2].addStructure('ofor')
call defaultRace[2].addStructure('owtw')
call defaultRace[2].addStructure('ovln')
call defaultRace[2].addStructure('obea')
call defaultRace[2].addStructure('osld')
call defaultRace[2].addStructure('obea')
call defaultRace[2].addHero('Obla')
call defaultRace[2].addHero('Ofar')
call defaultRace[2].addHero('Otch')
call defaultRace[2].addHero('Oshd')
call defaultRace[2].defSetup(function OnOrcSetup)
call defaultRace[2].defAISetup(function OnOrcSetupAI)
call defaultRace[2].defPlaylist("Sound\\Music\\mp3Music\\OrcX1.mp3;Sound\\Music\\mp3Music\\Orc3.mp3;Sound\\Music\\mp3Music\\Orc2.mp3;Sound\\Music\\mp3Music\\Orc1.mp3")
set defaultRace[3] = CustomRace.create(RACE_UNDEAD, "Scourge")
call defaultRace[3].defDescription("Once living creatures robbed of their sweet release " + /*
*/ "from life, these undead beings are relentless and " + /*
*/ "ferocious creatures, tempered by their bond to the " + /*
*/ "master, the Lich King.")
call defaultRace[3].defRacePic("war3mapImported\\undeadseal.blp")
call defaultRace[3].addUnit('uaco')
call defaultRace[3].addUnit('ugho')
call defaultRace[3].addUnit('ucry')
call defaultRace[3].addUnit('ugar')
call defaultRace[3].addUnit('umtw')
call defaultRace[3].addUnit('uobs')
call defaultRace[3].addUnit('unec')
call defaultRace[3].addUnit('uban')
call defaultRace[3].addUnit('ushd')
call defaultRace[3].addUnit('uabo')
call defaultRace[3].addUnit('ufro')
call defaultRace[3].addUnit('ubsp')
call defaultRace[3].addHall('unpl')
call defaultRace[3].addHall('unp1')
call defaultRace[3].addHall('unp2')
call defaultRace[3].addStructure('unpl')
call defaultRace[3].addStructure('unp1')
call defaultRace[3].addStructure('unp2')
call defaultRace[3].addStructure('uzig')
call defaultRace[3].addStructure('usep')
call defaultRace[3].addStructure('uaod')
call defaultRace[3].addStructure('ugrv')
call defaultRace[3].addStructure('utom')
call defaultRace[3].addStructure('ugol')
call defaultRace[3].addStructure('uslh')
call defaultRace[3].addStructure('utod')
call defaultRace[3].addStructure('usap')
call defaultRace[3].addStructure('ubon')
call defaultRace[3].addHero('Udea')
call defaultRace[3].addHero('Udre')
call defaultRace[3].addHero('Ulic')
call defaultRace[3].addHero('Ucrl')
call defaultRace[3].defSetup(function OnUndeadSetup)
call defaultRace[3].defAISetup(function OnUndeadSetupAI)
call defaultRace[3].defPlaylist("Sound\\Music\\mp3Music\\UndeadX1.mp3;Sound\\Music\\mp3Music\\Undead3.mp3;Sound\\Music\\mp3Music\\Undead2.mp3;Sound\\Music\\mp3Music\\Undead1.mp3")
set defaultRace[4] = CustomRace.create(RACE_NIGHTELF, "Sentinels")
call defaultRace[4].defDescription("Once prominent arcane magic practitioners, their " + /*
*/ "use of magic to reckless abandon had nearly torn " + /*
*/ "their world asunder, as the Burning Legion lay " + /*
*/ "waste to their world. After defeating the Burning " + /*
*/ "Legion, they vowed never to use arcane magic and " + /*
*/ "embraced druidism under the tutelage of Cenarius. " + /*
*/ "For centuries past, they had become a force to " + /*
*/ "be reckoned with.")
call defaultRace[4].defRacePic("war3mapImported\\nightelfseal.blp")
call defaultRace[4].addUnit('ewsp')
call defaultRace[4].addUnit('earc')
call defaultRace[4].addUnit('esen')
call defaultRace[4].addUnit('ebal')
call defaultRace[4].addUnit('edry')
call defaultRace[4].addUnit('emtg')
call defaultRace[4].addUnit('edoc')
call defaultRace[4].addUnit('edot')
call defaultRace[4].addUnit('efdr')
call defaultRace[4].addUnit('ehip')
call defaultRace[4].addUnit('echm')
call defaultRace[4].addUnit('edcm')
call defaultRace[4].addHall('etol')
call defaultRace[4].addHall('etoa')
call defaultRace[4].addHall('etoe')
call defaultRace[4].addStructure('etol')
call defaultRace[4].addStructure('etoa')
call defaultRace[4].addStructure('etoe')
call defaultRace[4].addStructure('emow')
call defaultRace[4].addStructure('eaom')
call defaultRace[4].addStructure('eate')
call defaultRace[4].addStructure('edob')
call defaultRace[4].addStructure('etrp')
call defaultRace[4].addStructure('eden')
call defaultRace[4].addStructure('eaoe')
call defaultRace[4].addStructure('eaow')
call defaultRace[4].addStructure('edos')
call defaultRace[4].addHero('Ekee')
call defaultRace[4].addHero('Emoo')
call defaultRace[4].addHero('Edem')
call defaultRace[4].addHero('Ewar')
call defaultRace[4].defSetup(function OnNightElfSetup)
call defaultRace[4].defAISetup(function OnNightElfSetupAI)
call defaultRace[4].defPlaylist("Sound\\Music\\mp3Music\\NightElfX1.mp3;Sound\\Music\\mp3Music\\NightElf3.mp3;Sound\\Music\\mp3Music\\NightElf2.mp3;Sound\\Music\\mp3Music\\NightElf1.mp3")
call CustomRace.addGlobalHero('Npbm')
call CustomRace.addGlobalHero('Nbrn')
call CustomRace.addGlobalHero('Nngs')
call CustomRace.addGlobalHero('Nplh')
call CustomRace.addGlobalHero('Nbst')
call CustomRace.addGlobalHero('Nalc')
call CustomRace.addGlobalHero('Ntin')
call CustomRace.addGlobalHero('Nfir')
endmethod
implement Init
endstruct
endlibrary
library CustomRaceTemplate requires /*
----------------------
*/ CustomRaceCore, /*
----------------------
------------------------------
*/ CustomRacePSelection, /*
------------------------------
--------------------------
*/ CustomRaceMatch, /*
--------------------------
*/
module CustomRaceTemplate
// private static race FACTION_RACE
// private static string FACTION_NAME
// private static string FACTION_DESCRIPTION
// private static string FACTION_DISPLAY
// private static string FACTION_PLAYLIST
// private static integer FACTION_HALL
// private static integer FACTION_WORKER
private static CustomRace faction = 0
static if not thistype.onSetup.exists then
private static method onSetup takes player whichPlayer, location startLoc, boolean doHeroes, boolean doCamera, boolean doPreload returns nothing
local boolean useRandomHero = IsMapFlagSet(MAP_RANDOM_HERO)
local real unitSpacing = 64.00
local unit nearestMine
local unit randomHero
local location mineLoc
local location nearMineLoc
local location heroLoc
local real peonX
local real peonY
local unit townHall = null
static if thistype.preloadPld.exists then
if (doPreload) then
call thistype.preloadPld()
endif
endif
set nearestMine = MeleeFindNearestMine(startLoc, bj_MELEE_MINE_SEARCH_RADIUS)
static if thistype.onSetupCreateUnits.exists then
call thistype.onSetupCreateUnits(whichPlayer, nearestMine)
else
if (nearestMine != null) then
// Spawn Town Hall at the start location.
set townHall = CreateUnitAtLoc(whichPlayer, FACTION_HALL, startLoc, bj_UNIT_FACING)
// Spawn Peasants near the mine.
set mineLoc = GetUnitLoc(nearestMine)
set nearMineLoc = MeleeGetProjectedLoc(mineLoc, startLoc, 320, 0)
set peonX = GetLocationX(nearMineLoc)
set peonY = GetLocationY(nearMineLoc)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX + 0.00 * unitSpacing, peonY + 1.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX + 1.00 * unitSpacing, peonY + 0.15 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX - 1.00 * unitSpacing, peonY + 0.15 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX + 0.60 * unitSpacing, peonY - 1.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX - 0.60 * unitSpacing, peonY - 1.00 * unitSpacing, bj_UNIT_FACING)
call RemoveLocation(nearMineLoc)
// Set random hero spawn point to be off to the side of the start location.
set heroLoc = MeleeGetProjectedLoc(mineLoc, startLoc, 384, 45)
call RemoveLocation(mineLoc)
else
// Spawn Town Hall at the start location.
set townHall = CreateUnitAtLoc(whichPlayer, FACTION_HALL, startLoc, bj_UNIT_FACING)
// Spawn Peasants directly south of the town hall.
set peonX = GetLocationX(startLoc)
set peonY = GetLocationY(startLoc) - 224.00
call CreateUnit(whichPlayer, FACTION_WORKER, peonX + 2.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX + 1.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX + 0.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX - 1.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, FACTION_WORKER, peonX - 2.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
// Set random hero spawn point to be just south of the start location.
// (Sorry, got lazy here.)
set heroLoc = Location(peonX, peonY - 2.00 * unitSpacing)
endif
endif
if (doHeroes) then
// If the "Random Hero" option is set, start the player with a random hero.
// Otherwise, give them a "free hero" token.
if useRandomHero then
set randomHero = CreateUnit(whichPlayer, faction.getRandomHero(), GetLocationX(heroLoc), GetLocationY(heroLoc), bj_UNIT_FACING)
if bj_meleeGrantHeroItems then
call MeleeGrantItemsToHero(randomHero)
endif
set randomHero = null
else
call SetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_HERO_TOKENS, bj_MELEE_STARTING_HERO_TOKENS)
endif
endif
call RemoveLocation(heroLoc)
if (doCamera) then
// Center the camera on the initial Peasants.
call SetCameraPositionForPlayer(whichPlayer, peonX, peonY)
call SetCameraQuickPositionForPlayer(whichPlayer, peonX, peonY)
endif
set nearestMine = null
set randomHero = null
set mineLoc = null
set nearMineLoc = null
set heroLoc = null
endmethod
endif
private static method onSetupPrep takes nothing returns nothing
call thistype.onSetup(CustomRaceMatch_OnStartGetPlayer(), /*
*/ CustomRaceMatch_OnStartGetLoc(), true, true, true)
endmethod
private static method onSetupPrepAI takes nothing returns nothing
static if thistype.onSetupAI.exists then
call thistype.onSetupAI(CustomRaceMatch_OnStartGetPlayer())
endif
endmethod
private static method onInit takes nothing returns nothing
set faction = CustomRace.create(FACTION_RACE, FACTION_NAME)
if faction == 0 then
return
endif
call faction.defDescription(FACTION_DESCRIPTION)
call faction.defRacePic(FACTION_DISPLAY)
call faction.defPlaylist(FACTION_PLAYLIST)
static if thistype.initTechtree.exists then
call thistype.initTechtree(faction)
endif
call faction.defSetup(function thistype.onSetupPrep)
call faction.defAISetup(function thistype.onSetupPrepAI)
endmethod
endmodule
struct CustomRaceTemplateGUI
readonly CustomRace faction
trigger setupTrig
trigger setupTrigAI
trigger preloadTrig
integer factionHall
integer factionWorker
private static thistype array factionMap
static if not thistype.onSetup.exists then
private static method onSetup takes player whichPlayer, location startLoc, boolean doHeroes, boolean doCamera, boolean doPreload returns nothing
local boolean useRandomHero = IsMapFlagSet(MAP_RANDOM_HERO)
local real unitSpacing = 64.00
local unit nearestMine
local unit randomHero
local location nearMineLoc
local location heroLoc
local real peonX
local real peonY
local real mineAngle
local unit townHall = null
local CustomRacePSelection obj = CRPSelection[whichPlayer]
local CustomRace faction = CustomRace.getRaceFaction(GetPlayerRace(whichPlayer), obj.faction)
local thistype this = factionMap[faction]
if (doPreload) then
set udg_CustomRace_Player = whichPlayer
call ConditionalTriggerExecute(this.preloadTrig)
endif
set nearestMine = MeleeFindNearestMine(startLoc, bj_MELEE_MINE_SEARCH_RADIUS)
if setupTrig != null then
// Introduce GUI Constants here
set udg_CustomRace_Player = whichPlayer
set udg_CustomRace_StartLocation = startLoc
set udg_CustomRace_PlayerMine = nearestMine
call ConditionalTriggerExecute(this.setupTrig)
set udg_CustomRace_StartLocation = null
else
if (nearestMine != null) then
// Spawn Town Hall at the start location.
set townHall = CreateUnitAtLoc(whichPlayer, this.factionHall, startLoc, bj_UNIT_FACING)
// Spawn Peasants near the mine.
set mineAngle = Atan2(GetUnitY(nearestMine) - GetLocationY(startLoc), /*
*/ GetUnitX(nearestMine) - GetLocationX(startLoc))
set peonX = GetLocationX(startLoc) + 320*Cos(mineAngle)
set peonY = GetLocationY(startLoc) + 320*Sin(mineAngle)
call CreateUnit(whichPlayer, this.factionWorker, peonX + 0.00 * unitSpacing, peonY + 1.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, this.factionWorker, peonX + 1.00 * unitSpacing, peonY + 0.15 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, this.factionWorker, peonX - 1.00 * unitSpacing, peonY + 0.15 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, this.factionWorker, peonX + 0.60 * unitSpacing, peonY - 1.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, this.factionWorker, peonX - 0.60 * unitSpacing, peonY - 1.00 * unitSpacing, bj_UNIT_FACING)
// Set random hero spawn point to be off to the side of the start location.
set mineAngle = mineAngle + bj_PI / 4.0
set heroLoc = Location(GetLocationX(startLoc) + 384*Cos(mineAngle), /*
*/ GetLocationY(startLoc) + 384*Sin(mineAngle))
else
// Spawn Town Hall at the start location.
set townHall = CreateUnitAtLoc(whichPlayer, this.factionHall, startLoc, bj_UNIT_FACING)
// Spawn Peasants directly south of the town hall.
set peonX = GetLocationX(startLoc)
set peonY = GetLocationY(startLoc) - 224.00
call CreateUnit(whichPlayer, this.factionWorker, peonX + 2.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, this.factionWorker, peonX + 1.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, this.factionWorker, peonX + 0.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, this.factionWorker, peonX - 1.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
call CreateUnit(whichPlayer, this.factionWorker, peonX - 2.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
// Set random hero spawn point to be just south of the start location.
set heroLoc = Location(peonX, peonY - 2.00 * unitSpacing)
endif
endif
if (doHeroes) then
// If the "Random Hero" option is set, start the player with a random hero.
// Otherwise, give them a "free hero" token.
if useRandomHero then
set randomHero = CreateUnit(whichPlayer, faction.getRandomHero(), GetLocationX(heroLoc), GetLocationY(heroLoc), bj_UNIT_FACING)
if bj_meleeGrantHeroItems then
call MeleeGrantItemsToHero(randomHero)
endif
set randomHero = null
else
call SetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_HERO_TOKENS, bj_MELEE_STARTING_HERO_TOKENS)
endif
endif
call RemoveLocation(heroLoc)
if (doCamera) then
// Center the camera on the initial Peasants.
call SetCameraPositionForPlayer(whichPlayer, peonX, peonY)
call SetCameraQuickPositionForPlayer(whichPlayer, peonX, peonY)
endif
set nearestMine = null
set randomHero = null
set nearMineLoc = null
set heroLoc = null
endmethod
endif
private static method onSetupPrep takes nothing returns nothing
call thistype.onSetup(CustomRaceMatch_OnStartGetPlayer(), /*
*/ CustomRaceMatch_OnStartGetLoc(), true, true, true)
endmethod
private static method onSetupPrepAI takes nothing returns nothing
local player whichPlayer = CustomRaceMatch_OnStartGetPlayer()
local CustomRacePSelection obj = CRPSelection[whichPlayer]
local CustomRace faction = CustomRace.getRaceFaction(GetPlayerRace(whichPlayer), obj.faction)
local thistype this = factionMap[faction]
set udg_CustomRace_Player = whichPlayer
call ConditionalTriggerExecute(this.setupTrigAI)
endmethod
static method create takes race whichRace, string whichName returns thistype
local thistype this = thistype.allocate()
set this.faction = CustomRace.create(whichRace, whichName)
if this.faction == 0 then
call this.deallocate()
return 0
endif
set factionMap[this.faction] = this
call this.faction.defSetup(function thistype.onSetupPrep)
call this.faction.defAISetup(function thistype.onSetupPrepAI)
return this
endmethod
endstruct
public function Create takes nothing returns nothing
local CustomRaceTemplateGUI template = CustomRaceTemplateGUI.create(udg_CustomRace_DefRace, udg_CustomRace_DefName)
local integer i = 1
call template.faction.defDescription(udg_CustomRace_Description)
call template.faction.defRacePic(udg_CustomRace_Display)
call template.faction.defPlaylist(udg_CustomRace_Playlist)
set udg_CustomRace_DefRace = null
set udg_CustomRace_DefName = ""
set udg_CustomRace_Description = ""
set udg_CustomRace_Display = ""
set udg_CustomRace_Playlist = ""
set template.setupTrig = udg_CustomRace_SetupTrig
set template.setupTrigAI = udg_CustomRace_AISetupTrig
set template.preloadTrig = udg_CustomRace_PreloadPLDTrig
set udg_CustomRace_SetupTrig = null
set udg_CustomRace_AISetupTrig = null
set udg_CustomRace_PreloadPLDTrig = null
set template.factionHall = udg_CustomRace_FactionHall
set template.factionWorker = udg_CustomRace_FactionWorker
set udg_CustomRace_FactionHall = 0
set udg_CustomRace_FactionWorker = 0
// Add all units
loop
exitwhen udg_CustomRace_UnitID[i] == 0
call template.faction.addUnit(udg_CustomRace_UnitID[i])
set udg_CustomRace_UnitID[i] = 0
set i = i + 1
endloop
// Add all Heroes
set i = 1
loop
exitwhen udg_CustomRace_HeroID[i] == 0
call template.faction.addHero(udg_CustomRace_HeroID[i])
set udg_CustomRace_HeroID[i] = 0
set i = i + 1
endloop
// Add all Halls
set i = 1
loop
exitwhen udg_CustomRace_HallID[i] == 0
call template.faction.addHall(udg_CustomRace_HallID[i])
set udg_CustomRace_HallID[i] = 0
set i = i + 1
endloop
// Add all Structures
set i = 1
loop
exitwhen udg_CustomRace_BuildingID[i] == 0
call template.faction.addStructure(udg_CustomRace_BuildingID[i])
set udg_CustomRace_BuildingID[i] = 0
set i = i + 1
endloop
endfunction
endlibrary
scope UnitAuxEventTest initializer Init
private function OnUnitTransform takes nothing returns nothing
call BJDebugMsg("A unit has transformed")
call BJDebugMsg("The unit's previous unit type: " + UnitAuxEvents_FourCC(UnitAuxHandler.prevTransformID))
call BJDebugMsg("The unit's current unit type: " + UnitAuxEvents_FourCC(UnitAuxHandler.curTransformID))
endfunction
private function OnUnitDeath takes nothing returns nothing
call BJDebugMsg("A unit has dead")
call BJDebugMsg("Current dead unit: " + GetUnitName(UnitAuxHandler.unit))
endfunction
private function OnUnitResurrect takes nothing returns nothing
call BJDebugMsg("A unit has alive")
call BJDebugMsg("Current alive unit: " + GetUnitName(UnitAuxHandler.unit))
endfunction
private function OnUnitLoad takes nothing returns nothing
call BJDebugMsg("A unit has loaded")
call BJDebugMsg("Current loaded unit: " + GetUnitName(UnitAuxHandler.unit))
call BJDebugMsg("Current loaded transport: " + GetUnitName(UnitAuxHandler.curTransport))
endfunction
private function OnUnitUnload takes nothing returns nothing
call BJDebugMsg("A unit has unloader")
call BJDebugMsg("Current unloaded unit: " + GetUnitName(UnitAuxHandler.unit))
call BJDebugMsg("Current unloaded transport: " + GetUnitName(UnitAuxHandler.curTransport))
endfunction
private function Init takes nothing returns nothing
call UnitAuxHandler.ON_TRANSFORM.register(function OnUnitTransform)
call UnitAuxHandler.ON_DEATH.register(function OnUnitDeath)
call UnitAuxHandler.ON_RESURRECT.register(function OnUnitResurrect)
call UnitAuxHandler.ON_LOAD.register(function OnUnitLoad)
call UnitAuxHandler.ON_UNLOAD.register(function OnUnitUnload)
endfunction
endscope
scope SentinelsOfEvolution
// Sample template
private struct S extends array
private static race FACTION_RACE = RACE_NIGHTELF
private static string FACTION_NAME = "Sentinels of Evolution"
private static string FACTION_DISPLAY = "war3mapImported\\nightelfseal.blp"
private static string FACTION_PLAYLIST = "Sound\\Music\\mp3Music\\NagaTheme.mp3;Sound\\Music\\mp3Music\\Comradeship.mp3;Sound\\Music\\mp3Music\\NightElfX1.mp3"
private static integer FACTION_HALL = 'e100'
private static integer FACTION_WORKER = 'e003'
private static string FACTION_DESCRIPTION = "A Multiversal faction of overseers, watching over half of all fictional worlds. " + /*
*/ "\nLed by Mal'furion from an alternate timeline in the 10,000 years hereafter, this " + /*
*/ "faction seeks to preserve the flow of history within each fictional world."
private static method initTechtree takes CustomRace faction returns nothing
call faction.addUnit('e003')
call faction.addUnit('e000')
call faction.addUnit('e001')
call faction.addUnit('e002')
call faction.addUnit('e004')
call faction.addUnit('e005')
call faction.addUnit('e006')
call faction.addHall('e100')
call faction.addHall('e106')
call faction.addHall('e107')
call faction.addHero('E007')
call faction.addStructure('e100')
call faction.addStructure('e101')
call faction.addStructure('e102')
call faction.addStructure('e103')
call faction.addStructure('e104')
call faction.addStructure('e106')
call faction.addStructure('e107')
endmethod
implement CustomRaceTemplate
endstruct
endscope
library GenericMorph requires /*
------------------------------
*/ Table, UnitDex, Init /*
------------------------------
------------------------------
*/ Alloc, EventListener /*
------------------------------
------------------------------
*/ SpellHandler, TimerUtils /*
------------------------------
Since this is specialized for the techtree contest map, it is
separated from the rest of the snippets/systems. If there is
a need to integrate this bundle into other maps, then the
file will be moved to systems.
*/
struct GenericMorphResponse extends array
implement Alloc
readonly EventResponder enterResponse
readonly EventResponder leaveResponse
readonly EventResponder skillResponse
readonly EventResponder skillStartResponse
readonly EventResponder transformResponse
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.enterResponse = EventResponder.create(null)
set this.leaveResponse = EventResponder.create(null)
set this.skillResponse = EventResponder.create(null)
set this.skillStartResponse = EventResponder.create(null)
set this.transformResponse = EventResponder.create(null)
return this
endmethod
endstruct
struct GenericMorph extends array
private static IntegerList responseList = 0
private static GenericMorphResponse array responseMap
private static constant integer EVENT_TYPE_ENTER = 1
private static constant integer EVENT_TYPE_LEAVE = 2
private static constant integer EVENT_TYPE_SKILL = 3
private static constant integer EVENT_TYPE_MORPH = 4
private static constant integer EVENT_TYPE_SKILL_START = 5
static method addResponse takes integer classID, GenericMorphResponse response returns nothing
if (responseMap[classID] != 0) then
return
endif
set responseMap[classID] = response
call responseList.push(response)
endmethod
private static method throwResponse takes integer eventType returns nothing
local IntegerListItem iter = responseList.first
local GenericMorphResponse obj = 0
loop
exitwhen iter == 0
set obj = GenericMorphResponse(iter.data)
set iter = iter.next
if (eventType == EVENT_TYPE_ENTER) then
call obj.enterResponse.conditionalRun()
elseif (eventType == EVENT_TYPE_LEAVE) then
call obj.leaveResponse.conditionalRun()
elseif (eventType == EVENT_TYPE_SKILL) then
call obj.skillResponse.conditionalRun()
elseif (eventType == EVENT_TYPE_SKILL_START) then
call obj.skillStartResponse.conditionalRun()
elseif (eventType == EVENT_TYPE_MORPH) then
call obj.transformResponse.conditionalRun()
endif
endloop
endmethod
private static method onUnitEnter takes nothing returns nothing
call thistype.throwResponse(EVENT_TYPE_ENTER)
endmethod
private static method onUnitLeave takes nothing returns nothing
call thistype.throwResponse(EVENT_TYPE_LEAVE)
endmethod
private static method onUnitTransform takes nothing returns nothing
call thistype.throwResponse(EVENT_TYPE_MORPH)
endmethod
private static method onUnitAttemptMorph takes nothing returns nothing
call thistype.throwResponse(EVENT_TYPE_SKILL)
endmethod
private static method onGroupAttemptMorph takes nothing returns nothing
call thistype.throwResponse(EVENT_TYPE_SKILL_START)
endmethod
private static method init takes nothing returns nothing
set responseList = IntegerList.create()
call OnUnitIndex(function thistype.onUnitEnter)
call OnUnitDeindex(function thistype.onUnitLeave)
call UnitAuxHandler.ON_TRANSFORM.register(function thistype.onUnitTransform)
call SpellHandler.ON_FINISH.register(function thistype.onUnitAttemptMorph)
call SpellHandler.ON_CHANNEL.register(function thistype.onGroupAttemptMorph)
endmethod
implement Init
endstruct
struct GenericMorphData extends array
implement Alloc
private unit unit
private integer order
private integer abil
private integer level
private static method enqueue takes nothing returns nothing
local thistype this = ReleaseTimer(GetExpiredTimer())
call BlzUnitDisableAbility(this.unit, this.abil, false, false)
call SetUnitAbilityLevel(this.unit, this.abil, this.level)
call IssueImmediateOrderById(this.unit, this.order)
call BlzUnitDisableAbility(this.unit, this.abil, true, true)
endmethod
static method queue takes unit source, integer orderID, integer abilID, integer level returns nothing
local thistype this = thistype.allocate()
set this.unit = source
set this.order = orderID
set this.abil = abilID
set this.level = level
call TimerStart(NewTimerEx(this), 0.0, false, function thistype.enqueue)
endmethod
endstruct
// Note: This module assumes that the number of
// registered unit ids is the same with the number
// of registered morph skill id's. Base transformation
// skill is Bear Form.
module GenericMorphHandler
private static GenericMorphResponse resp = 0
private static integer index = 0
private static integer rows = 0
private static Table unitIDMap = 0
private static Table morphSkillMap = 0
private static integer array unitType
private static integer array trueUnitTypeID
static integer ABIL_ID = 0
static integer array UNIT_ID
static integer array MORPH_SKILL_ID
static if not thistype.TRANSFORM_ORDER.exists then
static method TRANSFORM_ORDER takes nothing returns integer
return 852138
endmethod
endif
static if not thistype.UNTRANSFORM_ORDER.exists then
static method UNTRANSFORM_ORDER takes nothing returns integer
return 852139
endmethod
endif
static if not thistype.GROUPED_MORPH.exists then
static method GROUPED_MORPH takes nothing returns boolean
return true
endmethod
endif
private static method mapUnitIDs takes nothing returns nothing
loop
exitwhen (UNIT_ID[index + 1] == 0)
set index = index + 1
set unitIDMap.integer[UNIT_ID[index]] = index
set morphSkillMap.integer[MORPH_SKILL_ID[index]] = index
endloop
set rows = index - 1
endmethod
private static method onUnitEnter takes nothing returns nothing
local unit u = GetIndexedUnit()
local integer curTypeID = GetUnitTypeId(u)
local integer id = GetUnitId(u)
local integer iter = 1
if (not unitIDMap.integer.has(curTypeID)) then
set u = null
return
endif
set unitType[id] = unitIDMap.integer[curTypeID]
set trueUnitTypeID[id] = unitType[id]
loop
exitwhen iter > index
call UnitMakeAbilityPermanent(u, true, MORPH_SKILL_ID[iter])
set iter = iter + 1
endloop
call UnitAddAbility(u, ABIL_ID)
call UnitMakeAbilityPermanent(u, true, ABIL_ID)
call BlzUnitDisableAbility(u, ABIL_ID, true, true)
call BlzUnitDisableAbility(u, MORPH_SKILL_ID[unitType[id]], true, true)
static if thistype.onEnter.exists then
call thistype.onEnter(u, unitType[id])
endif
set u = null
endmethod
private static method onUnitLeave takes nothing returns nothing
local unit u = GetIndexedUnit()
local integer curTypeID = GetUnitTypeId(u)
local integer id = GetUnitId(u)
if (unitType[id] == 0) then
return
endif
static if thistype.onLeave.exists then
call thistype.onLeave(u, unitType[id])
endif
set unitType[id] = 0
set trueUnitTypeID[id] = 0
set u = null
endmethod
private static method onUnitTransform takes nothing returns nothing
local unit u = UnitAuxHandler.unit
local integer curTypeID = UnitAuxHandler.curTransformID
local integer id = GetUnitId(u)
local integer iter = 1
if (unitType[id] == 0) then
// If current type has an unmapped ID, ignore
if (not unitIDMap.integer.has(curTypeID)) then
set u = null
return
endif
loop
exitwhen iter > index
call UnitMakeAbilityPermanent(u, true, MORPH_SKILL_ID[iter])
set iter = iter + 1
endloop
else
call BlzUnitDisableAbility(u, MORPH_SKILL_ID[unitType[id]], false, false)
endif
set unitType[id] = unitIDMap.integer[curTypeID]
call BlzUnitDisableAbility(u, MORPH_SKILL_ID[unitType[id]], true, true)
static if thistype.onTransform.exists then
call thistype.onTransform(u, unitType[id])
endif
set u = null
endmethod
private static method onUnitAttemptMorph takes nothing returns nothing
local unit u = SpellHandler.unit
local integer id = GetUnitId(u)
local integer abilID = SpellHandler[u].curAbility
local integer abilType = 0
local integer uType = unitType[id]
local boolean wasDec = false
if (not morphSkillMap.integer.has(abilID)) then
set u = null
return
endif
set abilType = morphSkillMap.integer[abilID]
if (abilType > uType) then
set abilType = abilType - 1
set wasDec = true
endif
// Do something funky
static if thistype.onAttemptMorph.exists then
if wasDec then
set abilType = abilType + 1
endif
call thistype.onAttemptMorph(u, uType, abilType)
if wasDec then
set abilType = abilType - 1
endif
endif
// Set unit ability level
call BlzUnitDisableAbility(u, ABIL_ID, false, false)
call IssueImmediateOrderById(u, thistype.UNTRANSFORM_ORDER())
call BlzUnitDisableAbility(u, ABIL_ID, true, true)
call GenericMorphData.queue(u, thistype.TRANSFORM_ORDER(), ABIL_ID, (uType - 1)*rows + abilType)
//call SetUnitAbilityLevel(u, ABIL_ID, (uType - 1)*rows + abilType)
//call IssueImmediateOrderById(u, thistype.TRANSFORM_ORDER())
set u = null
endmethod
private static method onGroupAttemptMorph takes nothing returns nothing
local unit u = SpellHandler.unit
local unit foG
local integer id = GetUnitId(u)
local integer uTypeID = GetUnitTypeId(u)
local integer abilID = SpellHandler[u].curAbility
local group g
if (not morphSkillMap.integer.has(abilID)) or /*
*/ (not thistype.GROUPED_MORPH()) then
set u = null
return
endif
call resp.skillStartResponse.enable(false)
set g = CreateGroup()
call GroupEnumUnitsSelected(g, GetOwningPlayer(u), null)
loop
set foG = FirstOfGroup(g)
exitwhen (foG == null)
loop
exitwhen (foG == u)
if (GetUnitTypeId(foG) == uTypeID) then
call IssueTargetOrderById(foG, GetUnitCurrentOrder(u), foG)
endif
exitwhen true
endloop
call GroupRemoveUnit(g, foG)
endloop
call resp.skillStartResponse.enable(true)
call DestroyGroup(g)
set g = null
set u = null
endmethod
private static method onInit takes nothing returns nothing
set resp = GenericMorphResponse.create()
set unitIDMap = Table.create()
set morphSkillMap = Table.create()
call resp.enterResponse.change(function thistype.onUnitEnter)
call resp.leaveResponse.change(function thistype.onUnitLeave)
call resp.transformResponse.change(function thistype.onUnitTransform)
call resp.skillResponse.change(function thistype.onUnitAttemptMorph)
call resp.skillStartResponse.change(function thistype.onGroupAttemptMorph)
call thistype.initVars()
call thistype.mapUnitIDs()
call GenericMorph.addResponse(thistype.typeid, resp)
endmethod
endmodule
endlibrary
scope GreenDragonMorph
private module GreenDragonMorphConfig
static real array FLY_HEIGHT
static method initVars takes nothing returns nothing
set thistype.ABIL_ID = 'A008'
set thistype.UNIT_ID[1] = 'e000'
set thistype.UNIT_ID[2] = 'e001'
set thistype.UNIT_ID[3] = 'e002'
set thistype.MORPH_SKILL_ID[1] = 'A005'
set thistype.MORPH_SKILL_ID[2] = 'A006'
set thistype.MORPH_SKILL_ID[3] = 'A007'
set thistype.FLY_HEIGHT[1] = 80.0
set thistype.FLY_HEIGHT[2] = 0.0
set thistype.FLY_HEIGHT[3] = 240.0
endmethod
implement GenericMorphHandler
endmodule
private struct GreenDragonMorph extends array
private static real array lastMana
private static boolean array surgingStrikesHidden
private static boolean array manaStored
private static method onEnter takes unit dragon, integer curType returns nothing
if (curType == 2) then
call UnitMakeAbilityPermanent(dragon, true, 'A003')
else
set surgingStrikesHidden[GetUnitId(dragon)] = true
set manaStored[GetUnitId(dragon)] = (curType == 3)
endif
endmethod
private static method onLeave takes unit dragon, integer curType returns nothing
local integer dragID = GetUnitId(dragon)
set surgingStrikesHidden[dragID] = false
set manaStored[dragID] = false
endmethod
private static method onAttemptMorph takes unit dragon, integer prevType, integer curType returns nothing
local integer dragID = GetUnitId(dragon)
if (prevType == 3) then
set lastMana[dragID] = GetUnitState(dragon, UNIT_STATE_MANA)
endif
endmethod
private static method onTransform takes unit dragon, integer curType returns nothing
local integer dragID = GetUnitId(dragon)
if (curType == 3) then
if (not manaStored[dragID]) then
set manaStored[dragID] = true
set lastMana[dragID] = 200.0
endif
call SetUnitState(dragon, UNIT_STATE_MANA, lastMana[dragID])
call BlzSetUnitRealField(dragon, UNIT_RF_MANA_REGENERATION, 2.0)
endif
if (curType == 2) then
if UnitAddAbility(dragon, 'A003') then
call UnitMakeAbilityPermanent(dragon, true, 'A003')
else
call BlzUnitDisableAbility(dragon, 'A003', false, false)
endif
set surgingStrikesHidden[dragID] = false
else
if (GetUnitAbilityLevel(dragon, 'A003') != 0) and /*
*/ (not surgingStrikesHidden[dragID]) then
call BlzUnitDisableAbility(dragon, 'A003', true, true)
set surgingStrikesHidden[dragID] = true
endif
endif
if UnitAddAbility(dragon, 'Amrf') and UnitRemoveAbility(dragon, 'Amrf') then
endif
call SetUnitFlyHeight(dragon, FLY_HEIGHT[curType], 0.0)
endmethod
implement GreenDragonMorphConfig
endstruct
endscope
scope BearkinMorph
// TO-DO: Fix mana regeneration.
private module BearkinMorphConfig
static method initVars takes nothing returns nothing
set thistype.ABIL_ID = 'A00I'
set thistype.UNIT_ID[1] = 'e004'
set thistype.UNIT_ID[2] = 'e005'
set thistype.UNIT_ID[3] = 'e006'
set thistype.MORPH_SKILL_ID[1] = 'A00F'
set thistype.MORPH_SKILL_ID[2] = 'A00G'
set thistype.MORPH_SKILL_ID[3] = 'A00H'
endmethod
implement GenericMorphHandler
endmodule
private struct BearkinMorph extends array
private static real array lastMana
private static boolean array manaFirstTime
private static method onEnter takes unit bearkin, integer curType returns nothing
set manaFirstTime[GetUnitId(bearkin)] = (curType != 3)
endmethod
private static method onLeave takes unit bearkin, integer curType returns nothing
local integer bearID = GetUnitId(bearkin)
set lastMana[bearID] = 0.0
set manaFirstTime[bearID] = false
endmethod
private static method onAttemptMorph takes unit bear, integer prevType, integer curType returns nothing
local integer bearID = GetUnitId(bear)
if (prevType == 3) then
set lastMana[bearID] = GetUnitState(bear, UNIT_STATE_MANA)
endif
if (manaFirstTime[bearID]) and (curType == 3) then
set lastMana[bearID] = 50.0
set manaFirstTime[bearID] = false
endif
endmethod
private static method onTransform takes unit bearkin, integer curType returns nothing
local integer bearID = GetUnitId(bearkin)
if (curType == 3) then
if (GetUnitState(bearkin, UNIT_STATE_MANA) <= lastMana[bearID]) then
call SetUnitState(bearkin, UNIT_STATE_MANA, lastMana[bearID])
endif
call BlzSetUnitRealField(bearkin, UNIT_RF_MANA_REGENERATION, 0.50)
endif
if UnitAddAbility(bearkin, 'Amrf') and UnitRemoveAbility(bearkin, 'Amrf') then
endif
call SetUnitFlyHeight(bearkin, GetUnitDefaultFlyHeight(bearkin), 0.0)
endmethod
implement BearkinMorphConfig
endstruct
endscope
scope CorrosiveBile
private module CorrosiveBileConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A002'
endmethod
static method DEBUFF_ABIL_ID takes nothing returns integer
return 'A@00'
endmethod
static method DEBUFF_BUFF_ID takes nothing returns integer
return 'B@00'
endmethod
static constant method operator MAX_SOURCE_STACKS takes nothing returns integer
return 3
endmethod
static constant method operator MAX_STACKS_PER_TARGET takes nothing returns integer
return 10
endmethod
static constant method operator ARMOR_PER_STACK takes nothing returns real
return -1.0
endmethod
static constant method operator DEBUFF_DURATION takes nothing returns real
return 8.0
endmethod
static constant method operator DEBUFF_MODEL takes nothing returns string
return "Custom\\Model\\Buff\\corrosive_bile_beta.mdx"
endmethod
static constant method operator DEBUFF_MODEL_ATTACH takes nothing returns string
return "chest"
endmethod
endmodule
private struct CorrosiveBile extends array
implement CorrosiveBileConfig
private static unit dmgSource = null
private static Buff array debuff
private static effect array debuffFX
private static Table array debuffTable
private static integer array stackCount
private static CStat array armorMod
private unit source
private static method onBuffRemove takes nothing returns nothing
local integer id = GetUnitId(Buff.current.unit)
call DestroyEffect(debuffFX[id])
call debuffTable[id].destroy()
set debuffFX[id] = null
set debuffTable[id] = 0
set debuff[id] = 0
set armorMod[id] = 0
set stackCount[id] = 0
endmethod
private static method onBuffUnstack takes nothing returns nothing
local integer id = GetUnitId(Buff.current.unit)
local integer srcID = 0
// This function assumes that the Buff is created
// via the method .applyTimed() instead of .apply().
set srcID = GetUnitId(thistype(Buff.lastData).source)
set debuffTable[id].integer[srcID] = debuffTable[id].integer[srcID] - 1
if (debuffTable[id].integer[srcID] < MAX_SOURCE_STACKS) then
set stackCount[id] = stackCount[id] - 1
set armorMod[id].amount = ARMOR_PER_STACK*IMinBJ(stackCount[id], MAX_STACKS_PER_TARGET)
// Destroy the stat modifier when appropriate
if (stackCount[id] == 0) then
call armorMod[id].destroy()
set armorMod[id] = 0
endif
endif
endmethod
private static method onBuffStack takes nothing returns nothing
local integer id = GetUnitId(Buff.current.unit)
local integer srcID = GetUnitId(dmgSource)
if (debuffTable[id] == 0) then
set debuffTable[id] = Table.create()
endif
// This function assumes that the Buff is created
// via the method .applyTimed() instead of .apply().
set thistype(Buff.lastData).source = dmgSource
set debuffTable[id].integer[srcID] = debuffTable[id].integer[srcID] + 1
if (debuffTable[id].integer[srcID] <= MAX_SOURCE_STACKS) then
set stackCount[id] = stackCount[id] + 1
set armorMod[id].amount = ARMOR_PER_STACK*IMinBJ(stackCount[id], MAX_STACKS_PER_TARGET)
endif
endmethod
private static method onDamage takes nothing returns nothing
local integer id = GetUnitId(DamageHandler.target)
if ((GetUnitAbilityLevel(DamageHandler.source, ABILITY_ID) == 0) or /*
*/ (not DamageHandler.isDamageAttack())) then
return
endif
set dmgSource = DamageHandler.source
if (armorMod[id] == 0) then
set armorMod[id] = ArmorStat.apply(DamageHandler.target, 0.0, STAT_ADD)
endif
if (debuffFX[id] == null) then
set debuffFX[id] = AddSpecialEffectTarget(DEBUFF_MODEL, DamageHandler.target, DEBUFF_MODEL_ATTACH)
endif
set debuff[id] = thistype.applyBuff(DamageHandler.target, DEBUFF_DURATION)
endmethod
private static method onInit takes nothing returns nothing
call DamageHandler.MODIFIER_OUTGOING.register(function thistype.onDamage)
endmethod
implement CustomBuffHandler
endstruct
endscope
scope MaleficGun
private module MaleficGunConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A004'
endmethod
static constant method operator DEBUFF_ID takes nothing returns integer
return 'B001'
endmethod
static constant method operator BASE_DAMAGE takes nothing returns real
return 10.0
endmethod
static constant method operator CUR_MANA_DAMAGE_FACTOR takes nothing returns real
return 0.15
endmethod
static constant method operator CUR_MANA_COST_FACTOR takes nothing returns real
return 0.10
endmethod
static constant method operator GUN_ATTACK_TYPE takes nothing returns attacktype
return ATTACK_TYPE_NORMAL
endmethod
static constant method operator GUN_DAMAGE_TYPE takes nothing returns damagetype
return DAMAGE_TYPE_MAGIC
endmethod
endmodule
private struct MaleficGun extends array
implement MaleficGunConfig
private static group dragonGroup = CreateGroup()
private static EventResponder resp = 0
private static method onDragonEnter takes nothing returns nothing
local unit whichUnit = GetIndexedUnit()
if (GetUnitAbilityLevel(whichUnit, ABILITY_ID) == 0) then
set whichUnit = null
return
endif
call GroupAddUnit(dragonGroup, whichUnit)
if (BlzGroupGetSize(dragonGroup) == 1) then
call GTimer[UPDATE_TICK].requestCallback(resp)
endif
set whichUnit = null
endmethod
private static method onDragonLeave takes nothing returns nothing
local unit whichUnit = GetIndexedUnit()
if (not IsUnitInGroup(whichUnit, dragonGroup)) then
set whichUnit = null
return
endif
call GroupRemoveUnit(dragonGroup, whichUnit)
if (BlzGroupGetSize(dragonGroup) == 0) then
call GTimer[UPDATE_TICK].releaseCallback(resp)
endif
set whichUnit = null
endmethod
private static method onDragonTransform takes nothing returns nothing
local unit whichUnit = UnitAuxHandler.unit
// The unit might have lost the ability when transforming.
if (IsUnitInGroup(whichUnit, dragonGroup) and /*
*/ (GetUnitAbilityLevel(whichUnit, ABILITY_ID) == 0)) then
call GroupRemoveUnit(dragonGroup, whichUnit)
if (BlzGroupGetSize(dragonGroup) == 0) then
call GTimer[UPDATE_TICK].releaseCallback(resp)
endif
set whichUnit = null
return
endif
if (not IsUnitInGroup(whichUnit, dragonGroup)) and /*
*/ (GetUnitAbilityLevel(whichUnit, ABILITY_ID) != 0) then
// The unit might have acquired the ability
call GroupAddUnit(dragonGroup, whichUnit)
if (BlzGroupGetSize(dragonGroup) == 1) then
call GTimer[UPDATE_TICK].requestCallback(resp)
endif
endif
set whichUnit = null
endmethod
private static method updateMana takes nothing returns nothing
local unit picked = GetEnumUnit()
local real curMana = GetUnitState(picked, UNIT_STATE_MANA)
if (UnitAlive(picked)) then
call BlzSetUnitAbilityManaCost(picked, ABILITY_ID, 0, R2I(curMana*CUR_MANA_COST_FACTOR))
endif
set picked = null
endmethod
private static method onMonitorMana takes nothing returns nothing
call ForGroup(dragonGroup, function thistype.updateMana)
endmethod
private static method onDamage takes nothing returns nothing
local real mana
if (GetUnitAbilityLevel(DamageHandler.target, DEBUFF_ID) == 0) then
return
endif
call UnitRemoveAbility(DamageHandler.target, DEBUFF_ID)
set mana = GetUnitState(DamageHandler.source, UNIT_STATE_MANA)
call UnitDamageTarget(DamageHandler.source, DamageHandler.target, BASE_DAMAGE + mana*CUR_MANA_DAMAGE_FACTOR, /*
*/false, false, GUN_ATTACK_TYPE, GUN_DAMAGE_TYPE, null)
endmethod
private static method init takes nothing returns nothing
set resp = GTimer.register(UPDATE_TICK, function thistype.onMonitorMana)
call OnUnitIndex(function thistype.onDragonEnter)
call OnUnitDeindex(function thistype.onDragonLeave)
call DamageHandler.MODIFIER_OUTGOING.register(function thistype.onDamage)
call UnitAuxHandler.ON_TRANSFORM.register(function thistype.onDragonTransform)
endmethod
implement Init
endstruct
endscope
scope SurgingStrikes
// Hehe, Surging Strikes go brrr...
private module SurgingStrikesConfig
static constant method operator CARGO_HOLD_ID takes nothing returns integer
return 'SPAS'
endmethod
static constant method operator ABILITY_ID takes nothing returns integer
return 'A003'
endmethod
static constant method operator STRIKE_COUNT takes nothing returns integer
return 3
endmethod
// The base timeframe from which the delay will be considered is at the
// moment when the unit get launched from it's original position.
static constant method operator STRIKE_DAMAGE_DELAY takes nothing returns real
return 0.15
endmethod
static constant method operator STRIKE_SPEED takes nothing returns real
return 900.0
endmethod
static constant method operator STRIKE_OFFSET_DIST takes nothing returns real
return 150.0
endmethod
static constant method operator STRIKE_ATTACK_TYPE takes nothing returns attacktype
return ATTACK_TYPE_MELEE
endmethod
static constant method operator STRIKE_DAMAGE_TYPE takes nothing returns damagetype
return DAMAGE_TYPE_NORMAL
endmethod
static constant method operator STRIKE_WEAPON_TYPE takes nothing returns weapontype
return WEAPON_TYPE_METAL_LIGHT_SLICE
endmethod
static constant method operator STRIKE_ATTACK_INDEX takes nothing returns integer
return 0
endmethod
static constant method operator STRIKE_ANIM_INDEX takes nothing returns integer
return 8
endmethod
static constant method operator STRIKE_ANIM_SPEED_FACTOR takes nothing returns real
return 3.0
endmethod
static constant method operator CRIT_RATIO takes nothing returns real
return 2.0
endmethod
static constant method operator CRIT_TEXT_COLOR takes nothing returns integer
return 0xffc000ff
endmethod
static constant method operator MAX_DAMAGE_WEIGHT takes nothing returns real
return 5.0
endmethod
static constant method operator CRIT_MODEL takes nothing returns string
return "Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl"
endmethod
static method CRIT_MODEL_ATTACH takes integer index returns string
if (index == 1) then
return "left hand"
elseif (index == 2) then
return "right hand"
endif
return ""
endmethod
static method filterTarget takes unit source, unit target returns boolean
return UnitAlive(target) and /*
*/ IsUnitEnemy(target, GetOwningPlayer(source)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_STRUCTURE)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_FLYING))
endmethod
endmodule
private struct SurgingStrikesData extends array
implement SurgingStrikesConfig
endstruct
private struct SurgingStrikesAction extends array
integer strikeCount
unit strikeTarget
effect rightHandFX
effect leftHandFX
static method flagset takes nothing returns integer
return ObjectMovement.FLAG_DESTROY_ON_TARGET_DEATH + /*
*/ ObjectMovement.FLAG_DESTROY_ON_TARGET_REMOVE + /*
*/ ObjectMovement.FLAG_DESTROY_ON_OBJECT_DEATH + /*
*/ ObjectMovement.FLAG_STOP_ON_UNIT_ROOT
endmethod
static method setProjectedTarget takes ObjectMovement move, unit target returns nothing
local real cx = GetUnitX(move.unit)
local real cy = GetUnitY(move.unit)
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local real rad = Atan2(ty - cy, tx - cx)
set tx = tx + SurgingStrikesData.STRIKE_OFFSET_DIST * Cos(rad)
set ty = ty + SurgingStrikesData.STRIKE_OFFSET_DIST * Sin(rad)
call SetUnitFacing(move.unit, rad * bj_RADTODEG)
call move.setTargetAreaXY(tx, ty)
endmethod
private method presentDamage takes unit source, unit target, real baseDmg returns nothing
local texttag tag = null
local integer red = (BlzBitAnd(SurgingStrikesData.CRIT_TEXT_COLOR / 0x10000, 0xff) )
local integer green = (BlzBitAnd(SurgingStrikesData.CRIT_TEXT_COLOR / 0x100, 0xff) )
local integer blue = (BlzBitAnd(SurgingStrikesData.CRIT_TEXT_COLOR, 0xff))
local integer alpha = 0
// First bit is 1.
if (SurgingStrikesData.CRIT_TEXT_COLOR < 0) then
set alpha = 0x80 + (BlzBitAnd((SurgingStrikesData.CRIT_TEXT_COLOR - 0x80000000) / 0x1000000, 0xff))
else
set alpha = (BlzBitAnd((SurgingStrikesData.CRIT_TEXT_COLOR) / 0x1000000, 0xff))
endif
set tag = CreateTextTag()
call SetTextTagColor(tag, red, green, blue, alpha)
call SetTextTagText(tag, I2S(R2I(baseDmg)) + "!", TextTagSize2Height(11.5))
call SetTextTagPosUnit(tag, target, 75.0 / SurgingStrikesData.STRIKE_COUNT * this.strikeCount)
call SetTextTagPermanent(tag, false)
call SetTextTagLifespan(tag, 2.0)
call SetTextTagFadepoint(tag, 1.4)
call SetTextTagVelocityBJ(tag, 80.0, 90.0)
// Manage visibility
// Only the owners of the attacker and the target
// can see the text tag.
if ((GetLocalPlayer() != GetOwningPlayer(source)) and /*
*/ (GetLocalPlayer() != GetOwningPlayer(target))) then
call SetTextTagVisibility(tag, false)
endif
endmethod
private static method onDamageTarget takes nothing returns nothing
local thistype this = ReleaseTimer(GetExpiredTimer())
local ObjectMovement move = ObjectMovement(this)
local real baseDmg = BlzGetUnitBaseDamage(move.unit, SurgingStrikesData.STRIKE_ATTACK_INDEX)
local real roll = GetRandomInt(1, BlzGetUnitDiceSides(move.unit, SurgingStrikesData.STRIKE_ATTACK_INDEX)) * BlzGetUnitDiceNumber(move.unit, SurgingStrikesData.STRIKE_ATTACK_INDEX)
// Factor in the crit
set baseDmg = (baseDmg + roll) * SurgingStrikesData.CRIT_RATIO
call UnitDamageTarget(move.unit, this.strikeTarget, baseDmg, true, false, SurgingStrikesData.STRIKE_ATTACK_TYPE, /*
*/SurgingStrikesData.STRIKE_DAMAGE_TYPE, SurgingStrikesData.STRIKE_WEAPON_TYPE)
call this.presentDamage(move.unit, this.strikeTarget, baseDmg)
endmethod
private static method onDest takes nothing returns nothing
local thistype this = thistype(ObjectMovement.current)
call BlzPauseUnitEx(ObjectMovement.current.unit, false)
call QueueUnitAnimation(ObjectMovement.current.unit, "stand")
call SetUnitTimeScale(ObjectMovement.current.unit, 1.0)
call DestroyEffect(this.rightHandFX)
call DestroyEffect(this.leftHandFX)
set this.strikeCount = 0
set this.strikeTarget = null
set this.rightHandFX = null
set this.leftHandFX = null
endmethod
private static method onStop takes nothing returns nothing
local thistype this = thistype(ObjectMovement.current)
set this.strikeCount = this.strikeCount - 1
if (this.strikeCount < 1) then
call ObjectMovement.current.destroy()
return
endif
set ObjectMovement.current.veloc = SurgingStrikesData.STRIKE_SPEED
call thistype.setProjectedTarget(ObjectMovement.current, this.strikeTarget)
call ObjectMovement.current.launch()
endmethod
private static method onMove takes nothing returns nothing
local thistype this = thistype(ObjectMovement.current)
if (not UnitAlive(this.strikeTarget)) then
call ObjectMovement.current.destroy()
endif
endmethod
private static method onLaunch takes nothing returns nothing
local thistype this = thistype(ObjectMovement.current)
if (this.strikeCount == SurgingStrikesData.STRIKE_COUNT) then
call BlzPauseUnitEx(ObjectMovement.current.unit, true)
call SetUnitAnimationByIndex(ObjectMovement.current.unit, SurgingStrikesData.STRIKE_ANIM_INDEX)
call SetUnitTimeScale(ObjectMovement.current.unit, SurgingStrikesData.STRIKE_ANIM_SPEED_FACTOR)
endif
call TimerStart(NewTimerEx(this), SurgingStrikesData.STRIKE_DAMAGE_DELAY, false, function thistype.onDamageTarget)
endmethod
implement ObjectMovementTemplate
endstruct
// This will handle the event detection.
private struct SurgingStrikes extends array
private static integer array queryProcCount
private static unit array queryProcTarget
private static method onAbilReset takes nothing returns nothing
local unit dragon = GetUnitById(ReleaseTimer(GetExpiredTimer()))
call BlzEndUnitAbilityCooldown(dragon, SurgingStrikesData.ABILITY_ID)
set dragon = null
endmethod
private static method onAbilCooldown takes nothing returns nothing
local integer id = GetUnitId(SpellHandler.unit)
local ObjectMovement move = 0
local SurgingStrikesAction sMove = 0
call UnitRemoveAbility(SpellHandler.unit, SurgingStrikesData.CARGO_HOLD_ID)
if (queryProcTarget[id] == null) then
// Invalid target
call TimerStart(NewTimerEx(id), 0.00, false, function thistype.onAbilReset)
return
endif
set move = SurgingStrikesAction.applyUnitMovement(SpellHandler.unit)
set move.veloc = SurgingStrikesData.STRIKE_SPEED
set sMove = SurgingStrikesAction(move)
set sMove.strikeCount = SurgingStrikesData.STRIKE_COUNT
set sMove.strikeTarget = queryProcTarget[id]
set sMove.rightHandFX = AddSpecialEffectTarget(SurgingStrikesData.CRIT_MODEL, /*
*/ SpellHandler.unit, SurgingStrikesData.CRIT_MODEL_ATTACH(1))
set sMove.leftHandFX = AddSpecialEffectTarget(SurgingStrikesData.CRIT_MODEL, /*
*/ SpellHandler.unit, SurgingStrikesData.CRIT_MODEL_ATTACH(2))
call SurgingStrikesAction.setProjectedTarget(move, queryProcTarget[id])
call move.launch()
set queryProcCount[id] = 0
set queryProcTarget[id] = null
endmethod
private static method procPassiveCooldown takes unit source, unit target, integer id returns nothing
// Might revamp this later on. For now, go with hard-coded behavior.
set queryProcCount[id] = queryProcCount[id] + 1
set queryProcTarget[id] = target
if UnitAddAbility(source, SurgingStrikesData.CARGO_HOLD_ID) then
call UnitMakeAbilityPermanent(source, true, SurgingStrikesData.CARGO_HOLD_ID)
endif
endmethod
private static method onAttackLaunched takes nothing returns nothing
local unit attacker = GetAttacker()
local unit target = GetTriggerUnit()
local integer atkID = GetUnitId(attacker)
// Insert tech requirement check later.
// Check if attacker has the ability,
// if the target is valid, and if
// the ability is not on cooldown.
if (GetUnitAbilityLevel(attacker, SurgingStrikesData.ABILITY_ID) == 0) or /*
*/ (not SurgingStrikesData.filterTarget(attacker, target)) or /*
*/ (BlzGetUnitAbilityCooldownRemaining(attacker, SurgingStrikesData.ABILITY_ID) != 0.0) or /*
*/ (queryProcCount[atkID] != 0) then
set target = null
set attacker = null
return
endif
call thistype.procPassiveCooldown(attacker, target, atkID)
set target = null
set attacker = null
endmethod
private static method onInit takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(trig, Condition(function thistype.onAttackLaunched))
call SpellHandler.register(EVENT_ENDCAST, SurgingStrikesData.ABILITY_ID, function thistype.onAbilCooldown)
endmethod
endstruct
endscope
scope Maul
private module MaulConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A009'
endmethod
static constant method operator BONUS_DAMAGE takes nothing returns real
return 60.0
endmethod
static constant method operator MAUL_CHANCE takes nothing returns integer
return 20
endmethod
static constant method operator MAUL_MODEL takes nothing returns string
return "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
endmethod
static constant method operator STUN_DURATION_NORMAL takes nothing returns real
return 1.0
endmethod
static constant method operator STUN_DURATION_HERO takes nothing returns real
return 0.5
endmethod
static constant method operator DAMAGE_RECOVER_FACTOR takes nothing returns real
return 0.5
endmethod
static method filterTarget takes unit source, unit target returns boolean
return (not IsUnitType(target, UNIT_TYPE_STRUCTURE)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_MECHANICAL))
endmethod
endmodule
private struct Maul extends array
implement MaulConfig
private static method onDamageModify takes nothing returns nothing
local integer srcID = 0
local real dur = STUN_DURATION_NORMAL
// Filter out unwanted sources
if (GetUnitAbilityLevel(DamageHandler.source, ABILITY_ID) == 0) or /*
*/ (not DamageHandler.isDamageAttack()) or /*
*/ (not Maul.filterTarget(DamageHandler.source, DamageHandler.target)) or /*
*/ (GetRandomInt(1, 100) > MAUL_CHANCE) then
return
endif
if (IsUnitType(DamageHandler.target, UNIT_TYPE_HERO)) or /*
*/ (IsUnitType(DamageHandler.target, UNIT_TYPE_RESISTANT)) then
set dur = STUN_DURATION_HERO
endif
set DamageHandler.dmg = DamageHandler.dmg + BONUS_DAMAGE
call SetWidgetLife(DamageHandler.source, GetWidgetLife(DamageHandler.source) /*
*/+ DamageHandler.dmg*DAMAGE_RECOVER_FACTOR)
call DestroyEffect(AddSpecialEffectTarget(MAUL_MODEL, DamageHandler.source, "head"))
call Stun.applyBuff(DamageHandler.target, dur)
endmethod
private static method onInit takes nothing returns nothing
call DamageHandler.MODIFIER_OUTGOING.register(function thistype.onDamageModify)
endmethod
endstruct
endscope
scope SharpShotDaggers
private module SharpShotDaggerConfig
static constant method operator TECH_ID takes nothing returns integer
return 'R006'
endmethod
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00B'
endmethod
static constant method operator DEBUFF_ID takes nothing returns integer
return 'B004'
endmethod
static constant method operator BONUS_CHANCE takes nothing returns integer
return 25
endmethod
static constant method operator BONUS_DAMAGE takes nothing returns real
return 35.0
endmethod
static constant method operator HP_LOSS_FACTOR takes nothing returns real
return 0.08
endmethod
static constant method operator HP_LOSS_FACTOR_HERO takes nothing returns real
return 0.0
endmethod
static constant method operator TARGET_EFFECT takes nothing returns string
return "Custom\\Model\\Buff\\Snipe Target Ex.mdx"
endmethod
static constant method operator TARGET_ATTACH takes nothing returns string
return "overhead"
endmethod
static method filterTarget takes unit source, unit target returns boolean
return (not IsUnitType(target, UNIT_TYPE_STRUCTURE)) and /*
*/ (IsUnitEnemy(target, GetOwningPlayer(source)))
endmethod
endmodule
private struct SharpShotDagger extends array
implement SharpShotDaggerConfig
private static method onDamageModify takes nothing returns nothing
local real missHP
if (GetUnitAbilityLevel(DamageHandler.source, ABILITY_ID) == 0) or /*
*/ (not IsAbilityReqLifted(GetOwningPlayer(DamageHandler.source), TECH_ID, 1)) or /*
*/ (GetUnitAbilityLevel(DamageHandler.source, DEBUFF_ID) != 0) or /*
*/ (not DamageHandler.isDamageAttack()) or /*
*/ (not thistype.filterTarget(DamageHandler.source, DamageHandler.target)) or /*
*/ (GetRandomInt(1, 100) > BONUS_CHANCE) then
return
endif
set missHP = GetUnitState(DamageHandler.target, UNIT_STATE_MAX_LIFE) - GetWidgetLife(DamageHandler.target)
if (IsUnitType(DamageHandler.target, UNIT_TYPE_HERO)) or /*
*/ (IsUnitType(DamageHandler.target, UNIT_TYPE_RESISTANT)) then
set DamageHandler.dmg = DamageHandler.dmg + BONUS_DAMAGE + missHP*HP_LOSS_FACTOR_HERO
else
set DamageHandler.dmg = DamageHandler.dmg + BONUS_DAMAGE + missHP*HP_LOSS_FACTOR
endif
call AddSpecialEffectTargetTimed(TARGET_EFFECT, DamageHandler.target, TARGET_ATTACH, 0.5)
endmethod
private static method init takes nothing returns nothing
call DamageHandler.MODIFIER_OUTGOING.register(function thistype.onDamageModify)
endmethod
implement Init
endstruct
endscope
scope Dragonhide
private module DragonhideConfig
static constant method operator TECH_ID takes nothing returns integer
return 'R005'
endmethod
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00C'
endmethod
static constant method operator PIERCE_DAMAGE_RESIST_FACTOR takes nothing returns real
return 0.20
endmethod
static constant method operator NORMAL_DAMAGE_RESIST_FACTOR takes nothing returns real
return 0.15
endmethod
static constant method operator PIERCE_DAMAGE_RESIST_BASE takes nothing returns real
return 12.0
endmethod
static constant method operator NORMAL_DAMAGE_RESIST_BASE takes nothing returns real
return 6.0
endmethod
static constant method operator MIN_DAMAGE_AFTER_REDUC takes nothing returns real
return 2.0
endmethod
static method filterAttackType takes attacktype a returns boolean
return (a == ATTACK_TYPE_MELEE) or /*
*/ (a == ATTACK_TYPE_PIERCE)
endmethod
endmodule
private struct Dragonhide extends array
implement DragonhideConfig
private static method onDamageModify takes nothing returns nothing
local real missHP
if (GetUnitAbilityLevel(DamageHandler.target, ABILITY_ID) == 0) or /*
*/ (not IsAbilityReqLifted(GetOwningPlayer(DamageHandler.target), TECH_ID, 1)) or /*
*/ (not thistype.filterAttackType(DamageHandler.attacktype)) or /*
*/ (DamageHandler.dmg <= MIN_DAMAGE_AFTER_REDUC) then
return
endif
if (DamageHandler.attacktype == ATTACK_TYPE_MELEE) then
set DamageHandler.dmg = RMaxBJ(DamageHandler.dmg*(1 - NORMAL_DAMAGE_RESIST_FACTOR) - NORMAL_DAMAGE_RESIST_BASE, MIN_DAMAGE_AFTER_REDUC)
else
set DamageHandler.dmg = RMaxBJ(DamageHandler.dmg*(1 - PIERCE_DAMAGE_RESIST_FACTOR) - PIERCE_DAMAGE_RESIST_BASE, MIN_DAMAGE_AFTER_REDUC)
endif
endmethod
private static method onInit takes nothing returns nothing
call DamageHandler.MODIFIER_OUTGOING.register(function thistype.onDamageModify)
endmethod
endstruct
endscope
scope ClusterRockets
private module ClusterRocketConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00D'
endmethod
static constant method operator DUMMY_ABIL_ID takes nothing returns integer
return 'A00E'
endmethod
static constant method operator DUMMY_ORDER_ID takes nothing returns integer
return 852652
endmethod
endmodule
private struct ClusterRockets extends array
implement ClusterRocketConfig
private static EventResponder positionResp = 0
private static IntegerList dummyList = 0
// For Dummy owners
private static integer array dummyCount
// For Dummy objects
private static IntegerListItem array dummyPtr
private static method onDummyAbilEffect takes nothing returns nothing
local unit cast = SpellHandler.unit
local integer unitID = GetUnitId(cast)
local Dummy dummy = Dummy.request(GetOwningPlayer(cast), GetUnitX(cast), /*
*/ GetUnitY(cast), 0.0)
set dummy.data = unitID
set dummyCount[unitID] = dummyCount[unitID + 1]
call dummy.addAbil(DUMMY_ABIL_ID)
call dummy.issuePointOrderId(DUMMY_ORDER_ID, SpellHandler.current.curTargetX, /*
*/ SpellHandler.current.curTargetY)
// Bind dummy to caster
set dummyPtr[dummy] = dummyList.push(dummy).last
if (dummyList.size() == 1) then
call GTimer[UPDATE_TICK].requestCallback(positionResp)
endif
set cast = null
endmethod
private static method onDummyUpdatePos takes nothing returns nothing
local IntegerListItem iter = dummyList.first
local Dummy dummy = iter.data
local unit source
loop
exitwhen iter == 0
set iter = iter.next
// Update dummy position only. Handle possible situational
// development in other threads.
set source = GetUnitById(dummy.data)
loop
if (not UnitAlive(source)) then
// Issuing this order will trigger the ENDCAST callback below.
call dummy.issueOrderId(851972)
exitwhen true
endif
set dummy.x = GetUnitX(source)
set dummy.y = GetUnitY(source)
set dummy.z = GetUnitFlyHeight(source)
exitwhen true
endloop
set dummy = iter.data
endloop
if (dummyList.empty()) then
call GTimer[UPDATE_TICK].releaseCallback(positionResp)
endif
set source = null
endmethod
private static method onDummyFinishCast takes nothing returns nothing
local Dummy dummy = Dummy[SpellHandler.unit]
local unit source = GetUnitById(dummy.data)
local integer srcID = dummy.data
// Refresh dummy
set dummy.paused = true
call dummy.removeAbil(DUMMY_ABIL_ID)
call dummy.issueOrderId(851972)
set dummy.paused = false
call dummyList.erase(dummyPtr[dummy])
set dummyPtr[dummy] = 0
set dummyCount[srcID] = dummyCount[srcID + 1]
call dummy.recycle()
set source = null
endmethod
private static method onCasterRemove takes nothing returns nothing
local IntegerListItem iter = dummyList.first
local Dummy dummy = iter.data
local unit uncaster = GetIndexedUnit()
local unit source
local integer uncastID = GetIndexedUnitId()
if (dummyCount[uncastID] == 0) then
set uncaster = null
return
endif
// Iterate through all active instances and prune off
// those associated with the unit.
loop
exitwhen iter == 0
set iter = iter.next
// Update dummy position only. Handle possible situational
// development in other threads.
loop
set source = GetUnitById(dummy.data)
exitwhen (source != uncaster)
// Issuing this order will trigger the ENDCAST callback above.
call dummy.issueOrderId(851972)
exitwhen true
endloop
set dummy = iter.data
endloop
set source = null
set uncaster = null
endmethod
private static method onInit takes nothing returns nothing
set dummyList = IntegerList.create()
set positionResp = GTimer.register(UPDATE_TICK, function thistype.onDummyUpdatePos)
call SpellHandler.register(EVENT_EFFECT, ABILITY_ID, function thistype.onDummyAbilEffect)
call SpellHandler.register(EVENT_ENDCAST, DUMMY_ABIL_ID, function thistype.onDummyFinishCast)
call OnUnitDeindex(function thistype.onCasterRemove)
endmethod
endstruct
endscope
scope ThornSkills
private module PrisonOfThornsConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00R'
endmethod
static constant method operator ORDER_ID takes nothing returns integer
return 852171
endmethod
static constant method operator RECYCLE_DELAY takes nothing returns real
return 2.0
endmethod
static method filterTarget takes unit source, unit target returns boolean
return (UnitAlive(target)) and /*
*/ (IsUnitEnemy(target, GetOwningPlayer(source))) and /*
*/ (not IsUnitType(target, UNIT_TYPE_STRUCTURE)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_MECHANICAL)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_FLYING)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)) and /*
*/ (not UnitIsSleeping(target))
endmethod
endmodule
// ======================================
// GrassyGlide Config
// ======================================
private module GrassyGlideConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00Q'
endmethod
static constant method operator THORNS_MODEL takes nothing returns string
return "Abilities\\Spells\\NightElf\\EntanglingRoots\\EntanglingRootsTarget.mdl"
endmethod
// Additional scalar multiplier.
static constant method operator THORNS_SCALE_Z takes nothing returns real
return 1.67
endmethod
static constant method operator THORNS_SCALE takes nothing returns real
return 3.0
endmethod
static constant method operator THORNS_MODEL_LIFETIME takes nothing returns real
return 3.0
endmethod
static method CASTER_AOE_RADIUS takes integer level returns real
return 250.0
endmethod
static method TARGET_AOE_RADIUS takes integer level returns real
return 250.0
endmethod
static constant method operator AOE_EFFECT_DELAY takes nothing returns real
return 0.5
endmethod
static constant method operator BLINK_DELAY takes nothing returns real
return 1.0
endmethod
endmodule
// ======================================
// Deadly Thorns Config
// ======================================
private module DeadlyThornsConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00T'
endmethod
static constant method operator GLIDE_ID takes nothing returns integer
return 'A00Q'
endmethod
static method ROOT_CHANCE takes integer level returns integer
return 5*((level - 1)*level + 2) / 2
endmethod
endmodule
// ================================================================
// Prison of Thorns
// ================================================================
private struct PrisonOfThorns extends array
implement PrisonOfThornsConfig
private static group tempGroup = null
// ======================================================
// Dummy actions
// ======================================================
private static method onDummyCleanup takes nothing returns nothing
local Dummy dummy = Dummy.current
call dummy.disableAbil('Aatk', false, false)
call dummy.removeAbil(ABILITY_ID)
endmethod
static method attemptRootArea takes unit source, real cx, real cy, real r, integer level returns nothing
local Dummy dummy = Dummy.request(GetOwningPlayer(source), cx, cy, 0.0)
local unit targ
local real maxRecycle = 0.0
local ability abil
if (level <= 0) then
call dummy.recycle()
return
endif
call dummy.disableAbil('Aatk', true, true)
call dummy.addAbil(ABILITY_ID)
call dummy.setAbilLvl(ABILITY_ID, level)
// Get the maximum duration of the ability.
set abil = BlzGetUnitAbility(dummy.dummy, ABILITY_ID)
set maxRecycle = RMaxBJ(BlzGetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_NORMAL, level - 1), /*
*/ BlzGetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_HERO, level - 1)) + RECYCLE_DELAY
call GroupEnumUnitsInRange(tempGroup, cx, cy, r, null)
loop
set targ = FirstOfGroup(tempGroup)
exitwhen (targ == null)
call GroupRemoveUnit(tempGroup, targ)
loop
exitwhen (targ == source) or (not thistype.filterTarget(source, targ))
set dummy.x = GetUnitX(targ)
set dummy.y = GetUnitY(targ)
call dummy.issueTargetOrderId(ORDER_ID, targ)
exitwhen true
endloop
endloop
call dummy.recycleTimed(maxRecycle, function thistype.onDummyCleanup)
set abil = null
set targ = null
endmethod
static method attemptRoot takes unit source, unit target, integer level returns nothing
local Dummy dummy = Dummy.request(GetOwningPlayer(source), GetUnitX(target), GetUnitY(target), 0.0)
local real maxRecycle = 0.0
local ability abil
if (level <= 0) then
call dummy.recycle()
return
endif
call dummy.disableAbil('Aatk', true, true)
call dummy.addAbil(ABILITY_ID)
call dummy.setAbilLvl(ABILITY_ID, level)
// Get the maximum duration of the ability.
set abil = BlzGetUnitAbility(dummy.dummy, ABILITY_ID)
set maxRecycle = RMaxBJ(BlzGetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_NORMAL, level - 1), /*
*/ BlzGetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_HERO, level - 1)) + RECYCLE_DELAY
call dummy.issueTargetOrderId(ORDER_ID, target)
call dummy.recycleTimed(maxRecycle, function thistype.onDummyCleanup)
set abil = null
endmethod
private static method init takes nothing returns nothing
set tempGroup = CreateGroup()
endmethod
implement Init
endstruct
// ================================================================
// Grassy Glide
// ================================================================
scope GrassyGlide
private struct GrassyGlideEffect
private boolean active
readonly timer rootTimer
readonly timer timer
effect fx
unit source
integer level
integer data
real cx
real cy
real radius
method destroy takes nothing returns nothing
if (not this.active) then
return
endif
call ReleaseTimer(this.timer)
call ReleaseTimer(this.rootTimer)
call DestroyEffect(this.fx)
set this.timer = null
set this.source = null
set this.fx = null
set this.active = false
set this.level = 0
set this.data = 0
set this.cx = 0.0
set this.cy = 0.0
call this.deallocate()
endmethod
private static method onRemoveFX takes nothing returns nothing
call thistype(GetTimerData(GetExpiredTimer())).destroy()
endmethod
method start takes real dur returns nothing
call TimerStart(this.timer, dur, false, function thistype.onRemoveFX)
endmethod
method configure takes unit source, real cx, real cy, real radius, integer level returns nothing
set this.source = source
set this.cx = cx
set this.cy = cy
set this.radius = radius
set this.level = level
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.active = true
set this.rootTimer = NewTimerEx(this)
set this.timer = NewTimerEx(this)
return this
endmethod
endstruct
private struct GrassyGlide extends array
implement GrassyGlideConfig
private static constant integer STATE_CASTING = 1
private static constant integer STATE_EFFECT = 2
private static constant integer STATE_DONE = 4
private integer stateFlag
private real cx
private real cy
private real tx
private real ty
private GrassyGlideEffect casterFx
private GrassyGlideEffect targetFx
private timer delayTimer
private method operator unit takes nothing returns unit
return GetUnitById(this)
endmethod
private static method operator [] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
// ======================================================
// Timer callback functions
// ======================================================
private static method onCasterTeleport takes nothing returns nothing
local thistype this = ReleaseTimer(GetExpiredTimer())
set this.stateFlag = STATE_DONE
set this.casterFx = 0
set this.targetFx = 0
call SetUnitX(this.unit, this.tx)
call SetUnitY(this.unit, this.ty)
endmethod
private static method onCasterFxRoot takes nothing returns nothing
local GrassyGlideEffect fx = GetTimerData(GetExpiredTimer())
local thistype this = fx.data
set this.casterFx = 0
call PrisonOfThorns.attemptRootArea(fx.source, fx.cx, fx.cy, fx.radius, fx.level)
endmethod
private static method onTargetFxRoot takes nothing returns nothing
local GrassyGlideEffect fx = GetTimerData(GetExpiredTimer())
local thistype this = fx.data
set this.targetFx = 0
call PrisonOfThorns.attemptRootArea(fx.source, fx.cx, fx.cy, fx.radius, fx.level)
endmethod
// ======================================================
// Event callback functions
// ======================================================
private static method onSpellCast takes nothing returns nothing
local thistype this = thistype[SpellHandler.unit]
local integer level = SpellHandler[this.unit].curAbilityLevel
set this.stateFlag = STATE_CASTING
set this.cx = GetUnitX(SpellHandler.unit)
set this.cy = GetUnitY(SpellHandler.unit)
set this.casterFx = GrassyGlideEffect.create()
set this.casterFx.data = this
set this.casterFx.fx = AddSpecialEffect(THORNS_MODEL, this.cx, this.cy)
call this.casterFx.start(THORNS_MODEL_LIFETIME)
call this.casterFx.configure(SpellHandler.unit, this.cx, this.cy, CASTER_AOE_RADIUS(level), level)
call TimerStart(this.casterFx.rootTimer, AOE_EFFECT_DELAY, false, function thistype.onCasterFxRoot)
call BlzSetSpecialEffectMatrixScale(this.casterFx.fx, THORNS_SCALE, THORNS_SCALE, THORNS_SCALE*THORNS_SCALE_Z)
endmethod
private static method onSpellEffect takes nothing returns nothing
local thistype this = thistype[SpellHandler.unit]
local integer level = SpellHandler[this.unit].curAbilityLevel
set this.stateFlag = STATE_EFFECT
set this.tx = SpellHandler.current.curTargetX
set this.ty = SpellHandler.current.curTargetY
set this.delayTimer = NewTimerEx(this)
set this.targetFx = GrassyGlideEffect.create()
set this.targetFx.data = this
set this.targetFx.fx = AddSpecialEffect(THORNS_MODEL, this.tx, this.ty)
call this.targetFx.start(THORNS_MODEL_LIFETIME)
call this.targetFx.configure(SpellHandler.unit, this.tx, this.ty, TARGET_AOE_RADIUS(level), level)
call TimerStart(this.targetFx.rootTimer, AOE_EFFECT_DELAY, false, function thistype.onTargetFxRoot)
call BlzSetSpecialEffectMatrixScale(this.targetFx.fx, THORNS_SCALE, THORNS_SCALE, THORNS_SCALE*THORNS_SCALE_Z)
call SetUnitX(SpellHandler.unit, this.cx)
call SetUnitY(SpellHandler.unit, this.cy)
call TimerStart(this.delayTimer, BLINK_DELAY, false, function thistype.onCasterTeleport)
endmethod
private static method onSpellEndcast takes nothing returns nothing
local thistype this = thistype[SpellHandler.unit]
loop
exitwhen (this.stateFlag == STATE_DONE)
if (this.casterFx != 0) then
call this.casterFx.destroy()
endif
if (this.targetFx != 0) then
call this.targetFx.destroy()
endif
if (this.delayTimer != null) then
call ReleaseTimer(this.delayTimer)
endif
exitwhen true
endloop
set this.stateFlag = 0
set this.tx = 0.0
set this.ty = 0.0
set this.cx = 0.0
set this.cy = 0.0
set this.casterFx = 0
set this.targetFx = 0
set this.delayTimer = null
endmethod
// ======================================================
// Initializer function
// ======================================================
private static method onInit takes nothing returns nothing
call SpellHandler.register(EVENT_CAST, ABILITY_ID, function thistype.onSpellCast)
call SpellHandler.register(EVENT_EFFECT, ABILITY_ID, function thistype.onSpellEffect)
call SpellHandler.register(EVENT_ENDCAST, ABILITY_ID, function thistype.onSpellEndcast)
endmethod
endstruct
endscope
// ================================================================
// Deadly Thorns
// ================================================================
scope DeadlyThorns
private struct DeadlyThorns extends array
implement DeadlyThornsConfig
private static method onDamage takes nothing returns nothing
local integer level = GetUnitAbilityLevel(DamageHandler.target, GLIDE_ID)
if (GetUnitAbilityLevel(DamageHandler.target, ABILITY_ID) == 0) or /*
*/ (level == 0) or /*
*/ (not DamageHandler.isDamageAttack()) or /*
*/ (not DamageHandler.isDamageMelee()) or /*
*/ (not PrisonOfThorns.filterTarget(DamageHandler.target, DamageHandler.source)) or /*
*/ (GetRandomInt(1,100) > ROOT_CHANCE(level)) then
return
endif
call PrisonOfThorns.attemptRoot(DamageHandler.target, DamageHandler.source, level)
endmethod
private static method onInit takes nothing returns nothing
call DamageHandler.ON_DAMAGE.register(function thistype.onDamage)
endmethod
endstruct
endscope
// ================================================================
// Deadly Thorns End
// ================================================================
endscope
scope Lifesurge
// TO-DO: Create an energy missile that goes back and forth from the caster's position
// to the target area.
private module LifesurgeConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00S'
endmethod
static method PULSE_DAMAGE_HEAL_FACTOR takes integer level returns real
return 0.05*level
endmethod
static constant method operator PULSE_SPEED takes nothing returns real
return 500.0
endmethod
static constant method operator PULSE_MODEL takes nothing returns string
return "Custom\\Model\\Effect\\Radiance Nature.mdx"
endmethod
static constant method operator PULSE_HEAL_MODEL takes nothing returns string
return "Abilities\\Spells\\Human\\Heal\\HealTarget.mdl"
endmethod
static constant method operator PULSE_HARM_MODEL takes nothing returns string
return "Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl"
endmethod
static constant method operator PULSE_MODEL_SCALE takes nothing returns real
return 2.0
endmethod
static constant method operator PULSE_MODEL_HEIGHT takes nothing returns real
return 200.0
endmethod
static method PULSE_DAMAGE takes integer level returns real
return 20.0*((level - 1)*(level) + 2)
endmethod
static method PULSE_HEAL takes integer level returns real
return 10.0*((level - 1)*(level) + 2)
endmethod
static method PULSE_AOE_EFFECT takes integer level returns real
return 150.0
endmethod
static constant method operator PULSE_ATTACK_TYPE takes nothing returns attacktype
return ATTACK_TYPE_NORMAL
endmethod
static constant method operator PULSE_DAMAGE_TYPE takes nothing returns damagetype
return DAMAGE_TYPE_PLANT
endmethod
static constant method operator PULSE_WEAPON_TYPE takes nothing returns weapontype
return null
endmethod
static method filterEnemy takes unit source, unit target returns boolean
return (UnitAlive(target)) and /*
*/ (IsUnitEnemy(target, GetOwningPlayer(source))) and /*
*/ (not IsUnitType(target, UNIT_TYPE_STRUCTURE)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_MECHANICAL)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_FLYING))
endmethod
static method filterAlly takes unit source, unit target returns boolean
return (UnitAlive(target)) and /*
*/ (IsUnitAlly(target, GetOwningPlayer(source))) and /*
*/ (not IsUnitType(target, UNIT_TYPE_STRUCTURE)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_MECHANICAL)) and /*
*/ (not IsUnitType(target, UNIT_TYPE_FLYING)) and /*
*/ (GetPlayerAlliance(GetOwningPlayer(source), GetOwningPlayer(target), ALLIANCE_SHARED_SPELLS))
endmethod
static method printTextTag takes unit source, real amount returns nothing
local texttag tag = CreateTextTag()
call SetTextTagPermanent(tag, false)
call SetTextTagLifespan(tag, 2.5)
call SetTextTagFadepoint(tag, 1.75)
call SetTextTagText(tag, "+ " + I2S(R2I(amount)), TextTagSize2Height(10.5))
call SetTextTagVelocity(tag, 0.0, TextTagSpeed2Velocity(80.0))
call SetTextTagColor(tag, 0x40, 0xff, 0x40, 0xff)
call SetTextTagVisibility(tag, GetOwningPlayer(source) == GetLocalPlayer())
call SetTextTagPosUnit(tag, source, 30.0)
set tag = null
endmethod
endmodule
private struct Lifesurge extends array
implement LifesurgeConfig
private static constant integer PHASE_OUTGOING = 1
private static constant integer PHASE_INCOMING = 2
private static BezierEasing bezierIn = 0
private static BezierEasing bezierOut = 0
private static group tempGroup = null
private group filterGroup
private integer phase
private integer level
private unit source
private real damage
private static method FLAGSET takes nothing returns integer
return ObjectMovement.FLAG_NO_TARGET_ON_STOP + ObjectMovement.FLAG_DESTROY_ON_TARGET_DEATH + /*
*/ ObjectMovement.FLAG_DESTROY_ON_TARGET_REMOVE
endmethod
private static method operator current takes nothing returns thistype
return ObjectMovement.current
endmethod
private static method onDest takes nothing returns nothing
local ObjectMovement object = ObjectMovement.current
local thistype this = object
call DestroyGroup(this.filterGroup)
set this.source = null
set this.filterGroup = null
set this.phase = 0
set this.level = 0
set this.damage = 0.0
endmethod
private static method onStop takes nothing returns nothing
local ObjectMovement object = ObjectMovement.current
local thistype this = object
local real healAmount = 0.0
if (this.phase == PHASE_OUTGOING) then
set this.phase = PHASE_INCOMING
call GroupClear(this.filterGroup)
set object.veloc = PULSE_SPEED
set object.easeMode = bezierOut
call object.setTargetUnitOffset(this.source, PULSE_MODEL_HEIGHT)
call object.launch()
elseif (this.phase == PHASE_INCOMING) then
set healAmount = PULSE_HEAL(this.level) + this.damage * PULSE_DAMAGE_HEAL_FACTOR(this.level)
call SetWidgetLife(this.source, GetWidgetLife(this.source) + healAmount)
call DestroyEffect(AddSpecialEffectTarget(PULSE_HEAL_MODEL, this.source, "chest"))
call thistype.printTextTag(this.source, healAmount)
call object.destroy()
endif
endmethod
private static method onMove takes nothing returns nothing
local ObjectMovement object = ObjectMovement.current
local thistype this = object
local real cx = BlzGetLocalSpecialEffectX(object.effect)
local real cy = BlzGetLocalSpecialEffectY(object.effect)
local real prev = 0.0
local unit targ
// Filter targets
call GroupEnumUnitsInRange(tempGroup, cx, cy, PULSE_AOE_EFFECT(this.level), null)
loop
set targ = FirstOfGroup(tempGroup)
call GroupRemoveUnit(tempGroup, targ)
exitwhen (targ == null)
loop
exitwhen (targ == this.source) or (IsUnitInGroup(targ, this.filterGroup))
if thistype.filterEnemy(this.source, targ) then
set prev = GetWidgetLife(targ)
call AddSpecialEffectTargetTimed(PULSE_HARM_MODEL, targ, "chest", 3.0)
call UnitDamageTarget(this.source, targ, PULSE_DAMAGE(this.level), false, /*
*/ true, PULSE_ATTACK_TYPE, PULSE_DAMAGE_TYPE, PULSE_WEAPON_TYPE)
set this.damage = this.damage + (prev - GetWidgetLife(targ))
call GroupAddUnit(this.filterGroup, targ)
elseif thistype.filterAlly(this.source, targ) then
call AddSpecialEffectTargetTimed(PULSE_HEAL_MODEL, targ, "chest", 1.0)
call SetWidgetLife(targ, GetWidgetLife(targ) + PULSE_HEAL(this.level))
call GroupAddUnit(this.filterGroup, targ)
endif
exitwhen true
endloop
endloop
endmethod
private static method onSpellEffect takes nothing returns nothing
local ObjectMovement object = thistype.applyCustomMovement(PULSE_MODEL, GetUnitX(SpellHandler.unit), GetUnitY(SpellHandler.unit))
local thistype this = thistype(object)
set this.source = SpellHandler.unit
set this.phase = PHASE_OUTGOING
set this.level = SpellHandler.current.curAbilityLevel
set this.filterGroup = CreateGroup()
call SetSpecialEffectHeight(object.effect, PULSE_MODEL_HEIGHT)
call BlzSetSpecialEffectMatrixScale(object.effect, PULSE_MODEL_SCALE, PULSE_MODEL_SCALE, PULSE_MODEL_SCALE)
set object.veloc = PULSE_SPEED
set object.easeMode = bezierIn
call object.setTargetArea(SpellHandler.current.curTargetX, SpellHandler.current.curTargetY, /*
*/ PULSE_MODEL_HEIGHT)
call object.launch()
endmethod
private static method onInit takes nothing returns nothing
set tempGroup = CreateGroup()
set bezierIn = BezierEase.inOutSine
set bezierOut = BezierEase.inOutSine
call SpellHandler.register(EVENT_EFFECT, ABILITY_ID, function thistype.onSpellEffect)
endmethod
implement ObjectMovementTemplate
endstruct
endscope
scope HarmonicShatter
private module HarmonicShatterConfig
readonly static integer array RESET_ABIL_ID
static group tempGroup = null
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00U'
endmethod
private static method onInit takes nothing returns nothing
set RESET_ABIL_ID[1] = 'A00Q'
set RESET_ABIL_ID[2] = 'A00S'
endmethod
endmodule
private struct HarmonicShatter extends array
implement HarmonicShatterConfig
private static method onSpellEffect takes nothing returns nothing
local integer i = 1
loop
exitwhen (RESET_ABIL_ID[i] == 0)
call BlzEndUnitAbilityCooldown(SpellHandler.unit, RESET_ABIL_ID[i])
set i = i + 1
endloop
endmethod
private static method onInit takes nothing returns nothing
call SpellHandler.register(EVENT_EFFECT, ABILITY_ID, function thistype.onSpellEffect)
endmethod
endstruct
endscope
scope DaylightInvisibility
private module DaylightInvisibilityConfig
static constant method operator ABILITY_ID takes nothing returns integer
return 'A00K'
endmethod
static constant method operator TINT_ALPHA_START takes nothing returns integer
return 0xff
endmethod
static constant method operator TINT_ALPHA_END takes nothing returns integer
return 0x7f
endmethod
endmodule
private struct DaylightInvisibility extends array
implement DaylightInvisibilityConfig
private static group tentGroup = null
private static real INVISIBILITY_DUR = 0.0
// ======================================================
// Event callback functions
// ======================================================
private static method onEnter takes nothing returns nothing
if (GetUnitAbilityLevel(StructureHandler.structure, ABILITY_ID) == 0) then
return
endif
call GroupAddUnit(tentGroup, StructureHandler.structure)
if (not DNCycle.isDay) then
call BlzUnitDisableAbility(StructureHandler.structure, ABILITY_ID, true, true)
else
call VisibilityManager.unitApply(StructureHandler.structure, TINT_ALPHA_START, TINT_ALPHA_END, INVISIBILITY_DUR)
endif
endmethod
private static method onLeave takes nothing returns nothing
call GroupRemoveUnit(tentGroup, StructureHandler.structure)
endmethod
private static method onDaylight takes nothing returns nothing
local integer i = 0
local integer n = BlzGroupGetSize(tentGroup)
local boolean enable = (not DNCycle.isDay)
local unit temp
loop
exitwhen i >= n
set temp = BlzGroupUnitAt(tentGroup, i)
if (not enable) then
call VisibilityManager.unitApply(temp, TINT_ALPHA_START, TINT_ALPHA_END, INVISIBILITY_DUR)
else
call VisibilityManager.unitApply(temp, TINT_ALPHA_END, TINT_ALPHA_START, INVISIBILITY_DUR)
endif
call BlzUnitDisableAbility(temp, ABILITY_ID, enable, enable)
set i = i + 1
endloop
endmethod
// ======================================================
// Initializing function
// ======================================================
private static method initInviDur takes nothing returns nothing
local Dummy dummy = Dummy.request(Player(PLAYER_NEUTRAL_PASSIVE), 0.0, 0.0, 0.0)
call dummy.addAbil(ABILITY_ID)
set INVISIBILITY_DUR = BlzGetAbilityRealLevelField(BlzGetUnitAbility(dummy.dummy, ABILITY_ID), ABILITY_RLF_DURATION_NORMAL, 0)
call dummy.removeAbil(ABILITY_ID)
call dummy.recycle()
endmethod
private static method onInit takes nothing returns nothing
set tentGroup = CreateGroup()
call thistype.initInviDur()
call StructureHandler.ON_ENTER.register(function thistype.onEnter)
call StructureHandler.ON_LEAVE.register(function thistype.onLeave)
call DNCycle.ON_DAY.register(function thistype.onDaylight)
call DNCycle.ON_NIGHT.register(function thistype.onDaylight)
endmethod
endstruct
endscope
scope TentSpells
// =======================================================================
// Lumber gathering
private module LumberSynthesisConfig
static constant method operator LUMBER_ABIL_ID takes nothing returns integer
return 'A00L'
endmethod
static constant method operator LUMBER_ENUM_RANGE takes nothing returns real
return 1800.0
endmethod
static constant method operator LUMBER_GENERATE_AMOUNT takes nothing returns integer
return 10
endmethod
static constant method operator LUMBER_GENERATE_INTERVAL takes nothing returns real
return 15.0
endmethod
static constant method operator LUMBER_GENERATE_WISP_MAX takes nothing returns integer
return 4
endmethod
static constant method operator LUMBER_GARRISON_UNIT_ID takes nothing returns integer
return 'e003'
endmethod
static constant method operator LUMBER_GENERATE_WISP_INTERVAL takes nothing returns real
return 30.0
endmethod
static constant method operator LUMBER_GENERATE_MODEL_TIME takes nothing returns real
return 0.380
endmethod
static constant method operator LUMBER_GENERATE_MODEL_HEIGHT takes nothing returns real
return 200.0
endmethod
static constant method operator LUMBER_GENERATE_MODEL takes nothing returns string
return "Custom\\Model\\Unit\\WispEx.mdx"
endmethod
static constant method operator LUMBER_GATHER_MODEL takes nothing returns string
return "Custom\\Model\\Effect\\Soul Armor Lime Ex.mdx"
endmethod
static constant method operator LUMBER_EMPTY_MODEL takes nothing returns string
return "Custom\\Model\\dummy.mdx"
endmethod
static constant method operator LUMBER_GENERATE_MODEL_SCALE takes nothing returns real
return 1.50
endmethod
static constant method operator LUMBER_GATHER_MODEL_Z takes nothing returns real
return 80.0
endmethod
static constant method operator LUMBER_VISION_RADIUS takes nothing returns real
return 300.0
endmethod
static constant method operator LUMBER_ALPHA_START takes nothing returns integer
return 0xff
endmethod
static constant method operator LUMBER_ALPHA_END takes nothing returns integer
return 0x7f
endmethod
static constant method operator LUMBER_FADE_TRANSITION takes nothing returns real
return 4.0
endmethod
endmodule
private module LumberMissileConfig
static constant method operator LUMBER_MISSILE_SPEED takes nothing returns real
return 600.0
endmethod
static constant method operator LUMBER_MISSILE_RETURN_SPEED takes nothing returns real
return 300.0
endmethod
static constant method operator LUMBER_MISSILE_Z_START takes nothing returns real
return 200.0
endmethod
static constant method operator LUMBER_MISSILE_Z_END takes nothing returns real
return 50.0
endmethod
static method MISSILE_MODEL takes nothing returns string
return "Custom\\Model\\Projectile\\Chaos Missile.mdx"
endmethod
static method FLAGSET takes nothing returns integer
return ObjectMovement.FLAG_DESTROY_ON_STOP
endmethod
endmodule
// =======================================================================
// Tent Regeneration Ability
private module TentLoadUnloadConfig
static constant method operator REGEN_ABIL_ID takes nothing returns integer
return 'A00M'
endmethod
static constant method operator REGEN_HP_AMOUNT takes nothing returns real
return 5.0
endmethod
static constant method operator REGEN_MP_AMOUNT takes nothing returns real
return 3.0
endmethod
static constant method operator REGEN_TICK takes nothing returns integer
return 1
endmethod
endmodule
// ======================================================================
// Lumber Synthesis Mechanics
// ======================================================================
scope LumberSynthesis
private keyword LumberSynthesis
private struct LumberTreeMap extends array
implement Alloc
destructable dest
effect treeFX
LumberSynthesis owner
boolean isActive
timer lumberTimer
IntegerListItem ptr
IntegerListItem spiritPtr
fogmodifier vision
method operator x takes nothing returns real
return GetDestructableX(this.dest)
endmethod
method operator y takes nothing returns real
return GetDestructableY(this.dest)
endmethod
method destroy takes nothing returns nothing
if (this.treeFX != null) then
call DestroyEffect(this.treeFX)
endif
if (this.lumberTimer != null) then
call ReleaseTimer(this.lumberTimer)
endif
if (this.vision != null) then
call FogModifierStop(this.vision)
call DestroyFogModifier(this.vision)
endif
set this.dest = null
set this.treeFX = null
set this.lumberTimer = null
set this.vision = null
set this.isActive = false
set this.owner = 0
set this.ptr = 0
set this.spiritPtr = 0
call this.deallocate()
endmethod
static method create takes destructable d, LumberSynthesis owner returns thistype
local thistype this = thistype.allocate()
set this.dest = d
set this.owner = owner
return this
endmethod
endstruct
private struct LumberMissile extends array
implement LumberMissileConfig
readonly static constant integer PHASE_FORWARD = 1
readonly static constant integer PHASE_RETURN = 2
readonly static BezierEase FORWARD_EASE = 0
readonly static BezierEase RETURN_EASE = 0
static EventResponder forwardResp = 0
static EventResponder returnResp = 0
static EventResponder monitorResp = 0
integer phase
private static method onStop takes nothing returns nothing
local thistype this = ObjectMovement.current
if (this.phase == PHASE_FORWARD) then
call forwardResp.run()
else
call returnResp.run()
endif
set this.phase = 0
endmethod
private static method onMove takes nothing returns nothing
call monitorResp.run()
endmethod
private static method init takes nothing returns nothing
set FORWARD_EASE = BezierEasing.create(0.67, 0.0, 0.8, 0.35)
set RETURN_EASE = BezierEase.linear
endmethod
implement Init
implement ObjectMovementTemplate
endstruct
private struct LumberSynthesis extends array
implement LumberSynthesisConfig
private static code generateWispPtr = null
private static Table destMap = 0
private static IntegerList tentList = 0
private static IntegerList destList = 0
private static EventResponder destResp = 0
private static EventResponder tentResp = 0
private static constant integer PHASE_GENERATE = 1
private static constant integer PHASE_HOLD = 2
private integer currentPhase
private IntegerListItem tentPtr
private boolean inUse
private boolean isGenerating
private boolean alreadyEnumed
private boolean wasConstructing
private IntegerList spiritList
private integer workerCount
private integer activeSpiritCount
private timer generatorTimer
private effect generatorFx
private real generatorZ
private static method operator [] takes unit source returns thistype
return GetUnitId(source)
endmethod
method operator unit takes nothing returns unit
return GetUnitById(this)
endmethod
// ======================================================
// Relation clearing function
// ======================================================
private method removeTree takes LumberTreeMap map returns nothing
local real cz = 0.0
set this.activeSpiritCount = this.activeSpiritCount - 1
if (map.spiritPtr != 0) then
call this.spiritList.remove(map.spiritPtr)
endif
call destList.remove(map.ptr)
call destMap.integer.remove(GetHandleId(map.dest))
call map.destroy()
if (this.currentPhase == PHASE_HOLD) then
set this.currentPhase = PHASE_GENERATE
set this.alreadyEnumed = false
set this.generatorTimer = NewTimerEx(this)
set this.generatorFx = AddSpecialEffect(LUMBER_GENERATE_MODEL, GetUnitX(this.unit), GetUnitY(this.unit))
call BlzSetSpecialEffectMatrixScale(this.generatorFx, LUMBER_GENERATE_MODEL_SCALE, LUMBER_GENERATE_MODEL_SCALE, LUMBER_GENERATE_MODEL_SCALE)
call BlzSetSpecialEffectZ(this.generatorFx, BlzGetLocalSpecialEffectZ(this.generatorFx) + LUMBER_GENERATE_MODEL_HEIGHT)
set cz = BlzGetLocalSpecialEffectZ(this.generatorFx)
if (DNCycle.isDay) and (not IsUnitVisible(this.unit, GetLocalPlayer())) then
call BlzSetSpecialEffectX(this.generatorFx, WorldBounds.minX)
call BlzSetSpecialEffectY(this.generatorFx, WorldBounds.minY)
call BlzSetSpecialEffectZ(this.generatorFx, cz)
endif
if (this.workerCount > 0) then
call TimerStart(this.generatorTimer, LUMBER_GENERATE_WISP_INTERVAL / I2R(this.workerCount), false, generateWispPtr)
endif
endif
if (destList.empty()) then
call GTimer[UPDATE_TICK].releaseCallback(destResp)
endif
endmethod
private method destroy takes nothing returns nothing
local LumberTreeMap map = 0
if (not this.inUse) then
return
endif
// Clear out all used destructables.
if (this.spiritList != 0) then
loop
exitwhen this.spiritList.empty()
set map = this.spiritList.first.data
call this.removeTree(map)
endloop
call this.spiritList.destroy()
endif
if (this.generatorTimer != null) then
call ReleaseTimer(this.generatorTimer)
endif
if (this.generatorFx != null) then
call BlzSetSpecialEffectTimeScale(this.generatorFx, 1.0)
call DestroyEffect(this.generatorFx)
endif
call tentList.erase(this.tentPtr)
set this.inUse = false
set this.alreadyEnumed = false
set this.generatorTimer = null
set this.generatorFx = null
set this.currentPhase = 0
set this.activeSpiritCount = 0
set this.workerCount = 0
set this.spiritList = 0
set this.tentPtr = 0
endmethod
// ======================================================
// Desireable Tree filter function
// ======================================================
private static method isValidTree takes destructable dest, boolean checkSize returns boolean
local real cx = GetDestructableX(dest)
local real cy = GetDestructableY(dest)
local real dist = (cx - sourceCX)*(cx - sourceCX) + /*
*/ (cy - sourceCY)*(cy - sourceCY)
// Filter out trees that are visible
if (not IsDestructableTree(dest)) or /*
*/ (IsDestructableDead(dest)) or /*
*/ (4.0*dist > LUMBER_ENUM_RANGE*LUMBER_ENUM_RANGE) or /*
*/ (not IsVisibleToPlayer(cx, cy, GetOwningPlayer(sourceInstance.unit))) or /*
*/ (destMap.integer.has(GetHandleId(dest))) or /*
*/ ((checkSize) and /*
*/ (sourceInstance.spiritList.size() >= LUMBER_GENERATE_WISP_MAX)) then
return false
endif
return true
endmethod
// ======================================================
// Extension Callback handlers
// - onFeedbackLumber
// ======================================================
private static method onFeedbackLumber takes nothing returns nothing
local LumberTreeMap map = GetTimerData(GetExpiredTimer())
local thistype this = map.owner
local ObjectMovement object = 0
local string model = LumberMissile.MISSILE_MODEL()
if (not this.inUse) then
return
endif
if (not IsUnitVisible(this.unit, GetLocalPlayer())) then
set model = LUMBER_EMPTY_MODEL
endif
set object = LumberMissile.applyCustomMovement(model, map.x, map.y)
set object.veloc = LumberMissile.LUMBER_MISSILE_RETURN_SPEED
set object.data = map
set object.easeMode = LumberMissile.RETURN_EASE
set LumberMissile(object).phase = LumberMissile.PHASE_RETURN
call object.setTargetAreaXY(GetUnitX(this.unit), GetUnitY(this.unit))
call object.launch()
endmethod
// ======================================================
// Extension Callback handlers
// - onGenerateWisp
// ======================================================
private static real sourceCX = 0.0
private static real sourceCY = 0.0
private static thistype sourceInstance = 0
private method generateForwardMissile takes LumberTreeMap map returns nothing
local string model = LumberMissile.MISSILE_MODEL()
local ObjectMovement object = 0
if (not IsUnitVisible(this.unit, GetLocalPlayer())) then
set model = LUMBER_EMPTY_MODEL
endif
set object = LumberMissile.applyCustomMovement(model, sourceCX, sourceCY)
call SetSpecialEffectHeight(object.effect, LumberMissile.LUMBER_MISSILE_Z_START)
set object.veloc = LumberMissile.LUMBER_MISSILE_SPEED
set object.data = map
set object.easeMode = LumberMissile.FORWARD_EASE
set LumberMissile(object).phase = LumberMissile.PHASE_FORWARD
call object.setTargetArea(map.x, map.y, LumberMissile.LUMBER_MISSILE_Z_END)
call object.launch()
endmethod
private static method onSelectTrees takes nothing returns nothing
local destructable dest = GetEnumDestructable()
local LumberTreeMap map = 0
// Filter out trees that are visible
if (not thistype.isValidTree(dest, true)) then
set dest = null
return
endif
set map = LumberTreeMap.create(dest, sourceInstance)
set map.ptr = destList.push(map).last
set destMap.integer[GetHandleId(dest)] = map
set map.spiritPtr = sourceInstance.spiritList.push(map).last
if (destList.size() == 1) then
call GTimer[UPDATE_TICK].requestCallback(destResp)
endif
endmethod
private static method onGenerateWisp takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real cz
local rect enumRect
local IntegerListItem iter
local LumberTreeMap map
set sourceInstance = this
set sourceCX = GetUnitX(this.unit)
set sourceCY = GetUnitY(this.unit)
if (not this.alreadyEnumed) then
set this.alreadyEnumed = true
if (this.spiritList == 0) then
set this.spiritList = IntegerList.create()
endif
set enumRect = Rect(sourceCX - LUMBER_ENUM_RANGE * 0.5, sourceCY - LUMBER_ENUM_RANGE * 0.5, /*
*/ sourceCX + LUMBER_ENUM_RANGE * 0.5, sourceCY + LUMBER_ENUM_RANGE * 0.5)
call EnumDestructablesInRect(enumRect, null, function thistype.onSelectTrees)
call RemoveRect(enumRect)
set enumRect = null
endif
if (this.activeSpiritCount >= LUMBER_GENERATE_WISP_MAX) then
return
endif
set this.activeSpiritCount = this.activeSpiritCount + 1
set iter = this.spiritList.first
loop
set map = iter.data
exitwhen (iter == 0)
if (not map.isActive) then
set map.isActive = true
exitwhen true
endif
set iter = iter.next
endloop
// Send a missile directed from the wisp to the tree.
call this.generateForwardMissile(map)
// Stop once we've reached the max
if (this.activeSpiritCount >= LUMBER_GENERATE_WISP_MAX) then
set this.currentPhase = PHASE_HOLD
set this.isGenerating = false
call BlzSetSpecialEffectTimeScale(this.generatorFx, 1.0)
call DestroyEffect(this.generatorFx)
call ReleaseTimer(this.generatorTimer)
set this.generatorFx = null
set this.generatorTimer = null
return
endif
// Reset the wisp model.
call BlzSetSpecialEffectTimeScale(this.generatorFx, 0.0)
call BlzSetSpecialEffectZ(this.generatorFx, -RAbsBJ(BlzGetLocalSpecialEffectZ(this.generatorFx)))
call DestroyEffect(this.generatorFx)
set this.generatorFx = AddSpecialEffect(LUMBER_GENERATE_MODEL, GetUnitX(this.unit), GetUnitY(this.unit))
call BlzSetSpecialEffectMatrixScale(this.generatorFx, LUMBER_GENERATE_MODEL_SCALE, LUMBER_GENERATE_MODEL_SCALE, LUMBER_GENERATE_MODEL_SCALE)
call BlzSetSpecialEffectTimeScale(this.generatorFx, LUMBER_GENERATE_MODEL_TIME / LUMBER_GENERATE_WISP_INTERVAL * this.workerCount)
// The coordinates of this particular special effect
// are not meant to be used synchronously.
call BlzSetSpecialEffectZ(this.generatorFx, this.generatorZ)
if (DNCycle.isDay) and (not IsUnitVisible(this.unit, GetLocalPlayer())) then
call BlzSetSpecialEffectX(this.generatorFx, WorldBounds.minX)
call BlzSetSpecialEffectY(this.generatorFx, WorldBounds.minY)
call BlzSetSpecialEffectZ(this.generatorFx, this.generatorZ)
endif
call TimerStart(this.generatorTimer, LUMBER_GENERATE_WISP_INTERVAL / I2R(this.workerCount), false, function thistype.onGenerateWisp)
endmethod
// ==========================================================
// Tree checking functions.
// ==========================================================
private static destructable nextTree = null
private static method onLocateAdjacentTree takes nothing returns nothing
local destructable dest = GetEnumDestructable()
// Filter out trees that are visible
if (nextTree != null) or /*
*/ (not thistype.isValidTree(dest, false)) then
set dest = null
return
endif
set nextTree = dest
set dest = null
endmethod
private method findNextTree takes LumberTreeMap map returns boolean
local rect enumRect
set nextTree = null
set sourceCX = map.x
set sourceCY = map.y
set sourceInstance = this
set enumRect = Rect(sourceCX - LUMBER_ENUM_RANGE * 0.5, sourceCY - LUMBER_ENUM_RANGE * 0.5, /*
*/ sourceCX + LUMBER_ENUM_RANGE * 0.5, sourceCY + LUMBER_ENUM_RANGE * 0.5)
call EnumDestructablesInRect(enumRect, null, function thistype.onLocateAdjacentTree)
call RemoveRect(enumRect)
set enumRect = null
return (nextTree != null)
endmethod
private method moveToTree takes LumberTreeMap map returns nothing
local string model = LumberMissile.MISSILE_MODEL()
local ObjectMovement object = 0
if (not IsUnitVisible(this.unit, GetLocalPlayer())) then
set model = LUMBER_EMPTY_MODEL
endif
set object = LumberMissile.applyCustomMovement(model, sourceCX, sourceCY)
call SetSpecialEffectHeight(object.effect, LumberMissile.LUMBER_MISSILE_Z_END)
set object.veloc = LumberMissile.LUMBER_MISSILE_SPEED
set object.data = map
set object.easeMode = LumberMissile.FORWARD_EASE
set LumberMissile(object).phase = LumberMissile.PHASE_FORWARD
call destMap.integer.remove(GetHandleId(map.dest))
set map.dest = nextTree
set destMap.integer[GetHandleId(map.dest)] = map
// Since the timer is created upon missile hit, might as
// well release the old timer.
call ReleaseTimer(map.lumberTimer)
set map.lumberTimer = null
call object.setTargetArea(map.x, map.y, LumberMissile.LUMBER_MISSILE_Z_END)
set EffectVisibility[object.effect].visible = true
set EffectVisibility[object.effect].visible = false
if (not IsUnitVisible(this.unit, GetLocalPlayer())) then
set EffectVisibility[object.effect].visible = false
endif
call object.launch()
endmethod
// ==========================================================
// Event Callback Handlers.
// ==========================================================
private static method onTentLoad takes nothing returns nothing
local thistype this
local real remain = 0.0
local real cz = 0.0
local unit tent
if (GetUnitAbilityLevel(UnitAuxHandler.curTransport, LUMBER_ABIL_ID) == 0) or /*
*/ (GetUnitTypeId(UnitAuxHandler.unit) != LUMBER_GARRISON_UNIT_ID) then
return
endif
set tent = UnitAuxHandler.curTransport
set this = thistype[tent]
set this.workerCount = this.workerCount + 1
if (this.currentPhase == PHASE_HOLD) then
return
endif
if (this.currentPhase == 0) then
set this.inUse = true
set this.currentPhase = PHASE_GENERATE
// Let this instance point to a node in the list
// that manages the visibility of certain fx.
if (this.tentPtr == 0) then
set this.tentPtr = tentList.push(this).last
endif
endif
if (this.workerCount == 1) then
if (this.generatorTimer == null) then
set this.generatorTimer = NewTimerEx(this)
set remain = LUMBER_GENERATE_WISP_INTERVAL
else
set remain = TimerGetRemaining(this.generatorTimer)
endif
if (this.generatorFx == null) then
set this.generatorFx = AddSpecialEffect(LUMBER_GENERATE_MODEL, GetUnitX(tent), GetUnitY(tent))
set cz = BlzGetLocalSpecialEffectZ(this.generatorFx) + LUMBER_GENERATE_MODEL_HEIGHT
call BlzSetSpecialEffectMatrixScale(this.generatorFx, LUMBER_GENERATE_MODEL_SCALE, LUMBER_GENERATE_MODEL_SCALE, LUMBER_GENERATE_MODEL_SCALE)
// The coordinates of this particular special effect
// are not meant to be used synchronously.
set this.generatorZ = cz
call BlzSetSpecialEffectZ(this.generatorFx, cz)
if (DNCycle.isDay) and (not IsUnitVisible(this.unit, GetLocalPlayer())) then
call BlzSetSpecialEffectX(this.generatorFx, WorldBounds.minX)
call BlzSetSpecialEffectY(this.generatorFx, WorldBounds.minY)
call BlzSetSpecialEffectZ(this.generatorFx, this.generatorZ)
endif
endif
elseif (this.currentPhase == PHASE_GENERATE) then
set remain = TimerGetRemaining(this.generatorTimer)
call PauseTimer(this.generatorTimer)
call TimerStart(this.generatorTimer, 0.0, false, null)
call PauseTimer(this.generatorTimer)
set remain = remain / I2R(this.workerCount) * (this.workerCount - 1)
endif
call BlzSetSpecialEffectTimeScale(this.generatorFx, LUMBER_GENERATE_MODEL_TIME / LUMBER_GENERATE_WISP_INTERVAL * this.workerCount)
call TimerStart(this.generatorTimer, remain, false, function thistype.onGenerateWisp)
set tent = null
endmethod
private static method onTentUnload takes nothing returns nothing
local thistype this
local real remain = 0.0
local unit tent
set tent = UnitAuxHandler.curTransport
set this = thistype[tent]
if (not this.inUse) then
set tent = null
return
endif
set this.workerCount = IMaxBJ(this.workerCount - 1, 0)
if (this.currentPhase != PHASE_GENERATE) then
set tent = null
return
endif
call BlzSetSpecialEffectTimeScale(this.generatorFx, LUMBER_GENERATE_MODEL_TIME / LUMBER_GENERATE_WISP_INTERVAL * this.workerCount)
if (this.workerCount == 0) then
call PauseTimer(this.generatorTimer)
else
set remain = TimerGetRemaining(this.generatorTimer)
call PauseTimer(this.generatorTimer)
call TimerStart(this.generatorTimer, 0.0, false, null)
call PauseTimer(this.generatorTimer)
set remain = remain / I2R(this.workerCount) * (this.workerCount + 1)
call TimerStart(this.generatorTimer, remain, false, function thistype.onGenerateWisp)
endif
set tent = null
endmethod
private static method onTentDeath takes nothing returns nothing
local thistype this = thistype[UnitAuxHandler.unit]
call this.destroy()
endmethod
private static method onTentRemove takes nothing returns nothing
local thistype this = thistype[GetIndexedUnit()]
call this.destroy()
endmethod
private static method onForwardMissileHit takes nothing returns nothing
local LumberTreeMap map = ObjectMovement.current.data
local thistype this = map.owner
if (not this.inUse) then
return
endif
set map.lumberTimer = NewTimerEx(map)
set map.treeFX = AddSpecialEffect(LUMBER_GATHER_MODEL, map.x, map.y)
set map.vision = CreateFogModifierRadius(GetOwningPlayer(this.unit), FOG_OF_WAR_VISIBLE, /*
*/ map.x, map.y, LUMBER_VISION_RADIUS, true, true)
call FogModifierStart(map.vision)
if (DNCycle.isDay) and (not IsUnitVisible(this.unit, GetLocalPlayer())) then
call BlzSetSpecialEffectX(map.treeFX, WorldBounds.minX)
call BlzSetSpecialEffectY(map.treeFX, WorldBounds.minY)
endif
if (DNCycle.isDay) then
call VisibilityManager.effectApply(map.treeFX, LUMBER_ALPHA_START, /*
*/ LUMBER_ALPHA_END, LUMBER_FADE_TRANSITION)
endif
call BlzSetSpecialEffectZ(map.treeFX, BlzGetLocalSpecialEffectZ(map.treeFX) + LUMBER_GATHER_MODEL_Z)
call TimerStart(map.lumberTimer, LUMBER_GENERATE_INTERVAL, true, function thistype.onFeedbackLumber)
endmethod
private static method onReturnMissileHit takes nothing returns nothing
local LumberTreeMap map = ObjectMovement.current.data
local thistype this = map.owner
local player p
local texttag tag
if (not this.inUse) then
return
endif
set p = GetOwningPlayer(this.unit)
set tag = CreateTextTag()
call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, /*
*/ GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER) + LUMBER_GENERATE_AMOUNT)
call SetPlayerState(p, PLAYER_STATE_LUMBER_GATHERED, /*
*/ GetPlayerState(p, PLAYER_STATE_LUMBER_GATHERED) + LUMBER_GENERATE_AMOUNT)
// ===============================
// Text tag handling.
// ===============================
call SetTextTagPermanent(tag, false)
call SetTextTagLifespan(tag, 3.0)
call SetTextTagFadepoint(tag, 2.25)
call SetTextTagColor(tag, 0x40, 0xff, 0x40, 0xff)
call SetTextTagPosUnit(tag, this.unit, 20.0)
call SetTextTagText(tag, "+ " + I2S(LUMBER_GENERATE_AMOUNT), TextTagSize2Height(10.5))
call SetTextTagVelocity(tag, 0.0, TextTagSpeed2Velocity(100.0))
if (GetLocalPlayer() != p) then
call SetTextTagVisibility(tag, false)
endif
endmethod
private static method onMissileMonitor takes nothing returns nothing
local LumberTreeMap map = ObjectMovement.current.data
local thistype this = map.owner
if (not this.inUse) then
call ObjectMovement.current.stop()
endif
endmethod
private static method onTentStart takes nothing returns nothing
local thistype this = thistype[StructureHandler.structure]
if (GetUnitAbilityLevel(this.unit, LUMBER_ABIL_ID) == 0) then
return
endif
set this.wasConstructing = true
call BlzUnitDisableAbility(this.unit, LUMBER_ABIL_ID, true, true)
endmethod
private static method onTentEnter takes nothing returns nothing
local thistype this = thistype[StructureHandler.structure]
if (GetUnitAbilityLevel(this.unit, LUMBER_ABIL_ID) == 0) then
return
endif
if (this.wasConstructing) then
set this.wasConstructing = false
call BlzUnitDisableAbility(this.unit, LUMBER_ABIL_ID, false, false)
endif
endmethod
private static method onTentFXShowHide takes nothing returns nothing
local IntegerListItem iter = tentList.first
local IntegerListItem subIter = 0
local LumberTreeMap map = 0
local thistype this = iter.data
local boolean isVisible = true
local real cz = 0.0
loop
exitwhen (tentList.empty())
if (DNCycle.isDay) then
call GTimer[UPDATE_TICK].requestCallback(tentResp)
else
call GTimer[UPDATE_TICK].releaseCallback(tentResp)
endif
exitwhen true
endloop
loop
exitwhen (iter == 0)
set iter = iter.next
set isVisible = IsUnitVisible(this.unit, GetLocalPlayer())
loop
exitwhen (this.generatorFx == null)
if (not DNCycle.isDay) then
call BlzSetSpecialEffectX(this.generatorFx, GetUnitX(this.unit))
call BlzSetSpecialEffectY(this.generatorFx, GetUnitY(this.unit))
call BlzSetSpecialEffectZ(this.generatorFx, this.generatorZ)
call VisibilityManager.effectApply(this.generatorFx, LUMBER_ALPHA_END, /*
*/ LUMBER_ALPHA_START, LUMBER_FADE_TRANSITION)
else
if (not isVisible) then
call BlzSetSpecialEffectX(this.generatorFx, WorldBounds.minX)
call BlzSetSpecialEffectY(this.generatorFx, WorldBounds.minY)
call BlzSetSpecialEffectZ(this.generatorFx, this.generatorZ)
endif
call VisibilityManager.effectApply(this.generatorFx, LUMBER_ALPHA_START, /*
*/ LUMBER_ALPHA_END, LUMBER_FADE_TRANSITION)
endif
exitwhen true
endloop
set subIter = this.spiritList.first
set map = subIter.data
loop
exitwhen (subIter == 0)
loop
exitwhen (map.treeFX == null)
set cz = BlzGetLocalSpecialEffectZ(map.treeFX)
if (not DNCycle.isDay) then
call BlzSetSpecialEffectX(map.treeFX, map.x)
call BlzSetSpecialEffectY(map.treeFX, map.y)
call BlzSetSpecialEffectZ(map.treeFX, cz)
call VisibilityManager.effectApply(map.treeFX, LUMBER_ALPHA_END, /*
*/ LUMBER_ALPHA_START, LUMBER_FADE_TRANSITION)
else
if (not isVisible) then
call BlzSetSpecialEffectX(map.treeFX, WorldBounds.minX)
call BlzSetSpecialEffectY(map.treeFX, WorldBounds.minY)
call BlzSetSpecialEffectZ(map.treeFX, cz)
endif
call VisibilityManager.effectApply(map.treeFX, LUMBER_ALPHA_START, /*
*/ LUMBER_ALPHA_END, LUMBER_FADE_TRANSITION)
endif
exitwhen true
endloop
set subIter = subIter.next
set map = subIter.data
endloop
set this = iter.data
endloop
endmethod
private static method checkTreeStatus takes nothing returns nothing
local IntegerListItem iter = destList.first
local LumberTreeMap map = iter.data
local thistype this = 0
loop
exitwhen (iter == 0)
set iter = iter.next
loop
exitwhen (IsDestructableAlive(map.dest))
set this = map.owner
call DestroyEffect(map.treeFX)
call FogModifierStop(map.vision)
call DestroyFogModifier(map.vision)
set map.treeFX = null
set map.vision = null
if (not this.findNextTree(map)) then
call this.removeTree(map)
else
call this.moveToTree(map)
endif
exitwhen true
endloop
set map = iter.data
endloop
endmethod
private static method checkTentVisibility takes nothing returns nothing
local IntegerListItem iter = tentList.first
local IntegerListItem subIter = 0
local LumberTreeMap map = 0
local thistype this = iter.data
local boolean isVisible = true
local real cz = 0.0
loop
exitwhen (iter == 0)
set iter = iter.next
set isVisible = IsUnitVisible(this.unit, GetLocalPlayer())
loop
exitwhen (this.generatorFx == null)
if (isVisible) then
call BlzSetSpecialEffectX(this.generatorFx, GetUnitX(this.unit))
call BlzSetSpecialEffectY(this.generatorFx, GetUnitY(this.unit))
else
call BlzSetSpecialEffectX(this.generatorFx, WorldBounds.minX)
call BlzSetSpecialEffectY(this.generatorFx, WorldBounds.minY)
endif
call BlzSetSpecialEffectZ(this.generatorFx, this.generatorZ)
exitwhen true
endloop
set subIter = this.spiritList.first
set map = subIter.data
loop
exitwhen (subIter == 0) or (this.spiritList == 0)
loop
exitwhen (map.treeFX == null)
set cz = BlzGetLocalSpecialEffectZ(map.treeFX)
if (isVisible) then
call BlzSetSpecialEffectX(map.treeFX, map.x)
call BlzSetSpecialEffectY(map.treeFX, map.y)
else
call BlzSetSpecialEffectX(map.treeFX, WorldBounds.minX)
call BlzSetSpecialEffectY(map.treeFX, WorldBounds.minY)
endif
call BlzSetSpecialEffectZ(map.treeFX, cz)
exitwhen true
endloop
set subIter = subIter.next
set map = subIter.data
endloop
set this = iter.data
endloop
endmethod
// ======================================================
// ======================================================
// Initializer Function
// ======================================================
private static method onInit takes nothing returns nothing
call UnitAuxHandler.ON_LOAD.register(function thistype.onTentLoad)
call UnitAuxHandler.ON_UNLOAD.register(function thistype.onTentUnload)
call UnitAuxHandler.ON_DEATH.register(function thistype.onTentDeath)
call StructureHandler.ON_START.register(function thistype.onTentStart)
call StructureHandler.ON_ENTER.register(function thistype.onTentEnter)
call DNCycle.ON_DAY.register(function thistype.onTentFXShowHide)
call DNCycle.ON_NIGHT.register(function thistype.onTentFXShowHide)
call OnUnitDeindex(function thistype.onTentRemove)
set generateWispPtr = function thistype.onGenerateWisp
set LumberMissile.forwardResp = EventResponder.create(function thistype.onForwardMissileHit)
set LumberMissile.returnResp = EventResponder.create(function thistype.onReturnMissileHit)
set LumberMissile.monitorResp = EventResponder.create(function thistype.onMissileMonitor)
set destResp = GTimer.register(UPDATE_TICK, function thistype.checkTreeStatus)
set tentResp = GTimer.register(UPDATE_TICK, function thistype.checkTentVisibility)
set destList = IntegerList.create()
set tentList = IntegerList.create()
endmethod
endstruct
endscope
// ======================================================================
// Tent Load Unload Mechanics
// ======================================================================
scope TentLoadUnload
private struct TentLoadUnload extends array
implement TentLoadUnloadConfig
private static EventResponder intervalResp = 0
private static IntegerList objectList = 0
private static IntegerListItem array objectListPtr
private static real ACTUAL_REGEN_HP_AMOUNT = 0.0
private static real ACTUAL_REGEN_MP_AMOUNT = 0.0
private method operator unit takes nothing returns unit
return GetUnitById(this)
endmethod
private static method onTentLoad takes nothing returns nothing
local integer tentID
if (GetUnitAbilityLevel(UnitAuxHandler.curTransport, REGEN_ABIL_ID) == 0) then
return
endif
set tentID = GetUnitId(UnitAuxHandler.curTransport)
if (objectListPtr[tentID] == 0) then
set objectListPtr[tentID] = objectList.push(tentID).last
if (objectList.size() == 1) then
call GTimer[REGEN_TICK].requestCallback(intervalResp)
endif
endif
endmethod
private static method onTentUnload takes nothing returns nothing
local UnitAuxHandler tentID = UnitAuxHandler[UnitAuxHandler.curTransport]
local unit tent
if (objectListPtr[tentID] == 0) then
return
endif
if (tentID.transportCount <= 0) then
call objectList.erase(objectListPtr[tentID])
set objectListPtr[tentID] = 0
if (objectList.empty()) then
call GTimer[REGEN_TICK].releaseCallback(intervalResp)
endif
endif
endmethod
private static method onUnitRecover takes nothing returns nothing
local IntegerListItem iter = objectList.first
local thistype this = iter.data
local integer i = 0
local integer n = 0
local unit temp
if (not DNCycle.isDay) then
return
endif
loop
exitwhen (iter == 0)
set iter = iter.next
set n = UnitAuxHandler(this).transportCount
loop
exitwhen (i >= n)
set temp = BlzGroupUnitAt(UnitAuxHandler(this).transportGroup, i)
set i = i + 1
call SetWidgetLife(temp, GetWidgetLife(temp) + ACTUAL_REGEN_HP_AMOUNT)
call SetUnitState(temp, UNIT_STATE_MANA, GetUnitState(temp, UNIT_STATE_MANA) + ACTUAL_REGEN_MP_AMOUNT)
endloop
set i = 0
set this = iter.data
endloop
set temp = null
endmethod
// ==================================================================
private static method onInit takes nothing returns nothing
set ACTUAL_REGEN_HP_AMOUNT = REGEN_HP_AMOUNT / I2R(REGEN_TICK)
set ACTUAL_REGEN_MP_AMOUNT = REGEN_MP_AMOUNT / I2R(REGEN_TICK)
set intervalResp = GTimer.register(REGEN_TICK, function thistype.onUnitRecover)
call UnitAuxHandler.ON_LOAD.register(function thistype.onTentLoad)
call UnitAuxHandler.ON_UNLOAD.register(function thistype.onTentUnload)
endmethod
endstruct
endscope
// ======================================================================
endscope
scope UpgradeHall
native GetUnitGoldCost takes integer unitid returns integer
native GetUnitWoodCost takes integer unitid returns integer
private module UpgradeHallConfig
static integer array HALL_ID
static integer array ABIL_ID
static integer index = 0
static TableArray idMap = 0
static constant method operator ABILITY_ID takes nothing returns integer
return 'A@01'
endmethod
static constant method operator UNIT_ID takes nothing returns integer
return 'e008'
endmethod
static method operator unitMap takes nothing returns Table
return idMap[1]
endmethod
static method operator abilMap takes nothing returns Table
return idMap[0]
endmethod
private static method onDefineMap takes nothing returns nothing
loop
exitwhen (ABIL_ID[index + 1] == 0)
set index = index + 1
set idMap[0][ABIL_ID[index]] = index
set idMap[1][HALL_ID[index]] = index
endloop
endmethod
private static method onInit takes nothing returns nothing
set idMap = TableArray[2]
set HALL_ID[1] = 'e106'
set HALL_ID[2] = 'e107'
set ABIL_ID[1] = 'A00V'
set ABIL_ID[2] = 'A00W'
call thistype.onDefineMap()
endmethod
endmodule
//! runtextmacro DEFINE_LIST("private", "UnitList", "unit")
private struct UpgradeBuilder extends array
integer data
unit target
method operator unit takes nothing returns unit
return GetUnitById(this)
endmethod
static method operator [] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
private static method clearEntries takes nothing returns nothing
local thistype this = thistype(GetIndexedUnitId())
set this.data = 0
set this.target = null
endmethod
private static method onInit takes nothing returns nothing
call OnUnitDeindex(function thistype.clearEntries)
endmethod
endstruct
private struct UpgradeStructure extends array
UnitListItem listPtr
integer data
unit builder
method operator unit takes nothing returns unit
return GetUnitById(this)
endmethod
static method operator [] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
endstruct
private struct UpgradeHall extends array
implement UpgradeHallConfig
private static Table tempTable = 0
private static group tempGroup = null
private static group hallGroup = null
private integer mode
private UnitList list
private unit sentinel
private static method operator [] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
private method operator unit takes nothing returns unit
return GetUnitById(this)
endmethod
private method addAbil takes nothing returns nothing
local integer i = 0
if (this.mode < 1) or (this.mode > index) then
return
endif
loop
exitwhen (i > index)
if (this.mode != i) then
call UnitAddAbility(this.unit, ABIL_ID[i])
call BlzUnitDisableAbility(this.unit, ABIL_ID[i], true, true)
endif
set i = i + 1
endloop
call UnitAddAbility(this.unit, ABIL_ID[this.mode])
endmethod
private method disableMode takes nothing returns nothing
//call UnitRemoveAbility(this.unit, ABIL_ID[this.mode])
call BlzUnitDisableAbility(this.unit, ABIL_ID[this.mode], true, true)
endmethod
private method activateMode takes nothing returns nothing
// Move the ability up the stack.
call UnitRemoveAbility(this.unit, ABIL_ID[this.mode])
call UnitAddAbility(this.unit, ABIL_ID[this.mode])
endmethod
private method updateMode takes nothing returns nothing
local UnitListItem iter = this.list.first
local integer misIndex = 1
local integer unitType = 0
local unit temp
// Flag entries already occupied
call tempTable.flush()
loop
exitwhen (iter == 0)
set temp = iter.data
set unitType = GetUnitTypeId(temp)
set iter = iter.next
// Inspect unit type
set tempTable.integer[unitMap[unitType]] = tempTable[unitMap[unitType]] + 1
endloop
loop
exitwhen (misIndex > index) or (tempTable.integer[unitMap[HALL_ID[misIndex]]] == 0)
set misIndex = misIndex + 1
endloop
call this.disableMode()
set this.mode = misIndex
call this.activateMode()
endmethod
private static method getNearestHall takes unit wing returns thistype
local integer i = 0
local integer n = BlzGroupGetSize(hallGroup)
local thistype result = 0
local unit temp = null
local real minDist = 9999999999.00
local real distX
local real distY
loop
exitwhen (i >= n)
set temp = BlzGroupUnitAt(hallGroup, i)
loop
exitwhen (GetOwningPlayer(wing) != GetOwningPlayer(temp))
set distX = (GetUnitX(wing)-GetUnitX(temp))
set distY = (GetUnitY(wing)-GetUnitY(temp))
set distX = distX*distX + distY*distY
if (minDist >= distX) then
set result = thistype[temp]
set minDist = distX
endif
exitwhen true
endloop
set i = i + 1
endloop
set temp = null
return result
endmethod
private static method getBuilder takes unit wing returns UpgradeBuilder
local UpgradeBuilder result = 0
local unit temp = null
local real minDist = 9999999999.00
local real distX
local real distY
call GroupEnumUnitsOfPlayer(tempGroup, GetOwningPlayer(wing), null)
loop
set temp = FirstOfGroup(tempGroup)
call GroupRemoveUnit(tempGroup, temp)
exitwhen (temp == null)
loop
exitwhen (temp == wing) or (GetUnitCurrentOrder(temp) != GetUnitTypeId(wing))
set distX = (GetUnitX(wing)-GetUnitX(temp))
set distY = (GetUnitY(wing)-GetUnitY(temp))
set distX = distX*distX + distY*distY
if (minDist >= distX) then
set result = thistype[temp]
set minDist = distX
endif
exitwhen true
endloop
endloop
return result
endmethod
// ============================================================================
// Event Responses
// ============================================================================
// ============================================================================
// Construct Start event
// ============================================================================
private static method onWingStart takes nothing returns nothing
local thistype this
local UpgradeStructure object
local UpgradeBuilder builder
local unit source
// Locate builder unit
set source = thistype.getBuilder(StructureHandler.structure).unit
set builder = UpgradeBuilder[source]
set builder.target = StructureHandler.structure
set this = builder.data
set object = UpgradeStructure[StructureHandler.structure]
set object.listPtr = this.list.push(StructureHandler.structure).last
set object.data = this
set object.builder = source
if (this.list.size() == 1) then
call UnitAddAbility(this.unit, 'Avul')
endif
call UnitPauseTimedLife(source, true)
set source = null
endmethod
// ============================================================================
// Structure enter / construct finish event
// ============================================================================
private static method onHallEnter takes nothing returns nothing
local unit hall
local thistype this
if (GetUnitAbilityLevel(StructureHandler.structure, ABILITY_ID) == 0) then
return
endif
set hall = StructureHandler.structure
set this = thistype[hall]
set this.mode = 1
set this.list = UnitList.create()
set hall = null
call GroupAddUnit(hallGroup, hall)
call this.addAbil()
endmethod
private static method onWingEnter takes nothing returns nothing
local unit wing = StructureHandler.structure
local UpgradeStructure object = UpgradeStructure[wing]
local UpgradeBuilder builder
local thistype this
if (not unitMap.has(GetUnitTypeId(wing))) then
set wing = null
return
endif
if (object.listPtr == 0) then
set this = thistype.getNearestHall(wing)
set object.listPtr = this.list.push(StructureHandler.structure).last
set object.data = this
if (this.list.size() == 1) then
call UnitAddAbility(this.unit, 'Avul')
endif
else
set this = object.data
endif
// Remove the sentinel builder
if (object.builder != null) then
call RemoveUnit(object.builder)
set object.builder = null
endif
// Check which mode to go to next, based on the list of active
// structures.
call this.updateMode()
set wing = null
endmethod
private static method onStructureEnter takes nothing returns nothing
call thistype.onHallEnter()
call thistype.onWingEnter()
endmethod
// ============================================================================
// Structure death event
// ============================================================================
private static method onHallDeath takes nothing returns nothing
local thistype this = thistype[StructureHandler.structure]
local UnitListItem iter = 0
local unit temp
// Any structure with a mode == 0 did not proceed on HallEnter.
if (this.mode == 0) then
return
endif
set this.mode = 0
set iter = this.list.last
loop
exitwhen (iter == 0)
set temp = iter.data
set iter = iter.prev
// Trigger a sub-call here.
call KillUnit(temp)
endloop
call GroupRemoveUnit(hallGroup, this.unit)
endmethod
private static method onWingDeath takes nothing returns nothing
local UpgradeStructure object = UpgradeStructure[StructureHandler.structure]
local UpgradeBuilder builder = 0
local thistype this = 0
if (object.listPtr == 0) then
return
endif
set this = object.data
// Check if there's a builder.
if (object.builder != null) then
set builder = UpgradeBuilder[object.builder]
if (builder.target == StructureHandler.structure) then
call RemoveUnit(builder.unit)
set object.builder = null
endif
endif
set object.data = 0
call this.list.erase(object.listPtr)
if (this.list.empty()) then
if (this.mode == 0) then
call this.list.destroy()
set this.list = 0
else
call this.disableMode()
set this.mode = 1
call this.activateMode()
endif
call UnitRemoveAbility(this.unit, 'Avul')
else
call this.updateMode()
endif
set object.listPtr = 0
endmethod
private static method onStructureDeath takes nothing returns nothing
call thistype.onHallDeath()
call thistype.onWingDeath()
endmethod
// ============================================================================
// Structure removal event.
// ============================================================================
private static method onStructureLeave takes nothing returns nothing
call thistype.onHallDeath()
call thistype.onWingDeath()
endmethod
private static method onHallAttemptConstruct takes nothing returns nothing
local thistype this = thistype[SpellHandler.unit]
local integer index = 0
local unit bob
local player p
local real tx
local real ty
if (not abilMap.has(SpellHandler.current.curAbility)) then
return
endif
set tx = SpellHandler.current.curTargetX
set ty = SpellHandler.current.curTargetY
set p = GetOwningPlayer(SpellHandler.unit)
set index = abilMap[SpellHandler.current.curAbility]
set bob = CreateUnit(GetOwningPlayer(SpellHandler.unit), UNIT_ID, tx, ty, bj_UNIT_FACING)
set UpgradeBuilder[bob].data = this
call SetUnitPropWindow(bob, 0.0)
if (not IssueBuildOrderById(bob, HALL_ID[index], tx, ty)) or /*
*/ (GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) < GetUnitGoldCost(HALL_ID[index])) or /*
*/ (GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER) < GetUnitWoodCost(HALL_ID[index])) then
call PauseUnit(SpellHandler.unit, true)
call IssueImmediateOrderById(SpellHandler.unit, 851972)
call PauseUnit(SpellHandler.unit, false)
call RemoveUnit(bob)
set bob = null
return
endif
call UnitApplyTimedLife(bob, 'BTLF', 5.00)
call PauseUnit(SpellHandler.unit, true)
call IssueImmediateOrderById(SpellHandler.unit, 851972)
call PauseUnit(SpellHandler.unit, false)
call this.disableMode()
set bob = null
endmethod
// ============================================================================
// Initialization
// ============================================================================
private static method onInit takes nothing returns nothing
set tempTable = Table.create()
set tempGroup = CreateGroup()
set hallGroup = CreateGroup()
call StructureHandler.ON_ENTER.register(function thistype.onStructureEnter)
call StructureHandler.ON_DEATH.register(function thistype.onStructureDeath)
call StructureHandler.ON_LEAVE.register(function thistype.onStructureLeave)
call StructureHandler.ON_START.register(function thistype.onWingStart)
call SpellHandler.ON_CHANNEL.register(function thistype.onHallAttemptConstruct)
endmethod
endstruct
endscope