Name | Type | is_array | initial_value |
library dead
//returns true if the unit is alive. requires JassHelper
// to declare the AI native "UnitAlive" as a JASS native, so that
// we can use it in the trigger editor. If you trigger in vJASS,
// you should DEFINITELY use this since it is the only way to be
// sure that a unit is alive.
native UnitAlive takes unit id returns boolean
//returns true if the unit is dead. inline friendly.
function UnitDead takes unit u returns boolean
return not UnitAlive(u)
endfunction
//returns true if the unit exists in the game. inline friendly.
function DoesUnitExist takes unit u returns boolean
return GetUnitTypeId(u) != 0
endfunction
endlibrary
library bonusx initializer init
/* by quackerd
System to set/get bonus attributes of units
Please see below to add additional support for more attributes
*/
private struct BaseArrayX
integer array abi[17]
integer base
integer max_pow
endstruct
globals
BaseArrayX BNX_AGI
BaseArrayX BNX_STR
BaseArrayX BNX_INT
endglobals
private function BaseArrayX_get_val takes unit u, BaseArrayX arr returns integer
local integer val = 0
local integer tmp = 0
local integer i = 0
loop
exitwhen i > arr.max_pow
set tmp = GetUnitAbilityLevel(u, arr.abi[i])
if tmp > 0 then
if (i == arr.max_pow) then
set val = val - R2I(Pow(I2R(arr.base), I2R(i)))
else
set val = val + tmp * R2I(Pow(I2R(arr.base), I2R(i)))
endif
endif
set i = i + 1
endloop
return val
endfunction
private function BaseArrayX_set_val takes unit u, BaseArrayX arr, integer v returns nothing
local integer val = v
local integer mod = 0
local integer i = 0
local integer lvl = 0
if (val < -1 * R2I(Pow(I2R(arr.base), I2R(arr.max_pow)))) or (val > R2I(Pow(I2R(arr.base), I2R(arr.max_pow))) - 1) then
// we still have leftovers
call BJDebugMsg("Bonus: Integer " + I2S(v) + " too large.")
return
endif
// handle minus
if val < 0 then
set val = R2I(Pow(I2R(arr.base), I2R(arr.max_pow))) + val
set lvl = GetUnitAbilityLevel(u, arr.abi[arr.max_pow])
if lvl == 0 then
call UnitAddAbility(u, arr.abi[arr.max_pow])
call UnitMakeAbilityPermanent(u, true, arr.abi[arr.max_pow])
endif
else
call UnitRemoveAbility(u, arr.abi[arr.max_pow])
endif
loop
exitwhen i > arr.max_pow - 1
set mod = ModuloInteger(val, arr.base)
set val = val / arr.base
set lvl = GetUnitAbilityLevel(u, arr.abi[i])
if mod == 0 then
if lvl > 0 then
call UnitRemoveAbility(u, arr.abi[i])
endif
else
set lvl = GetUnitAbilityLevel(u, arr.abi[i])
if lvl == 0 then
call UnitAddAbility(u, arr.abi[i])
call UnitMakeAbilityPermanent(u, true, arr.abi[i])
endif
call SetUnitAbilityLevel(u, arr.abi[i], mod)
endif
set i = i + 1
endloop
endfunction
function BnxSetUnitBonus takes unit u, BaseArrayX arr, integer val returns nothing
call BaseArrayX_set_val(u, arr, val)
endfunction
function BnxGetUnitBonus takes unit u, BaseArrayX arr returns integer
return BaseArrayX_get_val(u, arr)
endfunction
function UtPreloadAbility takes integer abi returns nothing
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'ewsp', 0, 0, 0)
call UnitAddAbility(u, abi)
call UnitRemoveAbility(u, abi)
call RemoveUnit(u)
set u = null
endfunction
private function preload_BaseArrayX takes BaseArrayX arr returns nothing
local integer abi
local integer i = 0
loop
set abi = arr.abi[i]
exitwhen abi == 0 or i > 16
call UtPreloadAbility(abi)
set i = i + 1
endloop
endfunction
//===========================================================================
private function init takes nothing returns nothing
set BNX_STR = BaseArrayX.create()
set BNX_STR.max_pow = 16
set BNX_STR.base = 2
set BNX_STR.abi[0] = 'ABS0'
set BNX_STR.abi[1] = 'ABS1'
set BNX_STR.abi[2] = 'ABS2'
set BNX_STR.abi[3] = 'ABS3'
set BNX_STR.abi[4] = 'ABS4'
set BNX_STR.abi[5] = 'ABS5'
set BNX_STR.abi[6] = 'ABS6'
set BNX_STR.abi[7] = 'ABS7'
set BNX_STR.abi[8] = 'ABS8'
set BNX_STR.abi[9] = 'ABS9'
set BNX_STR.abi[10] = 'ABSA'
set BNX_STR.abi[11] = 'ABSB'
set BNX_STR.abi[12] = 'ABSC'
set BNX_STR.abi[13] = 'ABSD'
set BNX_STR.abi[14] = 'ABSE'
set BNX_STR.abi[15] = 'ABSF'
set BNX_STR.abi[16] = 'ABSm'
call preload_BaseArrayX(BNX_STR)
set BNX_INT = BaseArrayX.create()
set BNX_INT.max_pow = 16
set BNX_INT.base = 2
set BNX_INT.abi[0] = 'ABI0'
set BNX_INT.abi[1] = 'ABI1'
set BNX_INT.abi[2] = 'ABI2'
set BNX_INT.abi[3] = 'ABI3'
set BNX_INT.abi[4] = 'ABI4'
set BNX_INT.abi[5] = 'ABI5'
set BNX_INT.abi[6] = 'ABI6'
set BNX_INT.abi[7] = 'ABI7'
set BNX_INT.abi[8] = 'ABI8'
set BNX_INT.abi[9] = 'ABI9'
set BNX_INT.abi[10] = 'ABIA'
set BNX_INT.abi[11] = 'ABIB'
set BNX_INT.abi[12] = 'ABIC'
set BNX_INT.abi[13] = 'ABID'
set BNX_INT.abi[14] = 'ABIE'
set BNX_INT.abi[15] = 'ABIF'
set BNX_INT.abi[16] = 'ABIm'
call preload_BaseArrayX(BNX_INT)
set BNX_AGI = BaseArrayX.create()
set BNX_AGI.max_pow = 16
set BNX_AGI.base = 2
set BNX_AGI.abi[0] = 'ABA0'
set BNX_AGI.abi[1] = 'ABA1'
set BNX_AGI.abi[2] = 'ABA2'
set BNX_AGI.abi[3] = 'ABA3'
set BNX_AGI.abi[4] = 'ABA4'
set BNX_AGI.abi[5] = 'ABA5'
set BNX_AGI.abi[6] = 'ABA6'
set BNX_AGI.abi[7] = 'ABA7'
set BNX_AGI.abi[8] = 'ABA8'
set BNX_AGI.abi[9] = 'ABA9'
set BNX_AGI.abi[10] = 'ABAA'
set BNX_AGI.abi[11] = 'ABAB'
set BNX_AGI.abi[12] = 'ABAC'
set BNX_AGI.abi[13] = 'ABAD'
set BNX_AGI.abi[14] = 'ABAE'
set BNX_AGI.abi[15] = 'ABAF'
set BNX_AGI.abi[16] = 'ABAm'
call preload_BaseArrayX(BNX_AGI)
endfunction
endlibrary
scope beatupaura initializer init
// this is an example of stacking aura
// notice how our primary logic is in AuraNode instead of AuraBuff
globals
private constant integer SPELL_ID = 'A000'
private constant integer DMG = 10
private Table g_tbl
endglobals
private struct AuraBuff extends AuraSysBuff
private static constant string TARGET_EFFECT = "Abilities\\Spells\\NightElf\\shadowstrike\\shadowstrike.mdl"
private effect e
// called when this buff is applied to an unit the first time
method on_apply takes nothing returns nothing
set e = AddSpecialEffectTarget(TARGET_EFFECT, this.as_target, "overhead")
endmethod
// called when the last AuraSys that inflicts this buff is removed from an unit
method on_remove takes nothing returns nothing
call DestroyEffect(e)
set e = null
endmethod
endstruct
private struct AuraNode extends AuraSysNode
private timer t
static method on_tick takes nothing returns nothing
local thistype this = g_tbl[GetHandleId(GetExpiredTimer())]
call UnitDamageTargetBJ(this.as_sys.as_source, this.as_buff.as_target, 10 * (BlzGroupGetSize(this.as_sys.as_activeg) + BlzGroupGetSize(this.as_sys.as_lingerg)), ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL)
endmethod
// called everytime a new AuraSys is added to the buff
method on_apply takes nothing returns nothing
set t = CreateTimer()
call TimerStart(t, 1, true, function thistype.on_tick)
set g_tbl[GetHandleId(t)] = this
endmethod
// called everytime an AuraSys is removed from the buff
method on_remove takes nothing returns nothing
call g_tbl.remove(GetHandleId(t))
call DestroyTimer(t)
set t = null
endmethod
endstruct
private struct Aura extends AuraSys
implement AuraSysInit
private static constant integer BUFF_SPELL_ID = 'A00P'
private static constant integer BUFF_ID = 'B00P'
private effect e
method get_range takes nothing returns real
return I2R(GetUnitAbilityLevel(this.as_source, SPELL_ID) * 300)
endmethod
method active_cond takes nothing returns boolean
return UnitAlive(this.as_source)
endmethod
method filter takes unit u returns boolean
return IsUnitEnemy(u, GetOwningPlayer(this.as_source)) and UnitAlive(u)
endmethod
method linger_filter takes unit u returns boolean
return UnitAlive(u)
endmethod
method new_buff takes nothing returns AuraSysBuff
return AuraBuff.create()
endmethod
method new_node takes nothing returns AuraSysNode
return AuraNode.create()
endmethod
method get_buff_typeid takes nothing returns integer
return AuraBuff.typeid
endmethod
method on_apply takes nothing returns nothing
set e = AddSpecialEffectTarget("Abilities\\Spells\\Undead\\UnholyAura\\UnholyAura.mdl", this.as_source, "origin")
endmethod
method on_remove takes nothing returns nothing
call DestroyEffect(e)
set e = null
endmethod
endstruct
private function learn_action takes nothing returns nothing
local Aura aura
if (GetLearnedSkill() == SPELL_ID) and GetLearnedSkillLevel() == 1 then
set aura = Aura.new(GetTriggerUnit())
endif
endfunction
//===========================================================================
private function init takes nothing returns nothing
local trigger t
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
call TriggerAddAction(t, function learn_action)
set g_tbl = Table.create()
set t = null
endfunction
endscope
scope strengthaura initializer init
// this is an example of non-stacking aura
globals
private constant integer SPELL_ID = 'A01E'
private constant integer STR_LVL = 1
private Table g_tbl
endglobals
private struct AuraBuff extends AuraSysBuff
private static constant string TARGET_EFFECT = "Abilities\\Spells\\Orc\\Bloodlust\\BloodlustTarget.mdl"
private effect el
private effect er
private timer t
private integer max_lvl
private integer cur_str
// called when this buff is applied to an unit the first time
method on_apply takes nothing returns nothing
set el = AddSpecialEffectTarget(TARGET_EFFECT, this.as_target, "hand left")
set er = AddSpecialEffectTarget(TARGET_EFFECT, this.as_target, "hand right")
set t = CreateTimer()
call TimerStart(t, 1.5, true, function thistype.on_tick)
set g_tbl[GetHandleId(t)] = this
endmethod
// called when the last AuraSys that inflicts this buff is removed from an unit
method on_remove takes nothing returns nothing
call g_tbl.remove(GetHandleId(t))
call PauseTimer(t)
call DestroyTimer(t)
set t = null
call DestroyEffect(el)
set el = null
call DestroyEffect(er)
set el = null
endmethod
static method on_tick takes nothing returns nothing
// periodically update level since the hero stat might change
local thistype this = g_tbl[GetHandleId(GetExpiredTimer())]
call this.update()
endmethod
method update takes nothing returns nothing
call this.update_lvl()
call this.update_str()
endmethod
private method update_str takes nothing returns nothing
local integer next = max_lvl * STR_LVL * GetHeroStr(this.as_target, false)
if next != this.cur_str then
call BnxSetUnitBonus(this.as_target, BNX_STR, BnxGetUnitBonus(this.as_target, BNX_STR) + (next - this.cur_str))
set this.cur_str = next
endif
endmethod
private method update_lvl takes nothing returns nothing
// keep track of the max_lvl
local AuraSysNode head = AuraSys.get_node_head(this)
local AuraSysNode next = head
local integer tmp = 0
local integer tmp2
// enum the highest score
loop
set next = AuraSys.get_next_node(head, next, 0)
exitwhen next == 0
set tmp2 = GetUnitAbilityLevel(next.as_sys.as_source, SPELL_ID)
if tmp < tmp2 then
set tmp = tmp2
endif
endloop
set this.max_lvl = tmp
endmethod
endstruct
private struct AuraNode extends AuraSysNode
// called everytime a new AuraSys is added to the buff
method on_apply takes nothing returns nothing
local AuraBuff buf = this.as_buff
call buf.update()
endmethod
// called everytime an AuraSys is removed from the buff
method on_remove takes nothing returns nothing
local AuraBuff buf = this.as_buff
call buf.update()
endmethod
endstruct
private struct Aura extends AuraSys
implement AuraSysInit
private static constant integer BUFF_SPELL_ID = 'A00W'
private static constant integer BUFF_ID = 'B00W'
private effect e
method get_range takes nothing returns real
return 900.0
endmethod
method active_cond takes nothing returns boolean
return UnitAlive(this.as_source)
endmethod
method filter takes unit u returns boolean
return IsUnitAlly(u, GetOwningPlayer(this.as_source)) and IsUnitType(u, UNIT_TYPE_HERO) and UnitAlive(u)
endmethod
method linger_filter takes unit u returns boolean
return UnitAlive(u)
endmethod
method new_node takes nothing returns AuraSysNode
return AuraNode.create()
endmethod
method new_buff takes nothing returns AuraSysBuff
return AuraBuff.create()
endmethod
method get_buff_typeid takes nothing returns integer
return AuraBuff.typeid
endmethod
method on_apply takes nothing returns nothing
set e = AddSpecialEffectTarget("Abilities\\Spells\\Orc\\Bloodlust\\BloodlustTarget.mdl", this.as_source, "overhead")
endmethod
method on_remove takes nothing returns nothing
call DestroyEffect(e)
set e = null
endmethod
endstruct
private function learn_action takes nothing returns nothing
local Aura aura
if (GetLearnedSkill() == SPELL_ID) and GetLearnedSkillLevel() == 1 then
set aura = Aura.new(GetTriggerUnit())
endif
endfunction
//===========================================================================
private function init takes nothing returns nothing
local trigger t
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
call TriggerAddAction(t, function learn_action)
set g_tbl = Table.create()
set t = null
endfunction
endscope
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
library AuraSys initializer init requires Table
/* v0.2 made by quackerd
INTRO:
A somewhat complicated but very flexible custom aura system that follows vanilla aura's design.
***Impossible to use in GUI***
Supports:
- Stacking & non-stacking auras
- Aura lingering
- Aura enumeration
DESIGN:
AuraSys consists of 3 components:
- struct AuraSys: this is the main object representing an aura
- struct AuraBuff: this is the buff object that AuraSys applies onto each unit
- struct AuraNode: each AuraBuff contains multiple AuraNodes, which keeps track of all AuraSyses that give the buff.
this exists because AuraSyses that give the same buff only apply the buff once
Graphically:
+----------------------------------------------+
| |
v |
+----+----+ |
|AuraSys A| +------+---------+
|Unit 1 +----+ +--------------+ |AuraNode for |
+---------+ | Affects | AuraBuff A | |AuraSys A Unit 1|
+-------->+ Unit 3 +--->-----------------+
| +--------------+ |AuraNode for |
+---------+ | |AuraSys A Unit 2|
|AuraSys A+----+ +------+---------+
|Unit 2 | |
+----+----+ |
^ |
| |
+----------------------------------------------+
Both unit 1 and 2 have the same aura type. They both affect unit 3.
Notice that only one AuraBuff is created but there is an AuraNode for
each aura source.
GETTING STARTED:
1. Create a buff ability inheriting Slow Aura (Tornado) and a buff
Make sure to
- Set the buff of the ability to the corresponding buff
2. Create struct Aura that extends AuraSys
Make sure to
- Implement all stub functions
- Add "implement AuraSysInit" to your struct
- Declare
private static constant integer BUFF_SPELL_ID = Your Slow Aura ID from step 1
private static constant integer BUFF_ID = Your buff ID from step 1
3. Create struct AuraBuff that extends AuraSysBuff
Make sure to
- Implement all stub functions
4. Create struct AuraNode that extends AuraSysNode
Make sure to
- Implement all stub functions
5. Create event - Unit Learns Ability | Action - Add Aura to the unit
API:
I'm only listing public static functions here.
For struct-specific stuff please check out each struct and the demo map. There are detailed documentation and examples there.
To add an aura:
local SomeAura aura = SomeAura.new(source_unit)
To remove an aura:
call aura.delete()
To enum AuraBuffs of a unit:
local AuraSysBuff head = AuraSys.get_buff_head(some_unit)
local AuraSysBuff next = head
loop
// note that the following returns the next buff with buff.getType() == typeid
// setting typeid to 0 returns the next buff of any type
set next = AuraSys.get_next_buff(head, next, typeid)
exitwhen next == 0
// do your stuff
endloop
To enum AuraNodes of an AuraBuff:
local AuraNode head = AuraNode.get_node_head(some_buff)
local AuraNode next = head
loop
// note that the following returns the next node pointing to AuraSys sys
// setting sys to 0 returns the next node of any AuraSys
set next = AuraSys.get_next_node(head, next, sys)
exitwhen next == 0
// do your stuff
endloop
TENTATIVE STUFF (Please LMK):
- Aura Events?
- ...
Credits:
==vJass by Vexorian==
==BuffSystem by Flux== for vJass OOP lessons and some inspirations in aura stacking
==Table by bribe==
*/
globals
/**============ Configurables ============**/
// The interval at which the aura system registers units at. Change with caution as an extremely small value
// will result in reduced performance
private constant real UPDATE_INTERVAL = 0.25
// The aura lingering time (how many UPDATE_INTERVAL)
private constant integer LINGER_UNIT = 12 // 0.25 * 12 = 3 seconds
/**============ End Configurables ============**/
private Table g_tbl
endglobals
private module ListNode
thistype next
thistype prev
method ln_init takes nothing returns nothing
set this.next = this
set this.prev = this
endmethod
method ln_insert takes thistype e returns nothing
set this.next = e.next
set this.prev = e
set e.next.prev = this
set e.next = this
endmethod
method ln_empty takes nothing returns boolean
return (this.next == this)
endmethod
method ln_remove takes nothing returns nothing
set this.prev.next = this.next
set this.next.prev = this.prev
set this.next = this
set this.prev = this
endmethod
endmodule
/**============ struct AuraSysNode ============**/
struct AuraSysNode
// DONT TOUCH
implement ListNode
// The AuraSys this node points to
// Accessible in member methods, readonly
AuraSys as_sys
// The AuraBuff this node belongs to
// Accessible in member methods, readonly
AuraSysBuff as_buff
// on_apply callback. Called when a new node is inserted to the buff
// E.g. A unit gains the aura from a non-existing source
stub method on_apply takes nothing returns nothing
endmethod
// on_remove callback. Called when an existing node is removed from the buff
stub method on_remove takes nothing returns nothing
endmethod
endstruct
/**============End struct AuraSysNode ============**/
/**============ struct AuraSysBuff ============**/
struct AuraSysBuff
// DONT TOUCH
implement ListNode
// DONT TOUCH
AuraSysNode as_head
// The target unit of the buff
// Accessible in member methods, readonly
unit as_target
// on_apply callback. Called when a buff is applied to a unit
// useful for applying special effects
stub method on_apply takes nothing returns nothing
endmethod
// on_remove callback. Called when a buff is removed from a unit
stub method on_remove takes nothing returns nothing
endmethod
endstruct
/**============ End struct AuraSysBuff ============**/
struct AuraSys
// The source of the aura
// Accessible in member methods, readonly
unit as_source
// Group of units that are actively affected
// Accessible in member methods, readonly
group as_activeg
// Group of units that are lingering
// Accessible in member methods, readonly
group as_lingerg
/** start DONT TOUCH **/
private integer as_buff_spell_id
private integer as_buff_id
private group as_tmpg
private group as_enumg
private group as_removeg
private timer as_t
private Table as_linger_tbl
/** end DONT TOUCH **/
// the condition when the aura is considered active
// E.g. to disable aura upon unit death write "return IsUnitAliveBJ(this.as_source)"
stub method active_cond takes nothing returns boolean
return false
endmethod
// the filter for affected units. Returning true means can affect.
stub method filter takes unit u returns boolean
return false
endmethod
// the current range of the aura.
stub method get_range takes nothing returns real
return 0.0
endmethod
// the filter for lingering units. Returning true means can linger.
// E.g. to immediately remove buffs when an unit dies write "return IsUnitAliveBJ(this.as_source)"
stub method linger_filter takes unit u returns boolean
return IsUnitAliveBJ(u)
endmethod
// returns an instance of your custom node struct that extends AuraSysNode
stub method new_node takes nothing returns AuraSysNode
return 0
endmethod
// returns an instance of your custom buff struct that extends AuraSysBuff
stub method new_buff takes nothing returns AuraSysBuff
return 0
endmethod
// returns the typeid of your custom buff struct
stub method get_buff_typeid takes nothing returns integer
return 0
endmethod
// called when the aura is added to the source
stub method on_apply takes nothing returns nothing
endmethod
// called when the aura is removed from the source
stub method on_remove takes nothing returns nothing
endmethod
static method get_node_head takes AuraSysBuff buf returns AuraSysNode
return buf.as_head
endmethod
static method get_next_node takes AuraSysNode head, AuraSysNode cur, AuraSys sys returns AuraSysNode
set cur = cur.next
loop
exitwhen cur == head or sys == 0 or cur.as_sys == sys
set cur = cur.next
endloop
if cur == head then
set cur = 0
endif
return cur
endmethod
static method get_buff_head takes unit u returns AuraSysBuff
if not g_tbl.has(GetHandleId(u)) then
debug call BJDebugMsg("NOT HAVE")
return 0
endif
return g_tbl[GetHandleId(u)]
endmethod
static method get_next_buff takes AuraSysBuff head, AuraSysBuff cur, integer typeid returns AuraSysBuff
set cur = cur.next
loop
exitwhen cur == head or typeid == 0 or cur.getType() == typeid
set cur = cur.next
endloop
if cur == head then
set cur = 0
endif
return cur
endmethod
private method apply_buff takes unit u returns nothing
local AuraSysBuff buf_head = g_tbl[GetHandleId(u)]
local AuraSysBuff buf_first
local AuraSysNode node_new
local boolean newbuf = false
// ensure buf_head exists
if buf_head == 0 then
set buf_head = AuraSysBuff.create()
call buf_head.ln_init()
set g_tbl[GetHandleId(u)] = buf_head
debug call BJDebugMsg("unit " + I2S(GetHandleId(u)) + " buffhead insert " + I2S(buf_head))
endif
set buf_first = thistype.get_next_buff(buf_head, buf_head, this.get_buff_typeid())
if buf_first == 0 then
// add AuraSysBuff
call UnitAddAbility(u, this.as_buff_spell_id)
call UnitMakeAbilityPermanent(u, true, this.as_buff_spell_id)
set buf_first = this.new_buff()
call buf_first.ln_init()
set buf_first.as_target = u
set buf_first.as_head = AuraSysNode.create()
call buf_first.as_head.ln_init()
call buf_first.ln_insert(buf_head)
set newbuf = true
debug call BJDebugMsg("unit " + I2S(GetHandleId(u)) + " buff add " + I2S(buf_first))
endif
set node_new = this.new_node()
set node_new.as_sys = this
set node_new.as_buff = buf_first
call node_new.ln_init()
call node_new.ln_insert(buf_first.as_head)
debug call BJDebugMsg("unit " + I2S(GetHandleId(u)) + " node add " + I2S(node_new))
if newbuf then
call buf_first.on_apply()
endif
call node_new.on_apply()
endmethod
private method remove_buff takes unit u returns nothing
local AuraSysBuff buf_head = g_tbl[GetHandleId(u)]
local AuraSysBuff buf_first
local AuraSysNode node_first
if buf_head == 0 then
debug call BJDebugMsg("Removing from an empty head")
return
endif
set buf_first = thistype.get_next_buff(buf_head, buf_head, this.get_buff_typeid())
if buf_first == 0 then
debug call BJDebugMsg("Cannot find buff for removal")
return
else
// locate the node
set node_first = thistype.get_next_node(buf_first.as_head, buf_first.as_head, this)
if node_first == 0 then
debug call BJDebugMsg("Cannot find node for removal")
return
endif
// remove from buf
call node_first.ln_remove()
call node_first.on_remove()
call node_first.destroy()
debug call BJDebugMsg("unit " + I2S(GetHandleId(u)) + " node remove " + I2S(node_first))
// check for empty as_head
if buf_first.as_head.ln_empty() then
// remove buff
call UnitRemoveAbility(u, this.as_buff_spell_id)
call UnitRemoveAbility(u, this.as_buff_id)
call buf_first.ln_remove()
call buf_first.on_remove()
// cleanup
set buf_first.as_target = null
call buf_first.as_head.destroy()
call buf_first.destroy()
debug call BJDebugMsg("unit " + I2S(GetHandleId(u)) + " buff remove " + I2S(buf_first))
endif
// check for empty global buff
if buf_head.ln_empty() then
debug call BJDebugMsg("unit " + I2S(GetHandleId(u)) + " buffhead remove " + I2S(buf_head))
call buf_head.destroy()
call g_tbl.remove(GetHandleId(u))
endif
endif
endmethod
private method on_tick takes nothing returns nothing
local unit each
local integer i
local group g
call GroupClear(this.as_tmpg)
call GroupClear(this.as_removeg)
// process lingering stuff
loop
set each = FirstOfGroup(this.as_lingerg)
exitwhen each == null
set i = as_linger_tbl[GetHandleId(each)]
set i = i - 1
debug call BJDebugMsg("UNIT " + I2S(GetHandleId(each)) + " LINGER - " + I2S(i))
if i > 0 and this.linger_filter(each) then
set this.as_linger_tbl[GetHandleId(each)] = i
call GroupAddUnit(this.as_tmpg, each)
else
call this.as_linger_tbl.remove(GetHandleId(each))
call GroupAddUnit(this.as_removeg, each)
endif
call GroupRemoveUnit(this.as_lingerg, each)
endloop
set g = this.as_tmpg
set this.as_tmpg = this.as_lingerg
set this.as_lingerg = g
// as_tmpg == empty
if this.active_cond() then
call GroupClear(this.as_enumg)
call GroupEnumUnitsInRange(this.as_enumg, GetUnitX(this.as_source), GetUnitY(this.as_source), this.get_range(), null)
// delete aura from old units
loop
set each = FirstOfGroup(this.as_activeg)
exitwhen each == null
// remove units that do not meet the requirement
if ((not this.filter(each)) or (not IsUnitInGroup(each, this.as_enumg))) then
// let it linger
if this.linger_filter(each) then
call GroupAddUnit(this.as_lingerg, each)
set this.as_linger_tbl[GetHandleId(each)] = LINGER_UNIT
else
// immediately remove the buff
call GroupAddUnit(this.as_removeg, each)
endif
else
call GroupAddUnit(this.as_tmpg, each)
endif
call GroupRemoveUnit(this.as_activeg, each)
endloop
set g = this.as_activeg
set this.as_activeg = this.as_tmpg
set this.as_tmpg = g
// add aura to new units
loop
set each = FirstOfGroup(this.as_enumg)
exitwhen each == null
if this.filter(each) and not IsUnitInGroup(each, this.as_activeg) then
call GroupAddUnit(this.as_activeg, each)
if IsUnitInGroup(each, this.as_lingerg) then
// lingering units that regained the aura
call this.as_linger_tbl.remove(GetHandleId(each))
call GroupRemoveUnit(this.as_lingerg, each)
else
// new unit
call GroupAddUnit(this.as_tmpg, each)
endif
endif
call GroupRemoveUnit(this.as_enumg, each)
endloop
else
// add all active units to lingering
loop
set each = FirstOfGroup(this.as_activeg)
exitwhen each == null
if this.linger_filter(each) then
call GroupAddUnit(this.as_lingerg, each)
set this.as_linger_tbl[GetHandleId(each)] = LINGER_UNIT
else
call GroupAddUnit(this.as_removeg, each)
endif
call GroupRemoveUnit(this.as_activeg, each)
endloop
endif
// now that as_tmpg contains units need buff apply
// as_removeg contains units need buff removal
// as_activeg and as_lingerg is stable for the current epoch
loop
set each = FirstOfGroup(this.as_removeg)
exitwhen each == null
call this.remove_buff(each)
call GroupRemoveUnit(this.as_removeg, each)
endloop
loop
set each = FirstOfGroup(this.as_tmpg)
exitwhen each == null
call this.apply_buff(each)
call GroupRemoveUnit(this.as_tmpg, each)
endloop
set each = null
set g = null
endmethod
private static method timer_action takes nothing returns nothing
local thistype this = g_tbl[GetHandleId(GetExpiredTimer())]
// possibly delete fires in the same frame as timer
if this != 0 then
call this.on_tick()
endif
endmethod
method init takes unit u, integer buffid, integer buffspellid returns nothing
set this.as_source = u
set this.as_removeg = CreateGroup()
set this.as_activeg = CreateGroup()
set this.as_lingerg = CreateGroup()
set this.as_buff_id = buffid
set this.as_buff_spell_id = buffspellid
set this.as_enumg = CreateGroup()
set this.as_linger_tbl = Table.create()
set this.as_tmpg = CreateGroup()
set this.as_t = CreateTimer()
set g_tbl[GetHandleId(this.as_t)] = this
call TimerStart(this.as_t, UPDATE_INTERVAL ,true , function thistype.timer_action)
call this.on_apply()
call this.on_tick()
endmethod
method delete takes nothing returns nothing
local unit each
// delete everything active/linger
call GroupClear(this.as_tmpg)
call BlzGroupAddGroupFast(this.as_tmpg, this.as_activeg)
call BlzGroupAddGroupFast(this.as_tmpg, this.as_lingerg)
call GroupClear(this.as_activeg)
call GroupClear(this.as_lingerg)
// now activeg and lingerg are stable, call remove_buff
loop
set each = FirstOfGroup(this.as_tmpg)
exitwhen each == null
call this.remove_buff(each)
call GroupRemoveUnit(this.as_tmpg, each)
endloop
call this.on_remove()
// destroy the timer
call g_tbl.remove(GetHandleId(this.as_t))
call DestroyTimer(this.as_t)
set this.as_t = null
set this.as_source = null
call DestroyGroup(this.as_activeg)
set this.as_activeg = null
call DestroyGroup(this.as_tmpg)
set this.as_tmpg = null
call DestroyGroup(this.as_removeg)
set this.as_removeg = null
call DestroyGroup(this.as_lingerg)
set this.as_lingerg = null
call DestroyGroup(this.as_enumg)
set this.as_enumg = null
call this.as_linger_tbl.destroy()
set each = null
endmethod
endstruct
module AuraSysInit
static method new takes unit u returns AuraSys
local thistype ret = thistype.create()
call ret.init(u, BUFF_ID, BUFF_SPELL_ID)
return ret
endmethod
endmodule
private function init takes nothing returns nothing
set g_tbl = Table.create()
endfunction
endlibrary