Name | Type | is_array | initial_value |
positions | location | Yes |
/*****************************************************************************
*
* ItemRestriction v1.1.2.1
* by Bannar
*
* For restricting or limiting items from being equipped.
*
******************************************************************************
*
* Requirements:
*
* ListT by Bannar
* hiveworkshop.com/threads/containers-list-t.249011/
*
* RegisterPlayerUnitEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
* UnitDex by TriggerHappy
* hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
*
*
* Optional requirement:
*
* SimError by Vexorian
* wc3c.net/showthread.php?t=101260&highlight=SimError
*
******************************************************************************
*
* Configurable error messages:
*
* function GetUnitTypeErrorMessage takes UnitRequirement requirement, integer unitId returns string
* function GetLevelErrorMessage takes UnitRequirement requirement, integer level returns string
* function GetStatisticErrorMessage takes UnitRequirement requirement, integer value, string statistic returns string
* function GetLimitErrorMessage takes ItemRestriction restriction, integer limit returns string
* function GetExclusiveErrorMessage takes ItemRestriction first, ItemRestriction second returns string
* function GetForbiddenErrorMessage takes ItemRestriction restriction returns string
* function PrintErrorMessage takes player whichPlayer, string message returns nothing
*
******************************************************************************
*
* Interface UnitRequirementPredicate:
*
* static method isMet takes unit whichUnit returns string
* Returns null on success or error message if unit does not meet predicate criteria.
*
* module UnitRequirementPredicateModule
* Declares body for new predicate type.
*
*
* Predicate implementation example:
*
* | struct MyPredicate extends array
* | static method isMet takes unit whichUnit returns string
* | return "This unit does not meet requirement criteria"
* | endmethod
* |
* | implement UnitRequirementPredicateModule
* | endstruct
*
******************************************************************************
*
* struct UnitRequirement:
*
* Fields:
*
* | string name
* | Name associated with requirement.
* |
* | integer level
* | Unit level requirement.
* |
* | integer strength
* | Hero strength requirement.
* |
* | integer agility
* | Hero agility requirement.
* |
* | integer intelligence
* | Hero intelligence requirement.
* |
* | boolean includeBonuses
* | Whether to include bonuses when checking unit staticstics.
*
*
* General:
*
* | static method create takes string name returns thistype
* | Default ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
*
*
* Methods:
*
* | method getUnits takes nothing returns IntegerList
* | Returns unit type requirement list.
* |
* | method has takes integer unitTypeId returns boolean
* | Whether specified unit type is a part of requirement.
* |
* | method addUnit takes integer unitTypeId returns thistype
* | Adds specified unit type to requirement criterias.
* |
* | method removeUnit takes integer unitTypeId returns thistype
* | Removes specified unit type from requirement criterias.
* |
* | method requireStat takes integer str, integer agi, integer int returns thistype
* | Sets hero statistic requirements to specified values.
* |
* | method addCondition takes UnitRequirementPredicate predicate returns thistype
* | Adds new criteria to requirement criterias.
* |
* | method removeCondition takes UnitRequirementPredicate predicate returns thistype
* | Removes specified condition from requirement criterias.
* |
* | method test takes unit whichUnit returns string
* | Validates whether specified unit meets this unit requirements.
* |
* | method filter takes unit whichUnit returns boolean
* | Returns value indicating whether specified unit successfully passed requirement test.
*
*
* struct ItemRestriction:
*
* Fields:
*
* | string name
* | Name associated with restriction.
* |
* | integer limit
* | Maximum number of items a unit can carry.
* |
* | UnitRequirement requirement
* | Requirement a unit must meet to hold items.
*
*
* General:
*
* | static method create takes string name, integer limit returns thistype
* | Default ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
*
*
* Methods:
*
* | method getItems takes nothing returns IntegerList
* | Item types that enforce this restriction.
* |
* | method has takes integer itemTypeId returns boolean
* | Whether specified item type is a part of restriction.
* |
* | method removeItem takes integer itemTypeId returns thistype
* | Remove specified item type from this restriction.
* |
* | method addItem takes integer itemTypeId returns thistype
* | Add specified item type to this restriction.
* |
* | method getExceptions takes nothing returns LimitExceptionList
* | Returns collection of UnitRequirement instances that may define different limits.
* | Example: berserker may carry two 2H-weapons, rather than one.
* |
* | method removeException takes UnitRequirement requirement returns thistype
* | Removes item limit exception for specified requirement.
* |
* | method addException takes UnitRequirement requirement, integer newLimit returns thistype
* | Adds new item limit exception for specified requirement.
* |
* | method getExclusives takes nothing returns ItemRestrictionList
* | Returns collection of ItemRestriction instances that exclude each other from being picked.
* | Example: a unit cannot carry both 1H-weapons and 2H-weapons at the same time.
* |
* | method removeExclusive takes ItemRestriction restriction returns thistype
* | Makes specified restriction non-exclusive with this restriction.
* |
* | method addExclusive takes ItemRestriction restriction returns thistype
* | Makes specified restriction exclusive with this restriction.
* |
* | method getCount takes unit whichUnit returns integer
* | Returns related to this restriction, current item count for specified unit.
* |
* | method getException takes unit whichUnit returns LimitException
* | Returns currently chosen limit exception if any for specified unit.
* |
* | method test takes unit whichUnit, item whichItem returns string
* | Validates whether specified unit can hold specified itm given the restriction criteria.
* |
* | method filter takes unit whichUnit, item whichItem returns boolean
* | Returns value indicating whether specified unit successfully
* | passed restriction test for specified item.
*
*
*****************************************************************************/
library ItemRestriction requires /*
*/ ListT /*
*/ RegisterPlayerUnitEvent /*
*/ UnitDex /*
*/ optional SimError
private function GetUnitTypeErrorMessage takes UnitRequirement requirement, integer unitId returns string
return "This item can not be hold by this unit type."
endfunction
private function GetLevelErrorMessage takes UnitRequirement requirement, integer level returns string
return "This item requires level " + I2S(level) + " to be picked up."
endfunction
private function GetStatisticErrorMessage takes UnitRequirement requirement, integer value, string statistic returns string
return "This item requires " + I2S(value) + " " + statistic + "."
endfunction
private function GetLimitErrorMessage takes ItemRestriction restriction, integer limit returns string
return "This unit can not hold more than " + I2S(limit) + " item(s) of \"" + restriction.name + "\" type."
endfunction
private function GetExclusiveErrorMessage takes ItemRestriction first, ItemRestriction second returns string
return "This unit cannot hold items of type \"" + first.name + "\" and \"" + second.name + "\" at the same time."
endfunction
private function GetForbiddenErrorMessage takes ItemRestriction restriction returns string
return "This item can not be picked up by this unit."
endfunction
private function PrintErrorMessage takes player whichPlayer, string message returns nothing
static if LIBRARY_SimError then
call SimError(whichPlayer, message)
else
call DisplayTimedTextToPlayer(whichPlayer, 0, 0, 2.0, message)
endif
endfunction
globals
private trigger array triggers
private unit argUnit = null
private string retMessage = null
endglobals
struct UnitRequirementPredicate extends array
implement Alloc
static method isMet takes unit whichUnit returns string
return null
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set triggers[this] = CreateTrigger()
return this
endmethod
method destroy takes nothing returns nothing
call DestroyTrigger(triggers[this])
set triggers[this] = null
call deallocate()
endmethod
endstruct
module UnitRequirementPredicateModule
private delegate UnitRequirementPredicate predicate
private static method onInvoke takes nothing returns boolean
set retMessage = thistype.isMet(argUnit)
return retMessage == null
endmethod
static method create takes nothing returns thistype
local thistype this = UnitRequirementPredicate.create()
set predicate = this
call TriggerAddCondition(triggers[this], Condition(function thistype.onInvoke))
return this
endmethod
method destroy takes nothing returns nothing
call predicate.destroy()
endmethod
endmodule
struct UnitRequirement extends array
string name
integer level
integer strength
integer agility
integer intelligence
boolean includeBonuses
private IntegerList units
private IntegerList conditions
implement Alloc
static method create takes string name returns thistype
local thistype this = allocate()
set units = IntegerList.create()
set level = 0
set strength = 0
set agility = 0
set intelligence = 0
set includeBonuses = false
set this.name = name
set conditions = IntegerList.create()
return this
endmethod
method destroy takes nothing returns nothing
set name = null
call units.destroy()
call conditions.destroy()
call deallocate()
endmethod
method has takes integer unitTypeId returns boolean
return units.find(unitTypeId) != 0
endmethod
method requireStat takes integer str, integer agi, integer intel returns thistype
set strength = str
set agility = agi
set intelligence = intel
return this
endmethod
method getUnits takes nothing returns IntegerList
return IntegerList[units]
endmethod
method addUnit takes integer unitTypeId returns thistype
local IntegerListItem node = units.find(unitTypeId)
if unitTypeId > 0 and node == 0 then
call units.push(unitTypeId)
endif
return this
endmethod
method removeUnit takes integer unitTypeId returns thistype
local IntegerListItem node = units.find(unitTypeId)
if node != 0 then
call units.erase(node)
endif
return this
endmethod
method addCondition takes UnitRequirementPredicate predicate returns thistype
local IntegerListItem node = conditions.find(predicate)
if predicate != 0 and node == 0 then
call conditions.push(predicate)
endif
return this
endmethod
method removeCondition takes UnitRequirementPredicate predicate returns thistype
local IntegerListItem node = conditions.find(predicate)
if node != 0 then
call conditions.erase(node)
endif
return this
endmethod
method test takes unit whichUnit returns string
local integer unitTypeId = GetUnitTypeId(whichUnit)
local IntegerListItem iter
local UnitRequirementPredicate condition
if not units.empty() and not has(unitTypeId) then
return GetUnitTypeErrorMessage(this, unitTypeId)
elseif level > 0 and GetHeroLevel(whichUnit) < level then
return GetLevelErrorMessage(this, level)
elseif strength > 0 and GetHeroStr(whichUnit, includeBonuses) < strength then
return GetStatisticErrorMessage(this, strength, "Strength")
elseif agility > 0 and GetHeroAgi(whichUnit, includeBonuses) < agility then
return GetStatisticErrorMessage(this, agility, "Agility")
elseif intelligence > 0 and GetHeroInt(whichUnit, includeBonuses) < intelligence then
return GetStatisticErrorMessage(this, intelligence, "Intelligence")
endif
set argUnit = whichUnit
set iter = conditions.first
loop
exitwhen iter == 0
set condition = iter.data
if not TriggerEvaluate(triggers[condition]) then
return retMessage
endif
set iter = iter.next
endloop
return null
endmethod
method filter takes unit whichUnit returns boolean
return test(whichUnit) == null
endmethod
endstruct
struct LimitException extends array
UnitRequirement requirement
integer newLimit
implement Alloc
static method create takes UnitRequirement requirement, integer newLimit returns thistype
local thistype this = allocate()
set this.requirement = requirement
set this.newLimit = newLimit
return this
endmethod
method destroy takes nothing returns nothing
call deallocate()
endmethod
endstruct
//! runtextmacro DEFINE_STRUCT_LIST("", "LimitExceptionList", "LimitException")
//! runtextmacro DEFINE_STRUCT_LIST("", "ItemRestrictionList", "ItemRestriction")
private module ItemRestrictionInit
private static method onInit takes nothing returns nothing
set instanceTable = Table.create()
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.onPickup)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onTargetOrder)
call RegisterUnitIndexEvent(Condition(function thistype.onDeindex), EVENT_UNIT_DEINDEX)
endmethod
endmodule
struct ItemRestriction extends array
string name
integer limit
UnitRequirement requirement
private Table table
private IntegerList items
private LimitExceptionList exceptions
private ItemRestrictionList exclusives
// For extra speed, each item type involved will have separate list assigned
private static Table instanceTable
implement Alloc
private static method saveRestriction takes integer index, ItemRestriction restriction returns nothing
local ItemRestrictionList restrictions
if not instanceTable.has(index) then
set instanceTable[index] = ItemRestrictionList.create()
endif
set restrictions = instanceTable[index]
if restrictions.find(restriction) == 0 then
call restrictions.push(restriction)
endif
endmethod
private static method flushRestriction takes integer index, ItemRestriction restriction returns nothing
local ItemRestrictionList restrictions = instanceTable[index]
call restrictions.erase(restrictions.find(restriction))
if restrictions.empty() then
call restrictions.destroy()
call instanceTable.remove(index)
endif
endmethod
static method getRestrictions takes integer index returns ItemRestrictionList
if instanceTable.has(index) then
return instanceTable[index]
endif
return 0
endmethod
static method create takes string name, integer limit returns thistype
local thistype this = allocate()
set table = Table.create()
set items = IntegerList.create()
set exceptions = LimitExceptionList.create()
set exclusives = ItemRestrictionList.create()
set this.name = name
set this.limit = limit
set this.requirement = 0
// Global instance list for handling deindex event
call saveRestriction(0, this)
return this
endmethod
method destroy takes nothing returns nothing
local ItemRestrictionList restrictions
local ItemRestrictionListItem iter
local ItemRestrictionListItem node
local ItemRestriction exclusive
call flushRestriction(0, this)
set iter = items.first
loop
exitwhen iter == 0
call flushRestriction(iter.data, this)
set iter = iter.next
endloop
if not exclusives.empty() then
set iter = exclusives.first
loop
exitwhen iter == 0
set exclusive = iter.data
set node = exclusive.exclusives.find(this)
call exclusive.exclusives.erase(node)
set iter = iter.next
endloop
endif
call table.destroy()
call items.destroy()
call exceptions.destroy()
call exclusives.destroy()
set name = null
call deallocate()
endmethod
method getItems takes nothing returns IntegerList
return IntegerList[items]
endmethod
method has takes integer itemTypeId returns boolean
return items.find(itemTypeId) != 0
endmethod
method removeItem takes integer itemTypeId returns thistype
if has(itemTypeId) then
call items.erase(items.find(itemTypeId))
call flushRestriction(itemTypeId, this)
endif
return this
endmethod
method addItem takes integer itemTypeId returns thistype
if itemTypeId > 0 and not has(itemTypeId) then
call items.push(itemTypeId)
call saveRestriction(itemTypeId, this)
endif
return this
endmethod
method getExceptions takes nothing returns LimitExceptionList
return LimitExceptionList[exceptions]
endmethod
method removeException takes UnitRequirement requirement returns thistype
local LimitExceptionListItem iter = exceptions.first
local LimitException exception
loop
exitwhen iter == 0
set exception = iter.data
if exception.requirement == requirement then
call exceptions.erase(iter)
call exception.destroy()
exitwhen true
endif
set iter = iter.next
endloop
return this
endmethod
method addException takes UnitRequirement requirement, integer newLimit returns thistype
local LimitExceptionListItem iter = exceptions.first
loop
exitwhen iter == 0
if iter.data.requirement == requirement then
return this
endif
set iter = iter.next
endloop
call exceptions.push(LimitException.create(requirement, newLimit))
return this
endmethod
method getExclusives takes nothing returns ItemRestrictionList
return ItemRestrictionList[exclusives]
endmethod
method removeExclusive takes ItemRestriction restriction returns thistype
local ItemRestrictionListItem node = exclusives.find(restriction)
if node != 0 then
call exclusives.erase(node)
set node = restriction.exclusives.find(this)
call restriction.exclusives.erase(node)
endif
return this
endmethod
method addExclusive takes ItemRestriction restriction returns thistype
if restriction != 0 and exclusives.find(restriction) == 0 then
call exclusives.push(restriction)
call restriction.exclusives.push(this)
endif
return this
endmethod
private method geTcount takes integer index returns integer
return table[index]
endmethod
private method incCount takes integer index returns nothing
set table[index] = geTcount(index) + 1
endmethod
private method decCount takes integer index returns nothing
set table[index] = geTcount(index) - 1
endmethod
private method geTexception takes integer index returns LimitException
return table[-index]
endmethod
private method setException takes integer index, LimitException exception returns nothing
set table[-index] = exception
endmethod
method getCount takes unit whichUnit returns integer
return geTcount(GetUnitId(whichUnit))
endmethod
method getException takes unit whichUnit returns LimitException
return geTexception(GetUnitId(whichUnit))
endmethod
method test takes unit whichUnit, item whichItem returns string
local string errorMessage
local IntegerListItem iter
local ItemRestriction exclusive
local integer index = GetUnitId(whichUnit)
local integer threshold = limit
local LimitException exception
if not has(GetItemTypeId(whichItem)) then
return null
elseif requirement != 0 then
set errorMessage = requirement.test(whichUnit)
if errorMessage != null then
return errorMessage
endif
endif
set iter = exclusives.first
loop
exitwhen iter == 0
set exclusive = iter.data
if exclusive.geTcount(index) > 0 then
return GetExclusiveErrorMessage(this, exclusive)
endif
set iter = iter.next
endloop
if not exceptions.empty() then
set exception = geTexception(index)
if exception == 0 or exceptions.find(exception) == 0 then
call table.remove(-index) // clear assigned exception if any
set iter = exceptions.first
loop
exitwhen iter == 0
set exception = iter.data
if exception.requirement.filter(whichUnit) then
set threshold = exception.newLimit
call setException(index, exception)
exitwhen true
endif
set iter = iter.next
endloop
else
set threshold = exception.newLimit
endif
endif
if threshold <= 0 then
return GetForbiddenErrorMessage(this)
elseif geTcount(index) >= threshold then
return GetLimitErrorMessage(this, threshold)
endif
return null
endmethod
method filter takes unit whichUnit, item whichItem returns boolean
return test(whichUnit, whichItem) == null
endmethod
// Returns null (not allowed), empty list (no restrictions) or
// non-empty list (restrictions to increase count for).
// Caller is responsible for destroying retrieved list if any
private static method evaluateRestrictions takes unit u, item itm returns ItemRestrictionList
local ItemRestrictionList result = ItemRestrictionList.create()
local ItemRestrictionList restrictions = getRestrictions(GetItemTypeId(itm))
local ItemRestrictionListItem iter
local ItemRestriction restriction
local string errorMessage
if restrictions != 0 then
set iter = restrictions.first
loop
exitwhen iter == 0
set restriction = iter.data
set errorMessage = restriction.test(u, itm)
if errorMessage != null then
call PrintErrorMessage(GetOwningPlayer(u), errorMessage)
call result.destroy()
set result = 0
exitwhen true
endif
call result.push(restriction)
set iter = iter.next
endloop
endif
return result
endmethod
private static method onPickup takes nothing returns nothing
local item itm = GetManipulatedItem()
local unit u
local integer index
local ItemRestrictionList associated
local ItemRestrictionListItem iter
local trigger t
if not IsItemPowerup(itm) then
set u = GetTriggerUnit()
set associated = evaluateRestrictions(u, itm)
if associated != 0 then
set index = GetUnitId(u)
set iter = associated.first
loop
exitwhen iter == 0
call iter.data.incCount(index)
set iter = iter.next
endloop
call associated.destroy()
else
set t = GetAnyPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_DROP_ITEM)
call DisableTrigger(t)
call UnitRemoveItem(u, itm)
call EnableTrigger(t)
set t = null
endif
set u = null
endif
set itm = null
endmethod
private static method onDrop takes nothing returns nothing
local ItemRestrictionList restrictions = getRestrictions(GetItemTypeId(GetManipulatedItem()))
local ItemRestrictionListItem iter
local integer index
local integer count
local ItemRestriction restriction
if restrictions != 0 then
set index = GetUnitId(GetTriggerUnit())
set iter = restrictions.first
loop
exitwhen iter == 0
set restriction = iter.data
set count = restriction.geTcount(index)
if count > 0 then
call restriction.decCount(index)
endif
set iter = iter.next
endloop
endif
endmethod
private static method onTargetOrder takes nothing returns nothing
local item itm = GetOrderTargetItem()
local unit u
local ItemRestrictionList associated
if GetIssuedOrderId() == 851971 and itm != null then // order smart
set u = GetTriggerUnit()
set associated = evaluateRestrictions(u, itm)
if associated == 0 then
if not IsUnitPaused(u) then
call PauseUnit(u, true)
call IssueImmediateOrderById(u, 851972) // order stop
call PauseUnit(u, false)
endif
else
call associated.destroy()
endif
set u = null
endif
set itm = null
endmethod
private static method onDeindex takes nothing returns nothing
local integer index = GetIndexedUnitId()
local ItemRestrictionList restrictions = getRestrictions(0)
local ItemRestrictionListItem iter
local ItemRestriction restriction
if restrictions != 0 then
set iter = restrictions.first
loop
exitwhen iter == 0
set restriction = iter.data
if restriction.table.has(index) then
call restriction.table.flush()
endif
set iter = iter.next
endloop
endif
endmethod
implement ItemRestrictionInit
endstruct
endlibrary
scope ItemRestrictionDemo initializer Init
native UnitAlive takes unit whichUnit returns boolean
struct AtLeastOneSpiderAlive extends array
static method filter takes nothing returns boolean
return GetUnitTypeId(GetFilterUnit()) == 'nssp' and UnitAlive(GetFilterUnit()) // Spitting Spider
endmethod
static method isMet takes unit whichUnit returns string
local group g = CreateGroup()
local string result = null
local item itm = GetManipulatedItem()
set itm = GetOrderTargetItem()
call DisplayTimedTextToPlayer(Player(0), 0, 0, 5, "isMet: "+GetItemName(itm))
call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, Filter(function thistype.filter))
if FirstOfGroup(g) == null then
set result = "There are no spider alive on the map. Spare a few."
endif
call DestroyGroup(g)
set g = null
return result
endmethod
implement UnitRequirementPredicateModule
endstruct
globals
string description = "Heroes are limited to two 1H weapons.\n" + /*
*/ "Heroes are limited to one 2H weapon.\n" + /*
*/ "Tauren Chieftain is an exception, he can hold up to two 2H weapons.\n" + /*
*/ "1H weapons and 2H weapons exclude ech other.\n" + /*
*/ "Spider Ring can only be picked up if at least one spider is alive on the map."
endglobals
private function Init takes nothing returns nothing
local ItemRestriction weapon1h = ItemRestriction.create("1H-weapon", 2)
local ItemRestriction weapon2h = ItemRestriction.create("2H-weapon", 1)
local UnitRequirement tauren = UnitRequirement.create("tauren")
local ItemRestriction spiderRing = ItemRestriction.create("Friend of the spiders", 1)
local UnitRequirement atLeastOneSpiderAlive = UnitRequirement.create("Spare the spiders")
call spiderRing.addItem('sprn')
call atLeastOneSpiderAlive.addCondition(AtLeastOneSpiderAlive.create())
set spiderRing.requirement = atLeastOneSpiderAlive
call tauren.addUnit('Otch')
call weapon1h.addItem('desc')
call weapon2h.addItem('mlst')
call weapon2h.addExclusive(weapon1h)
call weapon2h.addException(tauren, 2)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 45, description)
endfunction
endscope
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ 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
module Alloc
private static integer instanceCount = 0
private thistype recycle
static method allocate takes nothing returns thistype
local thistype this
if (thistype(0).recycle == 0) then
debug if (instanceCount == 8190) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to allocate too many instances!")
debug return 0
debug endif
set instanceCount = instanceCount + 1
set this = instanceCount
else
set this = thistype(0).recycle
set thistype(0).recycle = thistype(0).recycle.recycle
endif
debug set this.recycle = -1
return this
endmethod
method deallocate takes nothing returns nothing
debug if (this.recycle != -1) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to deallocate an invalid instance at [" + I2S(this) + "]!")
debug return
debug endif
set this.recycle = thistype(0).recycle
set thistype(0).recycle = this
endmethod
endmodule
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.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
$ACCESS$ struct $NAME$Item extends array
// No default ctor and dctor due to limited array size
method operator data takes nothing returns $TYPE$
return Table(this).$TYPE$[-1] // hashtable[ node, -1 ] = data
endmethod
method operator data= takes $TYPE$ value returns nothing
set Table(this).$TYPE$[-1] = value
endmethod
method operator next takes nothing returns thistype
return Table(this)[-2] // hashtable[ node, -2 ] = next
endmethod
method operator next= takes thistype value returns nothing
set Table(this)[-2] = value
endmethod
method operator prev takes nothing returns thistype
return Table(this)[-3] // hashtable[ node, -3 ] = prev
endmethod
method operator prev= takes thistype value returns nothing
set Table(this)[-3] = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
readonly $NAME$Item first
readonly $NAME$Item last
private integer count
implement Alloc
private static method setNodeOwner takes $NAME$Item node, $NAME$ owner returns nothing
set Table(node)[-4] = owner
endmethod
private static method getNodeOwner takes $NAME$Item node returns thistype
return Table(node)[-4]
endmethod
private method createNode takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = Table.create()
set node.data = value
call setNodeOwner(node, this) // ownership
return node
endmethod
private method deleteNode takes $NAME$Item node returns nothing
call Table(node).destroy() // also removes ownership
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set count = 0
return this
endmethod
method clear takes nothing returns nothing
local $NAME$Item node = first
local $NAME$Item temp
loop // recycle all Table indexes
exitwhen 0 == node
set temp = node.next
call deleteNode(node)
set node = temp
endloop
set first = 0
set last = 0
set count = 0
endmethod
method destroy takes nothing returns nothing
call clear()
call deallocate()
endmethod
method front takes nothing returns $TYPE$
return first.data
endmethod
method back takes nothing returns $TYPE$
return last.data
endmethod
method empty takes nothing returns boolean
return count == 0
endmethod
method size takes nothing returns integer
return count
endmethod
method push takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set last.next = node
set node.prev = last
else
set first = node
set node.prev = 0
endif
set last = node
set node.next = 0
set count = count + 1
return this
endmethod
method unshift takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set first.prev = node
set node.next = first
else
set last = node
set node.next = 0
endif
set first = node
set node.prev = 0
set count = count + 1
return this
endmethod
method pop takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = last
set last = last.prev
if last == 0 then
set first = 0
else
set last.next = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call 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
/*****************************************************************************
*
* RegisterNativeEvent v1.1.1.3
* by Bannar
*
* Storage of trigger handles for native events.
*
******************************************************************************
*
* Optional requirements:
*
* Table by Bribe
* hiveworkshop.com/threads/snippet-new-table.188084/
*
******************************************************************************
*
* Important:
*
* Avoid using TriggerSleepAction within functions registered.
* Destroy native event trigger on your own responsibility.
*
******************************************************************************
*
* Core:
*
* function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
* Whether index whichIndex has already been attached to event whichEvent.
*
* function RegisterNativeEventTrigger takes integer whichIndex, integer eventId returns boolean
* Registers whichIndex within whichEvent scope and assigns new trigger handle for it.
*
* function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent specific to provided index whichIndex.
*
* function GetNativeEventTrigger takes integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent.
*
*
* Custom events:
*
* function CreateNativeEvent takes nothing returns integer
* Returns unique id for new event and registers it with RegisterNativeEvent.
*
* function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
* Registers new event handler func for event whichEvent specific to index whichIndex.
*
* function RegisterNativeEvent takes integer whichEvent, code func returns nothing
* Registers new event handler func for specified event whichEvent.
*
*****************************************************************************/
library RegisterNativeEvent uses optional Table
globals
private integer eventIndex = 500 // 0-499 reserved for Blizzard native events
endglobals
private module NativeEventInit
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set table = TableArray[0x2000]
endif
endmethod
endmodule
private struct NativeEvent extends array
static if LIBRARY_Table then
static TableArray table
else
static hashtable table = InitHashtable()
endif
implement NativeEventInit
endstruct
function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger.has(whichIndex)
else
return HaveSavedHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function RegisterNativeEventTrigger takes integer whichIndex, integer whichEvent returns boolean
if not IsNativeEventRegistered(whichIndex, whichEvent) then
static if LIBRARY_Table then
set NativeEvent.table[whichEvent].trigger[whichIndex] = CreateTrigger()
else
call SaveTriggerHandle(NativeEvent.table, whichEvent, whichIndex, CreateTrigger())
endif
return true
endif
return false
endfunction
function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger[whichIndex]
else
return LoadTriggerHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function GetNativeEventTrigger takes integer whichEvent returns trigger
return GetIndexNativeEventTrigger(bj_MAX_PLAYER_SLOTS, whichEvent)
endfunction
function CreateNativeEvent takes nothing returns integer
local integer eventId = eventIndex
call RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId)
set eventIndex = eventIndex + 1
return eventId
endfunction
function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
call RegisterNativeEventTrigger(whichIndex, whichEvent)
call TriggerAddCondition(GetIndexNativeEventTrigger(whichIndex, whichEvent), Condition(func))
endfunction
function RegisterNativeEvent takes integer whichEvent, code func returns nothing
call RegisterIndexNativeEvent(bj_MAX_PLAYER_SLOTS, whichEvent, func)
endfunction
endlibrary
/*****************************************************************************
*
* RegisterPlayerUnitEvent v1.0.3.2
* by Bannar
*
* Register version of TriggerRegisterPlayerUnitEvent.
*
* Special thanks to Magtheridon96, Bribe, azlier and BBQ for the original library version.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent.
*
* function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
* Registers generic playerunitevent whichEvent adding code func as callback.
*
* function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
* Registers playerunitevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerUnitEvent requires RegisterNativeEvent
function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), whichEvent, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerUnitEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent, null)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
* v1.2.1, 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 filter for units being indexed
/*static method onFilter takes unit u returns boolean
return true
endmethod*/
endmodule
/* _________________________________________________________________________
* 3. Function API
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
* Every function inlines except for UnitDexRemove
*
* 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.
* - All public functions are inlined except UnitDexRemove.
*
* -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
function UnitDexRemove takes unit u, boolean runEvents returns boolean
return UnitDex.Remove(u, runEvents)
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
/****************************************************************/
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 = 0
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
// Index preplaced units
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
// run init triggers
set 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
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
call TimerStart(CreateTimer(), 0, false, function thistype.onGameStart)
endmethod
endmodule
endlibrary
library SimError initializer init
//**************************************************************************************************
//*
//* SimError
//*
//* Mimic an interface error message
//* call SimError(ForPlayer, msg)
//* ForPlayer : The player to show the error
//* msg : The error
//*
//* To implement this function, copy this trigger and paste it in your map.
//* Unless of course you are actually reading the library from wc3c's scripts section, then just
//* paste the contents into some custom text trigger in your map.
//*
//**************************************************************************************************
//==================================================================================================
globals
private sound error
endglobals
//====================================================================================================
function SimError takes player ForPlayer, string msg returns nothing
set msg="\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00"+msg+"|r"
if (GetLocalPlayer() == ForPlayer) then
call ClearTextMessages()
call DisplayTimedTextToPlayer( ForPlayer, 0.52, 0.96, 2.00, msg )
call StartSound( error )
endif
endfunction
private function init takes nothing returns nothing
set error=CreateSoundFromLabel("InterfaceError",false,false,false,10,10)
//call StartSound( error ) //apparently the bug in which you play a sound for the first time
//and it doesn't work is not there anymore in patch 1.22
endfunction
endlibrary